From f4f2ba13c528600b113b55508f8148ed4ce8e2a9 Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 22 Feb 2024 13:14:42 -0500 Subject: [PATCH 01/58] move js searcher sdk --- express_relay/sdk/js/{ => searcher}/.eslintrc.js | 0 express_relay/sdk/js/{ => searcher}/.gitignore | 0 express_relay/sdk/js/{ => searcher}/README.md | 0 express_relay/sdk/js/{ => searcher}/package.json | 0 .../sdk/js/{ => searcher}/src/examples/SimpleSearcher.ts | 0 express_relay/sdk/js/{ => searcher}/src/index.ts | 0 express_relay/sdk/js/{ => searcher}/src/types.d.ts | 0 express_relay/sdk/js/{ => searcher}/tsconfig.json | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename express_relay/sdk/js/{ => searcher}/.eslintrc.js (100%) rename express_relay/sdk/js/{ => searcher}/.gitignore (100%) rename express_relay/sdk/js/{ => searcher}/README.md (100%) rename express_relay/sdk/js/{ => searcher}/package.json (100%) rename express_relay/sdk/js/{ => searcher}/src/examples/SimpleSearcher.ts (100%) rename express_relay/sdk/js/{ => searcher}/src/index.ts (100%) rename express_relay/sdk/js/{ => searcher}/src/types.d.ts (100%) rename express_relay/sdk/js/{ => searcher}/tsconfig.json (100%) diff --git a/express_relay/sdk/js/.eslintrc.js b/express_relay/sdk/js/searcher/.eslintrc.js similarity index 100% rename from express_relay/sdk/js/.eslintrc.js rename to express_relay/sdk/js/searcher/.eslintrc.js diff --git a/express_relay/sdk/js/.gitignore b/express_relay/sdk/js/searcher/.gitignore similarity index 100% rename from express_relay/sdk/js/.gitignore rename to express_relay/sdk/js/searcher/.gitignore diff --git a/express_relay/sdk/js/README.md b/express_relay/sdk/js/searcher/README.md similarity index 100% rename from express_relay/sdk/js/README.md rename to express_relay/sdk/js/searcher/README.md diff --git a/express_relay/sdk/js/package.json b/express_relay/sdk/js/searcher/package.json similarity index 100% rename from express_relay/sdk/js/package.json rename to express_relay/sdk/js/searcher/package.json diff --git a/express_relay/sdk/js/src/examples/SimpleSearcher.ts b/express_relay/sdk/js/searcher/src/examples/SimpleSearcher.ts similarity index 100% rename from express_relay/sdk/js/src/examples/SimpleSearcher.ts rename to express_relay/sdk/js/searcher/src/examples/SimpleSearcher.ts diff --git a/express_relay/sdk/js/src/index.ts b/express_relay/sdk/js/searcher/src/index.ts similarity index 100% rename from express_relay/sdk/js/src/index.ts rename to express_relay/sdk/js/searcher/src/index.ts diff --git a/express_relay/sdk/js/src/types.d.ts b/express_relay/sdk/js/searcher/src/types.d.ts similarity index 100% rename from express_relay/sdk/js/src/types.d.ts rename to express_relay/sdk/js/searcher/src/types.d.ts diff --git a/express_relay/sdk/js/tsconfig.json b/express_relay/sdk/js/searcher/tsconfig.json similarity index 100% rename from express_relay/sdk/js/tsconfig.json rename to express_relay/sdk/js/searcher/tsconfig.json From 474501d0012bcd7e64b0d4dd5738a9b485193389 Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 22 Feb 2024 13:14:55 -0500 Subject: [PATCH 02/58] add the python searcher sdk --- .../searcher_utils.cpython-311.pyc | Bin 0 -> 2577 bytes .../simple_searcher.cpython-311.pyc | Bin 0 -> 9513 bytes .../searcher/examples/simple_searcher.py | 189 ++++++++++++++++++ .../sdk/python/searcher/searcher_utils.py | 62 ++++++ .../types_liquidation_adapter.cpython-311.pyc | Bin 0 -> 2034 bytes .../python/utils/types_liquidation_adapter.py | 46 +++++ 6 files changed, 297 insertions(+) create mode 100644 express_relay/sdk/python/searcher/__pycache__/searcher_utils.cpython-311.pyc create mode 100644 express_relay/sdk/python/searcher/examples/__pycache__/simple_searcher.cpython-311.pyc create mode 100644 express_relay/sdk/python/searcher/examples/simple_searcher.py create mode 100644 express_relay/sdk/python/searcher/searcher_utils.py create mode 100644 express_relay/sdk/python/utils/__pycache__/types_liquidation_adapter.cpython-311.pyc create mode 100644 express_relay/sdk/python/utils/types_liquidation_adapter.py diff --git a/express_relay/sdk/python/searcher/__pycache__/searcher_utils.cpython-311.pyc b/express_relay/sdk/python/searcher/__pycache__/searcher_utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e6984498172993a249149dddb596fe43a25b134 GIT binary patch literal 2577 zcmb_e&uDyI5b2QtqM|N31SLu4+tvJqBc1|r2?&FgjSoKiM`2s*UXHQ zxN;>|LRAk`;($bshy$f+4;=ev1QZEtrAm={>TOZrf_mtCySC#1ReR`U{q~#LnKv`v z`{qr4%I7l#%EKR@u}djJ{=mwxlm=k$0f2ji5~?_)sniq&amb85GS2fQ>$qTiIL9ofA-}*cDz!A#ScYX;ZY6ej*hpylEp5(ZmTo0#1sc0TDieRAkBZ`_uXb9DUbHQF8EPtXEsrul`73taW%Lpg z!mMLMY~zeInis%uWqZe=a*r@lQz)s$t`Qbz396if))2)I(KyVR@Kc}Ibje-u{P?m> z{p`Bw*wkpd(snLalD=jbuGwUU;b#n^>Cv`>IA<6)+om)461QFH7Y)O7T~C_Q_FQ2Y zKa)L}gS@(4y(SnJRnxVr7nxX-UaN}RskS<@anj)abls>;Tp^knlu102c|dn*H7nqi@n9~7W)(B?F2qgLJNRT3`hDo z&RS;2klq?|gnW2`T$Z16?3;#VIu13Z=_i4vHuE*0%&?KXj9?aL(pY01 zUj@+St_6A=l`J5P&fYGVdqgVm8k9Qd@`2JNU8NiA#=D7bl8&hfYVmz-)l&~+pg{OV z<=d1TVM10%`2J7{!;aybChAHzRaai~p-JR%P*#&8qz=*j@tq7CNmuKtzb0SvmF}iN zRjTx?gCLcbSyU7QUO&N3duv-$-XCZz7I=(62%n9%3L=<0)5p3u#v2MVv?FbFOK zvAgwfx<~~$dFXLrY=UuF{9jXm&vmvx$zXkF^{cS+K_%P;R9oDWp5-~Z<++lZmJBI; zjiP;5<7$ok4Ym8Wae9-J7H3gE%qxZz$n_G+W$WHzm{IbVm#PGCbj90=%Ff$j!R9yvi(j z;$QrZ&O_w2l6o~YKNM*jvg+v%KdM~6!H>Y14`BD558q9zk??V1A z7`PjCPa4jnAZT;q7${Wu80Pnnj52gKKKfs3YS zR5E-DeV5VTO86l(@1U7MgDJ{asa=N<`C3!djWAE9M>`a_Hax0P;gdMvD2O@A4#udX zvCllOK&Jxy#P?u&8XPHf`I9L_Hv$-%C|euit|s4^N;2p9e(`P4lXum-g=eL? zr=__^+Hb4drMV}irJd4JZ)rD+S4ensREoYTJ1r0n$3)9@Fn$WCGt6Z>qIx#0MLaGb z7tuZ;_X&VRor^BxkNGL6 z;%y@S0u#g)Md_0x+XtUrlI?|meKNa!@adBewhumiGP8Z~3DlS}9gsK85B76xeX;*3 zb4r=+PafKeZ(X_b&CcX|K^z)(%QIWYw#3%#PWgC{goZ#Rg^3`ANF!4-K^jqpWk#HqG!kDs zGqf#+I&k-lM7L0II>UjDyFHwM9GqR)AO6V40?7gQm0W%j(jZ`B009Pq1L8jlUL%YB zN>vYsyQ{0JtE;N@&puxZf%2dK@RX7sLjDIIjN+Q2X|}2o^;GOEIN*J#tH9i30KlR<7V%;L`%{$<4JmFyzJSY z@FiPkS}nv%sCChA3{2Y|z=#jw(#uS{=$HwJPTC>5XshU^9?_C>5;EryTW)hRK`XgP zM9*y^da2`q4O+rgYZ+oKe6&++g;s7FnsbSMXcHFOZj+fV+9tLySrJ$61{y4jyJ`)Q zqOJ$<@X&Zc%rpW0mdosl1fC<#RK%jWuPkS%cutC|@ZcPa$J5!A8nJ55GmtzzL3Qz5X zwD3^I6oulCzqvrw1uF9bZ_ozR@Gz1=4le-`JS;R2Qu8NxX^vOY2YHW7?l9^$C8ppXQDYV{bDxIRkjGQYAFv?su6<2X|k*ZWqN~tFD zu>X1K1~ro=QXv7a`;<|Hb@2pM6S5hK$$l`<-?rk7|6=@g)jxKxniIB}q8 z0R~djyebPRMTo1slwwT{)=2P1;WKi2PEv*01VvnWi^j7mRH3UT#u>ArGUWxhl0s^k zzbP!U{wXjfE3ihAa*&_R>XanYips~+$t27iP;o360BDOZHOR%LluysG0fi(g+xBTB z5-@F;>F(tzYRr6RI`qKI~Mhdh%+eFozxQMw_CSs_tp6!IB{SP-Cp=t7eD z^vx8X1no&y1*Ro4$W2rU9MfQ+lmI2islWmeVR=ZYd9(Hn^JaC33n_k< z0{t223WN(|NvU`uE8;uQloU{zk)^mU#SE3_(lSmT7+GUEC{=ALT8~MQ-2)ppZBMqu^w)yqE-Vk5Gcn%#8Iap*G5i zmXq+7)vWcJtzP5mHG940z?$fU&D5o}PB(T*&>pR|iR>6YgDFZ%ih{gdqRW~?w+^xo zy3kq-4NuFOyY3)NMeLd@7E1|98jEQyu~;%KW`SWb%^Qn-4Cc7@#Ri^*=8wgM6etW> z8gMmYu`h{?jFQ_B1P$;ORFr7|g{HqsFkIyDSK`8^y4PQrC`^1ZQE3krIsBElV5P02 zFkP7bWV+%H6s8JOpTJd^(#OJKG@r6m7avT=1!@?Rzz2#O+y^9r&L=*e{blDqY zZh+6UvxpAS`2c!-Snt_9;)+8>IoC?wmgk^XSEJXwL#;c(1Ucbud^7iyx58||1%3#Z zURIod-_pQ`Cr{%^#T8R4d8_Dcd@=XC4bS&Fyu4klj|`ML7twsMV(xE)g%`#*<8GFz z*!sC+#g%u#`tb{QKwNC=IV)3eixghpTXA=Jtv9M~0Onom&$iFtCT3D?+|CZ&ih{YD zlWu{>ut0C|uo7|iH*I6MH#F8aY#QL6+(6F{%s^v*G?vLIp8~&4Q3M&h5m+{IlC5aA z%{KRh+dZ^lEZeaKj-`0BIjU`BHMgPWL!qyN-pKZD!O-8JGV3{BUqm266nqC9i&;VDtUt4^@t4V$R?P*zA9yiwRSrYPUs^Pqlv3fn zb@&k5ofKSEJkLzow=E|VlA?l#oH>AZ!{2sJwl2wPST=t^b#M@5eU|e4Z?IA1rqtEV`mqn}NfynXS{jAY)-UrbiNg zem2R2K(o9L!2z5?6H8VFC^&R|D%UUVWaYT%8$Xhzg|(OQeEYTN*U3W)>+wt{9o zbW0=h5DcW*S(qV*pe}bJ@FGC77;!YcMHOIg=u4*Ug=_~dPm%B$Q-MjV5W+1e_a6fxD zT?*_g2lj1-yYC;{c)#59b}4+M96nO%Y0J8ankPbgC43w;X!6 z>T`xc4*?XWs{!KeDRSLR$Kt@MdO>#o7_JwOLCh1x0>-G>>@;?M^M11e25SQs&W<8l zM5|~MxkvT~plJ_L+gP!@zJ;UNa}b@P>yi79Zrif0t>-(m^{PgC4k#3TV(TOSA3bi{ zwsG5EIqoYxoNb)6sJMD-WuwU>Fy4W0U|FnFDsr`Cn^#4rv{n}DK0?Z4dhanX%v*+q zsJQ*q%3_^@ZM0cUSarWk42T_%f@Uvm#g?~jv>H7&-}AN|W7o$cWTVMjYqZK+x3>Wv zp#ROv3a9Qh>foK$`H<(#trqKZ1n;zYO!TgI6fu;yo96^vLX%c2i)dM^O(oUNusq8<cz%$-f14k%ongb&U56$|5|b9-C|F}g<5HW z=ibJ%xO>Tlead@H92jhj)2NAk`E9MuDRT>1JFZSN@Ii9>jHkTYOy7zp@A>3MDh86Z zl6ON)<1zQR;syOeZ)HdMll7}V-|`D0^1rm1>B)P=J&y*=qc=-##b@Hrt5?$Ayhj{t z@D{k`TZ|Z_Z?~~)|NR>M88XYYnFYj}BeNb0F}5>0DCH)sJ>C zmen2CQGQ6t?H;*tc%*55BPJTbi$;M!?g;vF=;^rUxHsnpE}DC zoWK=KIBC`!hjWqXMuG}PhupowXH!a+y@l|#hKq#ty^*kyoP7it_8gZH=qtCVptd68 z(pfck`m)Yf=n}9D`NM!1$~TByhJg+mk^^5p16eDIY+-T1b8w1Dg9= z7p67)x$|ePUetV-Rq)!Am*8k20qxuuSojU63Yr(XXgsyR;0BVXwl*;^VR4H5HqarX zZ?AC}!E5#fRn6SeJY&joDtJXJLZXmUcD)$IBdo$$B_3aoWDDFviQQMqIA zO+Jquqlwns=i&S&!_0)8$}+?xP62zDU^FE`X-2b)bT&J$*%B0Fb5oGfAh7hQ9(Q5* zp>Y{FZ35E(2PctMrenxij`7X|42mrnjL`ECzzCT~8d#*{i0e}c3Ho`bQV)^vTuif2 z&EGr^5WVIA01C|lFU_jn(m0g7f*x2cc&gp>7>Y~60+5&CYYZ-B6ZX3Qg}(yX^o7d4 z!IE!Z*|)E7p~AI1<-$+6@O|$7(&PQVJ=gZvrB6q(2vEmAE`r0b(p^EQx)lKYO zTf}aMNCrTa0WYz8^nNh*si98)K~z7YwgoD_Gk*|u!07dFUW4ilJoWTG@${BFePvHy z;oR@JmcO6=bb905uO}amJsCV&96b7u7w=B4PyXgF|LvU;H(ut(i`@7ognWVhr9h+{ zh!iF^+rs6xJr&p4O50w@i?>632;gf3g>lG@+uNRUy-&E_``1g{{xY|}$nD=m28Nz+ zq4lK_H&EsVAbb8S*i{Y=7T(`%@7fVh0#y^AP88b?eDPL^J6h(B7P+IFzChX6SMu>? zA78jowH~tfSFK)m=vim)y~R6=o5Ak8lTU*Oo&*n6x_Zl9M~X+UY}u@#P6%8>omB_f zz5fgDk+0bEePB&^56C7Q0NI2CAe;8rga7jqf5&IyPv+LLvZ}8z@yLbO-mBEyV5r zsrNH)F?9SlM~bJfluleNpSW6bT`Rk;6wQS}VcRm0))zxOZc`930+qa_ul}096KBNc*nM@X^QO zzbyRLUkZOv4u7zE>6t%V^zYpa9l=%~KE4EO?1V-D)~z7lzQJGp@aI3=!S71fg|9k` zT^ID(tRUhe#Y!PQ4bOiun@UGW`b?#*wX%FA?-0@U`S1ZBZ`&aKy zvw3vg9p7?;#6Wg6v3>KhmH0c?lK00-t$pRzzM|_Dzb(N63ZtO+_3wRSyDlCh|8cBk zoY;P6{obV$w%?twLmkgT&>+NOk#2bkFy(m!R{>~V-I5M7Ln;gS$^jk^+B|fS<&W@< z2acy>HDje`l){GFG;CC2S@=f_r-p%pJvAnXLIw>Y2EUpc%=skS zY-9|Q^uFbS_8!mw-<8eeACG@G1F$r!Nc2PhaUe(O4^`xvL>PvH?aLgizC zRhz|PsStmG{Z&Y7f&DdAI*O$BtB~HJ`S*-?3+%5#`ith@Gcr+p)vrR%6wSY9?(Wra z*$oMul6$c19xOPXwe}Z0RY%CO7tV;PHn=}?_gC$3hb8Ust*W1VRCVGLF@r-6OGlOL z1nbwg@a@-)Pl1S~rviQ2@P6osem{MI-JjVzs&;r^C1&u~@Lx)n{wf^r)nU#`IPYri piEUTWwyVPV3m0!staIz!&m4ChaO_oP=U@6MSk*?jZopz9`G41?sF?r& literal 0 HcmV?d00001 diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py new file mode 100644 index 0000000000..1a682607da --- /dev/null +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -0,0 +1,189 @@ +import argparse +import asyncio +import logging +import urllib.parse +from typing import TypedDict + +import httpx +from eth_account import Account + +from searcher.searcher_utils import BidInfo, construct_signature_liquidator +from utils.types_liquidation_adapter import LiquidationOpportunity + +logger = logging.getLogger(__name__) + +VALID_UNTIL = 1_000_000_000_000 + + +def assess_liquidation_opportunity( + default_bid: int, + opp: LiquidationOpportunity, +) -> BidInfo | None: + """ + Assesses whether a liquidation opportunity is worth liquidating; if so, returns the bid and valid_until timestamp. Otherwise returns None. + This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the liquidation. + There are many ways to evaluate this, but the most common way is to check that the value of the amount the searcher will receive from the liquidation exceeds the value of the amount repaid. + Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation. + If the opporutnity is deemed worthwhile, this function can return a bid amount representing the amount of native token to bid on this opportunity, and a timestamp representing the time at which the transaction will expire. + Otherwise, this function can return None. + Args: + default_bid: The default amount of bid for liquidation opportunities. + opp: A LiquidationOpportunity object, representing a single liquidation opportunity. + Returns: + If the opportunity is deemed worthwhile, this function can return a BidInfo object, representing the user's bid and the timestamp at which the user's bid should expire. If the LiquidationOpportunity is not deemed worthwhile, this function can return None. + """ + user_liquidation_params = { + "bid": default_bid, + "valid_until": VALID_UNTIL, + } + return user_liquidation_params + + +class OpportunityBid(TypedDict): + opportunity_id: str + permission_key: str + amount: str + valid_until: str + liquidator: str + signature: str + + +def create_liquidation_transaction( + opp: LiquidationOpportunity, sk_liquidator: str, bid_info: BidInfo +) -> OpportunityBid: + """ + Creates a bid for a liquidation opportunity. + Args: + opp: A LiquidationOpportunity object, representing a single liquidation opportunity. + sk_liquidator: A 0x-prefixed hex string representing the liquidator's private key. + bid_info: necessary information for the liquidation bid + Returns: + An OpportunityBid object which can be sent to the liquidation server + """ + repay_tokens = [ + (opp["contract"], int(opp["amount"])) for opp in opp["repay_tokens"] + ] + receipt_tokens = [ + (opp["contract"], int(opp["amount"])) for opp in opp["receipt_tokens"] + ] + + liquidator = Account.from_key(sk_liquidator).address + liq_calldata = bytes.fromhex(opp["calldata"].replace("0x", "")) + + signature_liquidator = construct_signature_liquidator( + repay_tokens, + receipt_tokens, + opp["contract"], + liq_calldata, + int(opp["value"]), + bid_info, + sk_liquidator, + ) + + opportunity_bid = { + "opportunity_id": opp["opportunity_id"], + "permission_key": opp["permission_key"], + "amount": str(bid_info["bid"]), + "valid_until": str(bid_info["valid_until"]), + "liquidator": liquidator, + "signature": bytes(signature_liquidator.signature).hex(), + } + + return opportunity_bid + + +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-v", "--verbose", action="count", default=0) + parser.add_argument( + "--private-key", + type=str, + required=True, + help="Private key of the searcher for signing calldata", + ) + parser.add_argument( + "--chain-id", + type=str, + required=True, + help="Chain ID of the network to monitor for liquidation opportunities", + ) + parser.add_argument( + "--bid", + type=int, + default=10, + help="Default amount of bid for liquidation opportunities", + ) + parser.add_argument( + "--liquidation-server-url", + type=str, + required=True, + help="Liquidation server endpoint to use for fetching opportunities and submitting bids", + ) + args = parser.parse_args() + + logger.setLevel(logging.INFO if args.verbose == 0 else logging.DEBUG) + log_handler = logging.StreamHandler() + formatter = logging.Formatter( + "%(asctime)s %(levelname)s:%(name)s:%(module)s %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + log_handler.setFormatter(formatter) + logger.addHandler(log_handler) + + sk_liquidator = args.private_key + liquidator = Account.from_key(sk_liquidator).address + logger.info("Liquidator address: %s", liquidator) + client = httpx.AsyncClient() + while True: + try: + accounts_liquidatable = ( + await client.get( + urllib.parse.urljoin( + args.liquidation_server_url, "/v1/liquidation/opportunities" + ), + params={"chain_id": args.chain_id}, + ) + ).json() + except Exception as e: + logger.error(e) + await asyncio.sleep(5) + continue + + logger.debug("Found %d liquidation opportunities", len(accounts_liquidatable)) + + for liquidation_opp in accounts_liquidatable: + opp_id = liquidation_opp["opportunity_id"] + if liquidation_opp["version"] != "v1": + logger.warning( + "Opportunity %s has unsupported version %s", + opp_id, + liquidation_opp["version"], + ) + continue + bid_info = assess_liquidation_opportunity(args.bid, liquidation_opp) + + if bid_info is not None: + tx = create_liquidation_transaction( + liquidation_opp, sk_liquidator, bid_info + ) + + resp = await client.post( + urllib.parse.urljoin( + args.liquidation_server_url, + f"/v1/liquidation/opportunities/{opp_id}/bids", + ), + json=tx, + timeout=20, + ) + logger.info( + "Submitted bid amount %s for opportunity %s, server response: %s", + bid_info["bid"], + opp_id, + resp.text, + ) + + await asyncio.sleep(1) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/express_relay/sdk/python/searcher/searcher_utils.py b/express_relay/sdk/python/searcher/searcher_utils.py new file mode 100644 index 0000000000..0c8550e4cf --- /dev/null +++ b/express_relay/sdk/python/searcher/searcher_utils.py @@ -0,0 +1,62 @@ +from typing import TypedDict + +import web3 +from eth_abi import encode +from eth_account.datastructures import SignedMessage +from web3.auto import w3 + + +class BidInfo(TypedDict): + bid: int + valid_until: int + + +def construct_signature_liquidator( + repay_tokens: list[(str, int)], + receipt_tokens: list[(str, int)], + address: str, + liq_calldata: bytes, + value: int, + bid_info: BidInfo, + secret_key: str, +) -> SignedMessage: + """ + Constructs a signature for a liquidator's bid to submit to the liquidation server. + + Args: + repay_tokens: A list of tuples (token address, amount) representing the tokens to repay. + receipt_tokens: A list of tuples (token address, amount) representing the tokens to receive. + address: The address of the protocol contract for the liquidation. + liq_calldata: The calldata for the liquidation method call. + value: The value for the liquidation method call. + bid: The amount of native token to bid on this opportunity. + valid_until: The timestamp at which the transaction will expire. + secret_key: A 0x-prefixed hex string representing the liquidator's private key. + Returns: + A SignedMessage object, representing the liquidator's signature. + """ + + digest = encode( + [ + "(address,uint256)[]", + "(address,uint256)[]", + "address", + "bytes", + "uint256", + "uint256", + "uint256", + ], + [ + repay_tokens, + receipt_tokens, + address, + liq_calldata, + value, + bid_info["bid"], + bid_info["valid_until"], + ], + ) + msg_data = web3.Web3.solidity_keccak(["bytes"], [digest]) + signature = w3.eth.account.signHash(msg_data, private_key=secret_key) + + return signature diff --git a/express_relay/sdk/python/utils/__pycache__/types_liquidation_adapter.cpython-311.pyc b/express_relay/sdk/python/utils/__pycache__/types_liquidation_adapter.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6782feda5a52a590df26842837272f09b269b3a5 GIT binary patch literal 2034 zcmaKt&u`;I6vxN$*!j^kZ9{0g3x!f4p{&%+f$IthSc%dfsnB*0^kR8E!>(1w_Rct~ zhLeLha6#%}rR}jNV8vg;Es<6t@~ILhZmHVSiUaSBo8N)S%=4RhwrBi4^PT*sH7j^~(`l?+20GC&U5vF;hp&e@)$&jxI%lt@~&b423LVkyqmg9_yQHU8c zFh-GJDRK;=-%g@9NR(M3J3xDGjRV~RMwL;Dwx=!TW&Z;hhHC%rT-B*N@V~vsH z%wxiJPMP)qMwORAkLmD6n&ggx6YhT#wUV3@_>r)kC^79u(DbAFYa_{7mTxz>WhDj6 zY6Pt5pik*Ywt@J(gU^n(e!_hg_aSL0EGF z#%p0KdbaI|KuYJC?fNx-8VW9@CAep|YLcC(I|9Gfj9gFFBKTrydG7P3%hVi}&FnDZ z;(pi?2uc|Wj@$whkNtY3mG~e&Xdm>7OYvU3*M=7FiKW3q^K+Oo|Nk7SZ0f_j(xGPz zJyUrGH}Al7X#p15xudz^Q+f1K=E>^*=%WwAFc4AGcY#+a7c=7q-{rNh#!_;Yta7tP|c!ih_rB%ZiY{OiF?~+zq2aKl=ix!L9XbR^;J*0%al= z&=k=uf=Q_70(+JH5)P`AE~8f-7<^VX!Hg5RtgLk?)-GMU9MGBLER^Eic(=VfaesOB z?DXaR4&8*>rJI+P_4DbQ1u2Hrxca;4!LTvpk5fklYqzr3X<)?tas8mNOi z5oaKe2~357A_9?OrwaK3Aa|JsB6ht*e*(LsBHgFH9W@0XiYO5EYHk|tdoZ0Moainb z2MV+Cq!n=~rVwx7ooGIQCQrePBkq+~&-L?1XWzdmBbby;Z{wqjPk+-d?!DRA?og~< zy4_oTCq9f1+lM2a7H)OgTd8y?)-J8|^2K;3-f8dj?ta*zSi8!ClTyZ|%%}VXM5ZZ^ zsIrtI9+CekWo81WY_c)S=}6eVghLagBJki5@1m)oK^clQFm;hbcPf9M;24@JG>L1R z$lvgD2l^PLXE-t@qf}36Ch(>8s+pK@XuAGWf&UAsN|h`XI5qp^ir(fPq$m7m0+*yg zlYfBe>zbzZ$orki+BY-Wa-YnapITQqdNT?Y%Gyew%$oCWuW?B Z(VJ0d)zH@aWY%1KeTAbpb3!UR{{qYS>RSK+ literal 0 HcmV?d00001 diff --git a/express_relay/sdk/python/utils/types_liquidation_adapter.py b/express_relay/sdk/python/utils/types_liquidation_adapter.py new file mode 100644 index 0000000000..28b868a29e --- /dev/null +++ b/express_relay/sdk/python/utils/types_liquidation_adapter.py @@ -0,0 +1,46 @@ +from typing import TypedDict + + +class TokenQty(TypedDict): + contract: str + amount: str + + +class LiquidationOpportunity(TypedDict): + # The unique id of the opportunity + opportunity_id: str + # The id of the chain where the opportunity was found + chain_id: str + # Address of the contract where the liquidation method is called + contract: str + # The calldata that needs to be passed in with the liquidation method call + calldata: str + # The value that needs to be passed in with the liquidation method call + value: str + # The permission key necessary to call the liquidation method + permission_key: str + # A list of tokens that can be used to repay this account's debt. Each entry in the list is a tuple (token address, hex string of repay amount) + repay_tokens: list[TokenQty] + # A list of tokens that ought to be received by the liquidator in exchange for the repay tokens. Each entry in the list is a tuple (token address, hex string of receipt amount) + receipt_tokens: list[TokenQty] + # Opportunity format version, used to determine how to interpret the opportunity data + version: str + + +class LiquidationAdapterCalldata(TypedDict): + repay_tokens: list[(str, int)] + expected_receipt_tokens: list[(str, int)] + liquidator: str + contract: str + data: bytes + valid_until: int + bid: int + signature_liquidator: bytes + + +class LiquidationAdapterTransaction(TypedDict): + bid: str + calldata: str + chain_id: str + contract: str + permission_key: str From 574b8ff7d17668afac0afb9ebc12059d59e0ca86 Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 22 Feb 2024 13:28:36 -0500 Subject: [PATCH 03/58] remove pycache --- .gitignore | 1 + .../__pycache__/searcher_utils.cpython-311.pyc | Bin 2577 -> 0 bytes .../__pycache__/simple_searcher.cpython-311.pyc | Bin 9513 -> 0 bytes .../types_liquidation_adapter.cpython-311.pyc | Bin 2034 -> 0 bytes 4 files changed, 1 insertion(+) delete mode 100644 express_relay/sdk/python/searcher/__pycache__/searcher_utils.cpython-311.pyc delete mode 100644 express_relay/sdk/python/searcher/examples/__pycache__/simple_searcher.cpython-311.pyc delete mode 100644 express_relay/sdk/python/utils/__pycache__/types_liquidation_adapter.cpython-311.pyc diff --git a/.gitignore b/.gitignore index add7a01ca6..01abb5d227 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ tsconfig.tsbuildinfo *mnemonic* .envrc */*.sui.log* +__pycache__ diff --git a/express_relay/sdk/python/searcher/__pycache__/searcher_utils.cpython-311.pyc b/express_relay/sdk/python/searcher/__pycache__/searcher_utils.cpython-311.pyc deleted file mode 100644 index 4e6984498172993a249149dddb596fe43a25b134..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2577 zcmb_e&uDyI5b2QtqM|N31SLu4+tvJqBc1|r2?&FgjSoKiM`2s*UXHQ zxN;>|LRAk`;($bshy$f+4;=ev1QZEtrAm={>TOZrf_mtCySC#1ReR`U{q~#LnKv`v z`{qr4%I7l#%EKR@u}djJ{=mwxlm=k$0f2ji5~?_)sniq&amb85GS2fQ>$qTiIL9ofA-}*cDz!A#ScYX;ZY6ej*hpylEp5(ZmTo0#1sc0TDieRAkBZ`_uXb9DUbHQF8EPtXEsrul`73taW%Lpg z!mMLMY~zeInis%uWqZe=a*r@lQz)s$t`Qbz396if))2)I(KyVR@Kc}Ibje-u{P?m> z{p`Bw*wkpd(snLalD=jbuGwUU;b#n^>Cv`>IA<6)+om)461QFH7Y)O7T~C_Q_FQ2Y zKa)L}gS@(4y(SnJRnxVr7nxX-UaN}RskS<@anj)abls>;Tp^knlu102c|dn*H7nqi@n9~7W)(B?F2qgLJNRT3`hDo z&RS;2klq?|gnW2`T$Z16?3;#VIu13Z=_i4vHuE*0%&?KXj9?aL(pY01 zUj@+St_6A=l`J5P&fYGVdqgVm8k9Qd@`2JNU8NiA#=D7bl8&hfYVmz-)l&~+pg{OV z<=d1TVM10%`2J7{!;aybChAHzRaai~p-JR%P*#&8qz=*j@tq7CNmuKtzb0SvmF}iN zRjTx?gCLcbSyU7QUO&N3duv-$-XCZz7I=(62%n9%3L=<0)5p3u#v2MVv?FbFOK zvAgwfx<~~$dFXLrY=UuF{9jXm&vmvx$zXkF^{cS+K_%P;R9oDWp5-~Z<++lZmJBI; zjiP;5<7$ok4Ym8Wae9-J7H3gE%qxZz$n_G+W$WHzm{IbVm#PGCbj90=%Ff$j!R9yvi(j z;$QrZ&O_w2l6o~YKNM*jvg+v%KdM~6!H>Y14`BD558q9zk??V1A z7`PjCPa4jnAZT;q7${Wu80Pnnj52gKKKfs3YS zR5E-DeV5VTO86l(@1U7MgDJ{asa=N<`C3!djWAE9M>`a_Hax0P;gdMvD2O@A4#udX zvCllOK&Jxy#P?u&8XPHf`I9L_Hv$-%C|euit|s4^N;2p9e(`P4lXum-g=eL? zr=__^+Hb4drMV}irJd4JZ)rD+S4ensREoYTJ1r0n$3)9@Fn$WCGt6Z>qIx#0MLaGb z7tuZ;_X&VRor^BxkNGL6 z;%y@S0u#g)Md_0x+XtUrlI?|meKNa!@adBewhumiGP8Z~3DlS}9gsK85B76xeX;*3 zb4r=+PafKeZ(X_b&CcX|K^z)(%QIWYw#3%#PWgC{goZ#Rg^3`ANF!4-K^jqpWk#HqG!kDs zGqf#+I&k-lM7L0II>UjDyFHwM9GqR)AO6V40?7gQm0W%j(jZ`B009Pq1L8jlUL%YB zN>vYsyQ{0JtE;N@&puxZf%2dK@RX7sLjDIIjN+Q2X|}2o^;GOEIN*J#tH9i30KlR<7V%;L`%{$<4JmFyzJSY z@FiPkS}nv%sCChA3{2Y|z=#jw(#uS{=$HwJPTC>5XshU^9?_C>5;EryTW)hRK`XgP zM9*y^da2`q4O+rgYZ+oKe6&++g;s7FnsbSMXcHFOZj+fV+9tLySrJ$61{y4jyJ`)Q zqOJ$<@X&Zc%rpW0mdosl1fC<#RK%jWuPkS%cutC|@ZcPa$J5!A8nJ55GmtzzL3Qz5X zwD3^I6oulCzqvrw1uF9bZ_ozR@Gz1=4le-`JS;R2Qu8NxX^vOY2YHW7?l9^$C8ppXQDYV{bDxIRkjGQYAFv?su6<2X|k*ZWqN~tFD zu>X1K1~ro=QXv7a`;<|Hb@2pM6S5hK$$l`<-?rk7|6=@g)jxKxniIB}q8 z0R~djyebPRMTo1slwwT{)=2P1;WKi2PEv*01VvnWi^j7mRH3UT#u>ArGUWxhl0s^k zzbP!U{wXjfE3ihAa*&_R>XanYips~+$t27iP;o360BDOZHOR%LluysG0fi(g+xBTB z5-@F;>F(tzYRr6RI`qKI~Mhdh%+eFozxQMw_CSs_tp6!IB{SP-Cp=t7eD z^vx8X1no&y1*Ro4$W2rU9MfQ+lmI2islWmeVR=ZYd9(Hn^JaC33n_k< z0{t223WN(|NvU`uE8;uQloU{zk)^mU#SE3_(lSmT7+GUEC{=ALT8~MQ-2)ppZBMqu^w)yqE-Vk5Gcn%#8Iap*G5i zmXq+7)vWcJtzP5mHG940z?$fU&D5o}PB(T*&>pR|iR>6YgDFZ%ih{gdqRW~?w+^xo zy3kq-4NuFOyY3)NMeLd@7E1|98jEQyu~;%KW`SWb%^Qn-4Cc7@#Ri^*=8wgM6etW> z8gMmYu`h{?jFQ_B1P$;ORFr7|g{HqsFkIyDSK`8^y4PQrC`^1ZQE3krIsBElV5P02 zFkP7bWV+%H6s8JOpTJd^(#OJKG@r6m7avT=1!@?Rzz2#O+y^9r&L=*e{blDqY zZh+6UvxpAS`2c!-Snt_9;)+8>IoC?wmgk^XSEJXwL#;c(1Ucbud^7iyx58||1%3#Z zURIod-_pQ`Cr{%^#T8R4d8_Dcd@=XC4bS&Fyu4klj|`ML7twsMV(xE)g%`#*<8GFz z*!sC+#g%u#`tb{QKwNC=IV)3eixghpTXA=Jtv9M~0Onom&$iFtCT3D?+|CZ&ih{YD zlWu{>ut0C|uo7|iH*I6MH#F8aY#QL6+(6F{%s^v*G?vLIp8~&4Q3M&h5m+{IlC5aA z%{KRh+dZ^lEZeaKj-`0BIjU`BHMgPWL!qyN-pKZD!O-8JGV3{BUqm266nqC9i&;VDtUt4^@t4V$R?P*zA9yiwRSrYPUs^Pqlv3fn zb@&k5ofKSEJkLzow=E|VlA?l#oH>AZ!{2sJwl2wPST=t^b#M@5eU|e4Z?IA1rqtEV`mqn}NfynXS{jAY)-UrbiNg zem2R2K(o9L!2z5?6H8VFC^&R|D%UUVWaYT%8$Xhzg|(OQeEYTN*U3W)>+wt{9o zbW0=h5DcW*S(qV*pe}bJ@FGC77;!YcMHOIg=u4*Ug=_~dPm%B$Q-MjV5W+1e_a6fxD zT?*_g2lj1-yYC;{c)#59b}4+M96nO%Y0J8ankPbgC43w;X!6 z>T`xc4*?XWs{!KeDRSLR$Kt@MdO>#o7_JwOLCh1x0>-G>>@;?M^M11e25SQs&W<8l zM5|~MxkvT~plJ_L+gP!@zJ;UNa}b@P>yi79Zrif0t>-(m^{PgC4k#3TV(TOSA3bi{ zwsG5EIqoYxoNb)6sJMD-WuwU>Fy4W0U|FnFDsr`Cn^#4rv{n}DK0?Z4dhanX%v*+q zsJQ*q%3_^@ZM0cUSarWk42T_%f@Uvm#g?~jv>H7&-}AN|W7o$cWTVMjYqZK+x3>Wv zp#ROv3a9Qh>foK$`H<(#trqKZ1n;zYO!TgI6fu;yo96^vLX%c2i)dM^O(oUNusq8<cz%$-f14k%ongb&U56$|5|b9-C|F}g<5HW z=ibJ%xO>Tlead@H92jhj)2NAk`E9MuDRT>1JFZSN@Ii9>jHkTYOy7zp@A>3MDh86Z zl6ON)<1zQR;syOeZ)HdMll7}V-|`D0^1rm1>B)P=J&y*=qc=-##b@Hrt5?$Ayhj{t z@D{k`TZ|Z_Z?~~)|NR>M88XYYnFYj}BeNb0F}5>0DCH)sJ>C zmen2CQGQ6t?H;*tc%*55BPJTbi$;M!?g;vF=;^rUxHsnpE}DC zoWK=KIBC`!hjWqXMuG}PhupowXH!a+y@l|#hKq#ty^*kyoP7it_8gZH=qtCVptd68 z(pfck`m)Yf=n}9D`NM!1$~TByhJg+mk^^5p16eDIY+-T1b8w1Dg9= z7p67)x$|ePUetV-Rq)!Am*8k20qxuuSojU63Yr(XXgsyR;0BVXwl*;^VR4H5HqarX zZ?AC}!E5#fRn6SeJY&joDtJXJLZXmUcD)$IBdo$$B_3aoWDDFviQQMqIA zO+Jquqlwns=i&S&!_0)8$}+?xP62zDU^FE`X-2b)bT&J$*%B0Fb5oGfAh7hQ9(Q5* zp>Y{FZ35E(2PctMrenxij`7X|42mrnjL`ECzzCT~8d#*{i0e}c3Ho`bQV)^vTuif2 z&EGr^5WVIA01C|lFU_jn(m0g7f*x2cc&gp>7>Y~60+5&CYYZ-B6ZX3Qg}(yX^o7d4 z!IE!Z*|)E7p~AI1<-$+6@O|$7(&PQVJ=gZvrB6q(2vEmAE`r0b(p^EQx)lKYO zTf}aMNCrTa0WYz8^nNh*si98)K~z7YwgoD_Gk*|u!07dFUW4ilJoWTG@${BFePvHy z;oR@JmcO6=bb905uO}amJsCV&96b7u7w=B4PyXgF|LvU;H(ut(i`@7ognWVhr9h+{ zh!iF^+rs6xJr&p4O50w@i?>632;gf3g>lG@+uNRUy-&E_``1g{{xY|}$nD=m28Nz+ zq4lK_H&EsVAbb8S*i{Y=7T(`%@7fVh0#y^AP88b?eDPL^J6h(B7P+IFzChX6SMu>? zA78jowH~tfSFK)m=vim)y~R6=o5Ak8lTU*Oo&*n6x_Zl9M~X+UY}u@#P6%8>omB_f zz5fgDk+0bEePB&^56C7Q0NI2CAe;8rga7jqf5&IyPv+LLvZ}8z@yLbO-mBEyV5r zsrNH)F?9SlM~bJfluleNpSW6bT`Rk;6wQS}VcRm0))zxOZc`930+qa_ul}096KBNc*nM@X^QO zzbyRLUkZOv4u7zE>6t%V^zYpa9l=%~KE4EO?1V-D)~z7lzQJGp@aI3=!S71fg|9k` zT^ID(tRUhe#Y!PQ4bOiun@UGW`b?#*wX%FA?-0@U`S1ZBZ`&aKy zvw3vg9p7?;#6Wg6v3>KhmH0c?lK00-t$pRzzM|_Dzb(N63ZtO+_3wRSyDlCh|8cBk zoY;P6{obV$w%?twLmkgT&>+NOk#2bkFy(m!R{>~V-I5M7Ln;gS$^jk^+B|fS<&W@< z2acy>HDje`l){GFG;CC2S@=f_r-p%pJvAnXLIw>Y2EUpc%=skS zY-9|Q^uFbS_8!mw-<8eeACG@G1F$r!Nc2PhaUe(O4^`xvL>PvH?aLgizC zRhz|PsStmG{Z&Y7f&DdAI*O$BtB~HJ`S*-?3+%5#`ith@Gcr+p)vrR%6wSY9?(Wra z*$oMul6$c19xOPXwe}Z0RY%CO7tV;PHn=}?_gC$3hb8Ust*W1VRCVGLF@r-6OGlOL z1nbwg@a@-)Pl1S~rviQ2@P6osem{MI-JjVzs&;r^C1&u~@Lx)n{wf^r)nU#`IPYri piEUTWwyVPV3m0!staIz!&m4ChaO_oP=U@6MSk*?jZopz9`G41?sF?r& diff --git a/express_relay/sdk/python/utils/__pycache__/types_liquidation_adapter.cpython-311.pyc b/express_relay/sdk/python/utils/__pycache__/types_liquidation_adapter.cpython-311.pyc deleted file mode 100644 index 6782feda5a52a590df26842837272f09b269b3a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2034 zcmaKt&u`;I6vxN$*!j^kZ9{0g3x!f4p{&%+f$IthSc%dfsnB*0^kR8E!>(1w_Rct~ zhLeLha6#%}rR}jNV8vg;Es<6t@~ILhZmHVSiUaSBo8N)S%=4RhwrBi4^PT*sH7j^~(`l?+20GC&U5vF;hp&e@)$&jxI%lt@~&b423LVkyqmg9_yQHU8c zFh-GJDRK;=-%g@9NR(M3J3xDGjRV~RMwL;Dwx=!TW&Z;hhHC%rT-B*N@V~vsH z%wxiJPMP)qMwORAkLmD6n&ggx6YhT#wUV3@_>r)kC^79u(DbAFYa_{7mTxz>WhDj6 zY6Pt5pik*Ywt@J(gU^n(e!_hg_aSL0EGF z#%p0KdbaI|KuYJC?fNx-8VW9@CAep|YLcC(I|9Gfj9gFFBKTrydG7P3%hVi}&FnDZ z;(pi?2uc|Wj@$whkNtY3mG~e&Xdm>7OYvU3*M=7FiKW3q^K+Oo|Nk7SZ0f_j(xGPz zJyUrGH}Al7X#p15xudz^Q+f1K=E>^*=%WwAFc4AGcY#+a7c=7q-{rNh#!_;Yta7tP|c!ih_rB%ZiY{OiF?~+zq2aKl=ix!L9XbR^;J*0%al= z&=k=uf=Q_70(+JH5)P`AE~8f-7<^VX!Hg5RtgLk?)-GMU9MGBLER^Eic(=VfaesOB z?DXaR4&8*>rJI+P_4DbQ1u2Hrxca;4!LTvpk5fklYqzr3X<)?tas8mNOi z5oaKe2~357A_9?OrwaK3Aa|JsB6ht*e*(LsBHgFH9W@0XiYO5EYHk|tdoZ0Moainb z2MV+Cq!n=~rVwx7ooGIQCQrePBkq+~&-L?1XWzdmBbby;Z{wqjPk+-d?!DRA?og~< zy4_oTCq9f1+lM2a7H)OgTd8y?)-J8|^2K;3-f8dj?ta*zSi8!ClTyZ|%%}VXM5ZZ^ zsIrtI9+CekWo81WY_c)S=}6eVghLagBJki5@1m)oK^clQFm;hbcPf9M;24@JG>L1R z$lvgD2l^PLXE-t@qf}36Ch(>8s+pK@XuAGWf&UAsN|h`XI5qp^ir(fPq$m7m0+*yg zlYfBe>zbzZ$orki+BY-Wa-YnapITQqdNT?Y%Gyew%$oCWuW?B Z(VJ0d)zH@aWY%1KeTAbpb3!UR{{qYS>RSK+ From 64b714251b9a518cdc99880bf66c17099fe71911 Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 22 Feb 2024 14:17:48 -0500 Subject: [PATCH 04/58] create class for simple searcher --- .../searcher/examples/simple_searcher.py | 204 +++++++++--------- .../sdk/python/searcher/searcher_utils.py | 1 - .../python/utils/types_liquidation_adapter.py | 10 + 3 files changed, 116 insertions(+), 99 deletions(-) diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py index 1a682607da..92523e634b 100644 --- a/express_relay/sdk/python/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -8,88 +8,110 @@ from eth_account import Account from searcher.searcher_utils import BidInfo, construct_signature_liquidator -from utils.types_liquidation_adapter import LiquidationOpportunity +from utils.types_liquidation_adapter import LiquidationOpportunity, OpportunityBid logger = logging.getLogger(__name__) VALID_UNTIL = 1_000_000_000_000 +class SimpleSearcher: + def __init__(self, private_key: str, chain_id: str, default_bid: int, liquidation_server_url: str): + self.private_key = private_key + self.chain_id = chain_id + self.default_bid = default_bid + self.liquidation_server_url = liquidation_server_url + self.liquidation_opportunities = [] + + async def get_liquidation_opportunities(self): + async with httpx.AsyncClient() as client: + self.liquidation_opportunities = ( + await client.get( + urllib.parse.urljoin( + self.liquidation_server_url, "/v1/liquidation/opportunities" + ), + params={"chain_id": self.chain_id}, + ) + ).json() -def assess_liquidation_opportunity( - default_bid: int, - opp: LiquidationOpportunity, -) -> BidInfo | None: - """ - Assesses whether a liquidation opportunity is worth liquidating; if so, returns the bid and valid_until timestamp. Otherwise returns None. - This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the liquidation. - There are many ways to evaluate this, but the most common way is to check that the value of the amount the searcher will receive from the liquidation exceeds the value of the amount repaid. - Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation. - If the opporutnity is deemed worthwhile, this function can return a bid amount representing the amount of native token to bid on this opportunity, and a timestamp representing the time at which the transaction will expire. - Otherwise, this function can return None. - Args: - default_bid: The default amount of bid for liquidation opportunities. - opp: A LiquidationOpportunity object, representing a single liquidation opportunity. - Returns: - If the opportunity is deemed worthwhile, this function can return a BidInfo object, representing the user's bid and the timestamp at which the user's bid should expire. If the LiquidationOpportunity is not deemed worthwhile, this function can return None. - """ - user_liquidation_params = { - "bid": default_bid, - "valid_until": VALID_UNTIL, - } - return user_liquidation_params - - -class OpportunityBid(TypedDict): - opportunity_id: str - permission_key: str - amount: str - valid_until: str - liquidator: str - signature: str - - -def create_liquidation_transaction( - opp: LiquidationOpportunity, sk_liquidator: str, bid_info: BidInfo -) -> OpportunityBid: - """ - Creates a bid for a liquidation opportunity. - Args: - opp: A LiquidationOpportunity object, representing a single liquidation opportunity. - sk_liquidator: A 0x-prefixed hex string representing the liquidator's private key. - bid_info: necessary information for the liquidation bid - Returns: - An OpportunityBid object which can be sent to the liquidation server - """ - repay_tokens = [ - (opp["contract"], int(opp["amount"])) for opp in opp["repay_tokens"] - ] - receipt_tokens = [ - (opp["contract"], int(opp["amount"])) for opp in opp["receipt_tokens"] - ] - - liquidator = Account.from_key(sk_liquidator).address - liq_calldata = bytes.fromhex(opp["calldata"].replace("0x", "")) - - signature_liquidator = construct_signature_liquidator( - repay_tokens, - receipt_tokens, - opp["contract"], - liq_calldata, - int(opp["value"]), - bid_info, - sk_liquidator, - ) - - opportunity_bid = { - "opportunity_id": opp["opportunity_id"], - "permission_key": opp["permission_key"], - "amount": str(bid_info["bid"]), - "valid_until": str(bid_info["valid_until"]), - "liquidator": liquidator, - "signature": bytes(signature_liquidator.signature).hex(), - } + def assess_liquidation_opportunity( + self, + opp: LiquidationOpportunity, + ) -> BidInfo | None: + """ + Assesses whether a liquidation opportunity is worth liquidating; if so, returns the bid and valid_until timestamp. Otherwise returns None. + This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the liquidation. + There are many ways to evaluate this, but the most common way is to check that the value of the amount the searcher will receive from the liquidation exceeds the value of the amount repaid. + Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation. + If the opporutnity is deemed worthwhile, this function can return a bid amount representing the amount of native token to bid on this opportunity, and a timestamp representing the time at which the transaction will expire. + Otherwise, this function can return None. + In this simple searcher, the function always (naively) returns the default bid and a valid_until timestamp. + Args: + default_bid: The default amount of bid for liquidation opportunities. + opp: A LiquidationOpportunity object, representing a single liquidation opportunity. + Returns: + If the opportunity is deemed worthwhile, this function can return a BidInfo object, representing the user's bid and the timestamp at which the user's bid should expire. If the LiquidationOpportunity is not deemed worthwhile, this function can return None. + """ + user_liquidation_params = { + "bid": self.default_bid, + "valid_until": VALID_UNTIL, + } + return user_liquidation_params + + def create_liquidation_transaction( + self, opp: LiquidationOpportunity, bid_info: BidInfo + ) -> OpportunityBid: + """ + Creates a bid for a liquidation opportunity. + Args: + opp: A LiquidationOpportunity object, representing a single liquidation opportunity. + sk_liquidator: A 0x-prefixed hex string representing the liquidator's private key. + bid_info: necessary information for the liquidation bid + Returns: + An OpportunityBid object which can be sent to the liquidation server + """ + repay_tokens = [ + (opp["contract"], int(opp["amount"])) for opp in opp["repay_tokens"] + ] + receipt_tokens = [ + (opp["contract"], int(opp["amount"])) for opp in opp["receipt_tokens"] + ] + + liquidator = Account.from_key(self.private_key).address + liq_calldata = bytes.fromhex(opp["calldata"].replace("0x", "")) + + signature_liquidator = construct_signature_liquidator( + repay_tokens, + receipt_tokens, + opp["contract"], + liq_calldata, + int(opp["value"]), + bid_info, + self.private_key, + ) + + opportunity_bid = { + "opportunity_id": opp["opportunity_id"], + "permission_key": opp["permission_key"], + "amount": str(bid_info["bid"]), + "valid_until": str(bid_info["valid_until"]), + "liquidator": liquidator, + "signature": bytes(signature_liquidator.signature).hex(), + } + + return opportunity_bid + + async def submit_bid(self, opportunity_bid: OpportunityBid): + async with httpx.AsyncClient() as client: + resp = await client.post( + urllib.parse.urljoin( + self.liquidation_server_url, + f"/v1/liquidation/opportunities/{opportunity_bid['opportunity_id']}/bids", + ), + json=opportunity_bid, + timeout=20, + ) + return resp - return opportunity_bid async def main(): @@ -133,25 +155,20 @@ async def main(): sk_liquidator = args.private_key liquidator = Account.from_key(sk_liquidator).address logger.info("Liquidator address: %s", liquidator) - client = httpx.AsyncClient() + + simple_searcher = SimpleSearcher(sk_liquidator, args.chain_id, args.bid, args.liquidation_server_url) + while True: try: - accounts_liquidatable = ( - await client.get( - urllib.parse.urljoin( - args.liquidation_server_url, "/v1/liquidation/opportunities" - ), - params={"chain_id": args.chain_id}, - ) - ).json() + await simple_searcher.get_liquidation_opportunities() except Exception as e: logger.error(e) await asyncio.sleep(5) continue - logger.debug("Found %d liquidation opportunities", len(accounts_liquidatable)) + logger.debug("Found %d liquidation opportunities", len(simple_searcher.liquidation_opportunities)) - for liquidation_opp in accounts_liquidatable: + for liquidation_opp in simple_searcher.liquidation_opportunities: opp_id = liquidation_opp["opportunity_id"] if liquidation_opp["version"] != "v1": logger.warning( @@ -160,21 +177,12 @@ async def main(): liquidation_opp["version"], ) continue - bid_info = assess_liquidation_opportunity(args.bid, liquidation_opp) + bid_info = simple_searcher.assess_liquidation_opportunity(liquidation_opp) if bid_info is not None: - tx = create_liquidation_transaction( - liquidation_opp, sk_liquidator, bid_info - ) + tx = simple_searcher.create_liquidation_transaction(liquidation_opp, bid_info) - resp = await client.post( - urllib.parse.urljoin( - args.liquidation_server_url, - f"/v1/liquidation/opportunities/{opp_id}/bids", - ), - json=tx, - timeout=20, - ) + resp = await simple_searcher.submit_bid(tx) logger.info( "Submitted bid amount %s for opportunity %s, server response: %s", bid_info["bid"], diff --git a/express_relay/sdk/python/searcher/searcher_utils.py b/express_relay/sdk/python/searcher/searcher_utils.py index 0c8550e4cf..3650149385 100644 --- a/express_relay/sdk/python/searcher/searcher_utils.py +++ b/express_relay/sdk/python/searcher/searcher_utils.py @@ -10,7 +10,6 @@ class BidInfo(TypedDict): bid: int valid_until: int - def construct_signature_liquidator( repay_tokens: list[(str, int)], receipt_tokens: list[(str, int)], diff --git a/express_relay/sdk/python/utils/types_liquidation_adapter.py b/express_relay/sdk/python/utils/types_liquidation_adapter.py index 28b868a29e..56c2244437 100644 --- a/express_relay/sdk/python/utils/types_liquidation_adapter.py +++ b/express_relay/sdk/python/utils/types_liquidation_adapter.py @@ -26,6 +26,16 @@ class LiquidationOpportunity(TypedDict): # Opportunity format version, used to determine how to interpret the opportunity data version: str +class OpportunityBid(TypedDict): + opportunity_id: str + permission_key: str + # The bid amount + amount: str + # The last block number at which the transaction will be valid + valid_until: str + liquidator: str + signature: str + class LiquidationAdapterCalldata(TypedDict): repay_tokens: list[(str, int)] From 244d8e07c03fc536b7a40d0b7662809d1468b8be Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 22 Feb 2024 17:34:50 -0500 Subject: [PATCH 05/58] add websocket to python searcher sdk --- .../searcher/examples/simple_searcher.py | 108 ++++------- .../sdk/python/searcher/searcher_utils.py | 169 +++++++++++++----- 2 files changed, 159 insertions(+), 118 deletions(-) diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py index 92523e634b..9222ff2a6e 100644 --- a/express_relay/sdk/python/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -1,37 +1,18 @@ import argparse import asyncio import logging -import urllib.parse -from typing import TypedDict -import httpx -from eth_account import Account - -from searcher.searcher_utils import BidInfo, construct_signature_liquidator +from searcher.searcher_utils import BidInfo, SearcherClient from utils.types_liquidation_adapter import LiquidationOpportunity, OpportunityBid logger = logging.getLogger(__name__) VALID_UNTIL = 1_000_000_000_000 -class SimpleSearcher: - def __init__(self, private_key: str, chain_id: str, default_bid: int, liquidation_server_url: str): - self.private_key = private_key - self.chain_id = chain_id +class SimpleSearcher(SearcherClient): + def __init__(self, private_key: str, chain_id: str, liquidation_server_url: str, default_bid: int): + super().__init__(private_key, chain_id, liquidation_server_url) self.default_bid = default_bid - self.liquidation_server_url = liquidation_server_url - self.liquidation_opportunities = [] - - async def get_liquidation_opportunities(self): - async with httpx.AsyncClient() as client: - self.liquidation_opportunities = ( - await client.get( - urllib.parse.urljoin( - self.liquidation_server_url, "/v1/liquidation/opportunities" - ), - params={"chain_id": self.chain_id}, - ) - ).json() def assess_liquidation_opportunity( self, @@ -69,24 +50,9 @@ def create_liquidation_transaction( Returns: An OpportunityBid object which can be sent to the liquidation server """ - repay_tokens = [ - (opp["contract"], int(opp["amount"])) for opp in opp["repay_tokens"] - ] - receipt_tokens = [ - (opp["contract"], int(opp["amount"])) for opp in opp["receipt_tokens"] - ] - - liquidator = Account.from_key(self.private_key).address - liq_calldata = bytes.fromhex(opp["calldata"].replace("0x", "")) - - signature_liquidator = construct_signature_liquidator( - repay_tokens, - receipt_tokens, - opp["contract"], - liq_calldata, - int(opp["value"]), + signature_liquidator = self.construct_signature_liquidator( + opp, bid_info, - self.private_key, ) opportunity_bid = { @@ -94,25 +60,12 @@ def create_liquidation_transaction( "permission_key": opp["permission_key"], "amount": str(bid_info["bid"]), "valid_until": str(bid_info["valid_until"]), - "liquidator": liquidator, + "liquidator": self.liquidator, "signature": bytes(signature_liquidator.signature).hex(), } return opportunity_bid - async def submit_bid(self, opportunity_bid: OpportunityBid): - async with httpx.AsyncClient() as client: - resp = await client.post( - urllib.parse.urljoin( - self.liquidation_server_url, - f"/v1/liquidation/opportunities/{opportunity_bid['opportunity_id']}/bids", - ), - json=opportunity_bid, - timeout=20, - ) - return resp - - async def main(): parser = argparse.ArgumentParser() @@ -141,6 +94,13 @@ async def main(): required=True, help="Liquidation server endpoint to use for fetching opportunities and submitting bids", ) + parser.add_argument( + "--use-ws", + action="store_true", + dest="use_ws", + default=False, + help="Use websocket to fetch liquidation opportunities", + ) args = parser.parse_args() logger.setLevel(logging.INFO if args.verbose == 0 else logging.DEBUG) @@ -153,34 +113,44 @@ async def main(): logger.addHandler(log_handler) sk_liquidator = args.private_key - liquidator = Account.from_key(sk_liquidator).address - logger.info("Liquidator address: %s", liquidator) - simple_searcher = SimpleSearcher(sk_liquidator, args.chain_id, args.bid, args.liquidation_server_url) + simple_searcher = SimpleSearcher(sk_liquidator, args.chain_id, args.liquidation_server_url, args.bid) + logger.info("Liquidator address: %s", simple_searcher.liquidator) + + if args.use_ws: + # populate liquidation opportunities with initial call to http endpoint + await simple_searcher.get_liquidation_opportunities() + logging.debug("Using websocket to fetch liquidation opportunities") + ws_call = simple_searcher.ws_liquidation_opportunities() + asyncio.create_task(ws_call) + else: + logging.debug("Using http to fetch liquidation opportunities") while True: - try: - await simple_searcher.get_liquidation_opportunities() - except Exception as e: - logger.error(e) - await asyncio.sleep(5) - continue + if not args.use_ws: + try: + await simple_searcher.get_liquidation_opportunities() + except Exception as e: + logger.error(e) + await asyncio.sleep(5) + continue logger.debug("Found %d liquidation opportunities", len(simple_searcher.liquidation_opportunities)) - for liquidation_opp in simple_searcher.liquidation_opportunities: - opp_id = liquidation_opp["opportunity_id"] - if liquidation_opp["version"] != "v1": + for permission_key in simple_searcher.liquidation_opportunities.keys(): + liquidation_opportunity = simple_searcher.liquidation_opportunities[permission_key] + opp_id = liquidation_opportunity["opportunity_id"] + if liquidation_opportunity["version"] != "v1": logger.warning( "Opportunity %s has unsupported version %s", opp_id, - liquidation_opp["version"], + liquidation_opportunity["version"], ) continue - bid_info = simple_searcher.assess_liquidation_opportunity(liquidation_opp) + bid_info = simple_searcher.assess_liquidation_opportunity(liquidation_opportunity) if bid_info is not None: - tx = simple_searcher.create_liquidation_transaction(liquidation_opp, bid_info) + tx = simple_searcher.create_liquidation_transaction(liquidation_opportunity, bid_info) resp = await simple_searcher.submit_bid(tx) logger.info( diff --git a/express_relay/sdk/python/searcher/searcher_utils.py b/express_relay/sdk/python/searcher/searcher_utils.py index 3650149385..7b623636ec 100644 --- a/express_relay/sdk/python/searcher/searcher_utils.py +++ b/express_relay/sdk/python/searcher/searcher_utils.py @@ -3,59 +3,130 @@ import web3 from eth_abi import encode from eth_account.datastructures import SignedMessage +from eth_account import Account from web3.auto import w3 +import httpx +import urllib.parse +import websockets +import json + +from utils.types_liquidation_adapter import OpportunityBid, LiquidationOpportunity class BidInfo(TypedDict): bid: int valid_until: int -def construct_signature_liquidator( - repay_tokens: list[(str, int)], - receipt_tokens: list[(str, int)], - address: str, - liq_calldata: bytes, - value: int, - bid_info: BidInfo, - secret_key: str, -) -> SignedMessage: - """ - Constructs a signature for a liquidator's bid to submit to the liquidation server. - - Args: - repay_tokens: A list of tuples (token address, amount) representing the tokens to repay. - receipt_tokens: A list of tuples (token address, amount) representing the tokens to receive. - address: The address of the protocol contract for the liquidation. - liq_calldata: The calldata for the liquidation method call. - value: The value for the liquidation method call. - bid: The amount of native token to bid on this opportunity. - valid_until: The timestamp at which the transaction will expire. - secret_key: A 0x-prefixed hex string representing the liquidator's private key. - Returns: - A SignedMessage object, representing the liquidator's signature. - """ - - digest = encode( - [ - "(address,uint256)[]", - "(address,uint256)[]", - "address", - "bytes", - "uint256", - "uint256", - "uint256", - ], - [ - repay_tokens, - receipt_tokens, - address, - liq_calldata, - value, - bid_info["bid"], - bid_info["valid_until"], - ], - ) - msg_data = web3.Web3.solidity_keccak(["bytes"], [digest]) - signature = w3.eth.account.signHash(msg_data, private_key=secret_key) - - return signature +class SearcherClient: + def __init__(self, private_key: str, chain_id: str, liquidation_server_url: str): + self.private_key = private_key + self.liquidator = Account.from_key(private_key).address + self.chain_id = chain_id + self.liquidation_server_url = liquidation_server_url + if self.liquidation_server_url.startswith("https"): + self.ws_endpoint = f"wss{self.liquidation_server_url[5:]}/v1/ws" + elif self.liquidation_server_url.startswith("http"): + self.ws_endpoint = f"ws{self.liquidation_server_url[4:]}/v1/ws" + else: + raise ValueError("Invalid liquidation server URL") + self.ws_msg_counter = 0 + self.liquidation_opportunities = {} + + async def get_liquidation_opportunities(self): + async with httpx.AsyncClient() as client: + opportunities = ( + await client.get( + urllib.parse.urljoin( + self.liquidation_server_url, "/v1/liquidation/opportunities" + ), + params={"chain_id": self.chain_id}, + ) + ).json() + + for opportunity in opportunities: + self.liquidation_opportunities[opportunity['permission_key']] = opportunity + + async def ws_liquidation_opportunities(self): + async with websockets.connect(self.ws_endpoint) as ws: + # subscribe to this chain_id opportunities ws + json_subscribe = { + "method": "subscribe", + "params": { + "chain_ids": [self.chain_id], + }, + "id": str(self.ws_msg_counter), + } + await ws.send(json.dumps(json_subscribe)) + self.ws_msg_counter += 1 + + while True: + msg = json.loads(await ws.recv()) + status = msg.get("status") + if status and status != "success": + raise Exception(f"Error in websocket subscription: {msg.get('result')}") + + try: + if msg.get("type") != "new_opportunity": + continue + + opportunity = msg["opportunity"] + self.liquidation_opportunities[opportunity['permission_key']] = opportunity + + except Exception as e: + raise Exception(f"Error in websocket message: {e}") + + async def submit_bid(self, opportunity_bid: OpportunityBid): + async with httpx.AsyncClient() as client: + resp = await client.post( + urllib.parse.urljoin( + self.liquidation_server_url, + f"/v1/liquidation/opportunities/{opportunity_bid['opportunity_id']}/bids", + ), + json=opportunity_bid, + timeout=20, + ) + return resp + + def construct_signature_liquidator( + self, + liquidation_opportunity: LiquidationOpportunity, + bid_info: BidInfo, + ) -> SignedMessage: + """ + Constructs a signature for a liquidator's bid to submit to the liquidation server. + + Args: + liquidation_opportunity: An object of type LiquidationOpportnity. + bid_info: An object of type BidInfo representing the bid amount and validity constraints set by the liquidator. + Returns: + A SignedMessage object, representing the liquidator's signature. + """ + + repay_tokens = [(token["contract"], int(token["amount"])) for token in liquidation_opportunity["repay_tokens"]] + receipt_tokens = [(token["contract"], int(token["amount"])) for token in liquidation_opportunity["receipt_tokens"]] + calldata = bytes.fromhex(liquidation_opportunity["calldata"].replace("0x", "")) + + digest = encode( + [ + "(address,uint256)[]", + "(address,uint256)[]", + "address", + "bytes", + "uint256", + "uint256", + "uint256", + ], + [ + repay_tokens, + receipt_tokens, + liquidation_opportunity["contract"], + calldata, + int(liquidation_opportunity["value"]), + bid_info["bid"], + bid_info["valid_until"], + ], + ) + msg_data = web3.Web3.solidity_keccak(["bytes"], [digest]) + signature = w3.eth.account.signHash(msg_data, private_key=self.private_key) + + return signature From 8dd3e74832cc8a152af8d3a87d13b4c09685f230 Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 23 Feb 2024 10:45:40 -0500 Subject: [PATCH 06/58] finish ws, avoid storing liquidation opportunities within client --- .../searcher/examples/simple_searcher.py | 74 ++++++++++--------- .../sdk/python/searcher/searcher_utils.py | 17 +++-- 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py index 9222ff2a6e..b0a50a3613 100644 --- a/express_relay/sdk/python/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -66,6 +66,19 @@ def create_liquidation_transaction( return opportunity_bid + async def ws_opportunity_handler( + self, opp: LiquidationOpportunity + ): + bid_info = self.assess_liquidation_opportunity(opp) + opp_bid = self.create_liquidation_transaction(opp, bid_info) + resp = await self.submit_bid(opp_bid) + logger.info( + "Submitted bid amount %s for opportunity %s, server response: %s", + bid_info["bid"], + opp_bid["opportunity_id"], + resp.text, + ) + async def main(): parser = argparse.ArgumentParser() @@ -118,49 +131,44 @@ async def main(): logger.info("Liquidator address: %s", simple_searcher.liquidator) if args.use_ws: - # populate liquidation opportunities with initial call to http endpoint - await simple_searcher.get_liquidation_opportunities() logging.debug("Using websocket to fetch liquidation opportunities") - ws_call = simple_searcher.ws_liquidation_opportunities() - asyncio.create_task(ws_call) + await simple_searcher.ws_liquidation_opportunities(simple_searcher.ws_opportunity_handler) else: logging.debug("Using http to fetch liquidation opportunities") - while True: - if not args.use_ws: + while True: try: - await simple_searcher.get_liquidation_opportunities() + liquidation_opportunities = await simple_searcher.get_liquidation_opportunities() except Exception as e: logger.error(e) await asyncio.sleep(5) continue - logger.debug("Found %d liquidation opportunities", len(simple_searcher.liquidation_opportunities)) - - for permission_key in simple_searcher.liquidation_opportunities.keys(): - liquidation_opportunity = simple_searcher.liquidation_opportunities[permission_key] - opp_id = liquidation_opportunity["opportunity_id"] - if liquidation_opportunity["version"] != "v1": - logger.warning( - "Opportunity %s has unsupported version %s", - opp_id, - liquidation_opportunity["version"], - ) - continue - bid_info = simple_searcher.assess_liquidation_opportunity(liquidation_opportunity) - - if bid_info is not None: - tx = simple_searcher.create_liquidation_transaction(liquidation_opportunity, bid_info) - - resp = await simple_searcher.submit_bid(tx) - logger.info( - "Submitted bid amount %s for opportunity %s, server response: %s", - bid_info["bid"], - opp_id, - resp.text, - ) - - await asyncio.sleep(1) + logger.debug("Found %d liquidation opportunities", len(liquidation_opportunities)) + + for liquidation_opportunity in liquidation_opportunities: + opp_id = liquidation_opportunity["opportunity_id"] + if liquidation_opportunity["version"] != "v1": + logger.warning( + "Opportunity %s has unsupported version %s", + opp_id, + liquidation_opportunity["version"], + ) + continue + bid_info = simple_searcher.assess_liquidation_opportunity(liquidation_opportunity) + + if bid_info is not None: + tx = simple_searcher.create_liquidation_transaction(liquidation_opportunity, bid_info) + + resp = await simple_searcher.submit_bid(tx) + logger.info( + "Submitted bid amount %s for opportunity %s, server response: %s", + bid_info["bid"], + opp_id, + resp.text, + ) + + await asyncio.sleep(1) if __name__ == "__main__": diff --git a/express_relay/sdk/python/searcher/searcher_utils.py b/express_relay/sdk/python/searcher/searcher_utils.py index 7b623636ec..14fdb1111e 100644 --- a/express_relay/sdk/python/searcher/searcher_utils.py +++ b/express_relay/sdk/python/searcher/searcher_utils.py @@ -30,9 +30,8 @@ def __init__(self, private_key: str, chain_id: str, liquidation_server_url: str) else: raise ValueError("Invalid liquidation server URL") self.ws_msg_counter = 0 - self.liquidation_opportunities = {} - async def get_liquidation_opportunities(self): + async def get_liquidation_opportunities(self) -> list[LiquidationOpportunity]: async with httpx.AsyncClient() as client: opportunities = ( await client.get( @@ -43,12 +42,16 @@ async def get_liquidation_opportunities(self): ) ).json() - for opportunity in opportunities: - self.liquidation_opportunities[opportunity['permission_key']] = opportunity + return opportunities - async def ws_liquidation_opportunities(self): + async def ws_liquidation_opportunities(self, opportunity_handler): + """ + Connects to the liquidation server's websocket and handles new liquidation opportunities. + + Args: + opportunity_handler (async func): An async function that defines how to handle new liquidation opportunities. Should take in one external argument of type LiquidationOpportunity. + """ async with websockets.connect(self.ws_endpoint) as ws: - # subscribe to this chain_id opportunities ws json_subscribe = { "method": "subscribe", "params": { @@ -70,7 +73,7 @@ async def ws_liquidation_opportunities(self): continue opportunity = msg["opportunity"] - self.liquidation_opportunities[opportunity['permission_key']] = opportunity + await opportunity_handler(opportunity) except Exception as e: raise Exception(f"Error in websocket message: {e}") From fcaff73bf3c5061316b05af2ac1a939b2c3b3639 Mon Sep 17 00:00:00 2001 From: ani Date: Sat, 24 Feb 2024 19:04:27 -0500 Subject: [PATCH 07/58] python scripts now working w auto type generation --- express_relay/sdk/python/searcher/README.md | 33 ++++++++ .../searcher/examples/simple_searcher.py | 77 ++++++++----------- .../sdk/python/searcher/pyproject.toml | 13 ++++ .../sdk/python/searcher/searcher_utils.py | 46 ++++++----- .../python/utils/types_liquidation_adapter.py | 56 -------------- 5 files changed, 102 insertions(+), 123 deletions(-) create mode 100644 express_relay/sdk/python/searcher/README.md create mode 100644 express_relay/sdk/python/searcher/pyproject.toml delete mode 100644 express_relay/sdk/python/utils/types_liquidation_adapter.py diff --git a/express_relay/sdk/python/searcher/README.md b/express_relay/sdk/python/searcher/README.md new file mode 100644 index 0000000000..1c627a0bdf --- /dev/null +++ b/express_relay/sdk/python/searcher/README.md @@ -0,0 +1,33 @@ +# Pyth Express Relay Searcher Python SDK + +Utility library for searchers to interact with the Pyth Express Relay API. + +The SDK includes a basic Searcher client for connecting to the Express Relay server as well as an example SimpleSearcher class that provides a simple workflow for assessing and bidding on liquidation opportunities. + +## Installation + +### pip + +### openapi-generator + +You can use the `openapi-generator` command line tool to auto generate the types you will need for the client. To install, run + +``` +$ brew install openapi-generator +``` + +To generate the types from the Express Relay server, run + +``` +$ openapi-generator generate -i https://per-staging.dourolabs.app/docs/openapi.json -g python -o schema +``` + +## Quickstart + +To run the simple searcher script, you can run + +``` +$ python3 -m examples.simple_searcher --private-key --chain-id development --verbose --liquidation-server-url https://per-staging.dourolabs.app/ +``` + +This simple example runs a searcher that queries the Express Relay liquidation server for available liquidation opportunities and naively submits a bid on each available opportunity. \ No newline at end of file diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py index b0a50a3613..08620db429 100644 --- a/express_relay/sdk/python/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -2,8 +2,10 @@ import asyncio import logging -from searcher.searcher_utils import BidInfo, SearcherClient -from utils.types_liquidation_adapter import LiquidationOpportunity, OpportunityBid +from searcher_utils import BidInfo, SearcherClient + +from openapi_client.models.opportunity_bid import OpportunityBid +from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata logger = logging.getLogger(__name__) @@ -16,66 +18,51 @@ def __init__(self, private_key: str, chain_id: str, liquidation_server_url: str, def assess_liquidation_opportunity( self, - opp: LiquidationOpportunity, + opp: OpportunityParamsWithMetadata, ) -> BidInfo | None: """ - Assesses whether a liquidation opportunity is worth liquidating; if so, returns the bid and valid_until timestamp. Otherwise returns None. + Assesses whether a liquidation opportunity is worth liquidating; if so, returns a BidInfo object. Otherwise returns None. This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the liquidation. There are many ways to evaluate this, but the most common way is to check that the value of the amount the searcher will receive from the liquidation exceeds the value of the amount repaid. Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation. - If the opporutnity is deemed worthwhile, this function can return a bid amount representing the amount of native token to bid on this opportunity, and a timestamp representing the time at which the transaction will expire. - Otherwise, this function can return None. - In this simple searcher, the function always (naively) returns the default bid and a valid_until timestamp. + In this simple searcher, the function always (naively) returns a BidInfo object with the default bid and a valid_until timestamp. Args: default_bid: The default amount of bid for liquidation opportunities. - opp: A LiquidationOpportunity object, representing a single liquidation opportunity. + opp: A OpportunityParamsWithMetadata object, representing a single liquidation opportunity. Returns: - If the opportunity is deemed worthwhile, this function can return a BidInfo object, representing the user's bid and the timestamp at which the user's bid should expire. If the LiquidationOpportunity is not deemed worthwhile, this function can return None. - """ - user_liquidation_params = { - "bid": self.default_bid, - "valid_until": VALID_UNTIL, - } - return user_liquidation_params - - def create_liquidation_transaction( - self, opp: LiquidationOpportunity, bid_info: BidInfo - ) -> OpportunityBid: - """ - Creates a bid for a liquidation opportunity. - Args: - opp: A LiquidationOpportunity object, representing a single liquidation opportunity. - sk_liquidator: A 0x-prefixed hex string representing the liquidator's private key. - bid_info: necessary information for the liquidation bid - Returns: - An OpportunityBid object which can be sent to the liquidation server + If the opportunity is deemed worthwhile, this function can return a BidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. """ signature_liquidator = self.construct_signature_liquidator( opp, - bid_info, + self.default_bid, + VALID_UNTIL, ) opportunity_bid = { - "opportunity_id": opp["opportunity_id"], - "permission_key": opp["permission_key"], - "amount": str(bid_info["bid"]), - "valid_until": str(bid_info["valid_until"]), + "permission_key": opp.permission_key, + "amount": str(self.default_bid), + "valid_until": str(VALID_UNTIL), "liquidator": self.liquidator, "signature": bytes(signature_liquidator.signature).hex(), } + opportunity_bid = OpportunityBid.from_dict(opportunity_bid) + + bid_info = BidInfo( + opportunity_id=opp.opportunity_id, + opportunity_bid=opportunity_bid, + ) - return opportunity_bid + return bid_info async def ws_opportunity_handler( - self, opp: LiquidationOpportunity + self, opp: OpportunityParamsWithMetadata ): bid_info = self.assess_liquidation_opportunity(opp) - opp_bid = self.create_liquidation_transaction(opp, bid_info) - resp = await self.submit_bid(opp_bid) + resp = await self.submit_bid(bid_info) logger.info( "Submitted bid amount %s for opportunity %s, server response: %s", - bid_info["bid"], - opp_bid["opportunity_id"], + bid_info.opportunity_bid.amount, + bid_info.opportunity_id, resp.text, ) @@ -147,24 +134,22 @@ async def main(): logger.debug("Found %d liquidation opportunities", len(liquidation_opportunities)) for liquidation_opportunity in liquidation_opportunities: - opp_id = liquidation_opportunity["opportunity_id"] - if liquidation_opportunity["version"] != "v1": + opp_id = liquidation_opportunity.opportunity_id + if liquidation_opportunity.version != "v1": logger.warning( "Opportunity %s has unsupported version %s", opp_id, - liquidation_opportunity["version"], + liquidation_opportunity.version, ) continue bid_info = simple_searcher.assess_liquidation_opportunity(liquidation_opportunity) if bid_info is not None: - tx = simple_searcher.create_liquidation_transaction(liquidation_opportunity, bid_info) - - resp = await simple_searcher.submit_bid(tx) + resp = await simple_searcher.submit_bid(bid_info) logger.info( "Submitted bid amount %s for opportunity %s, server response: %s", - bid_info["bid"], - opp_id, + bid_info.opportunity_bid.amount, + bid_info.opportunity_id, resp.text, ) diff --git a/express_relay/sdk/python/searcher/pyproject.toml b/express_relay/sdk/python/searcher/pyproject.toml new file mode 100644 index 0000000000..85c6333c07 --- /dev/null +++ b/express_relay/sdk/python/searcher/pyproject.toml @@ -0,0 +1,13 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" + +[tool.poetry] +name = "express-relay-searcher-utils" +version = 0.0.1 +description = "Utilities for searchers to interact with the express relay protocol." +license = "Proprietary" +readme = "README.md" \ No newline at end of file diff --git a/express_relay/sdk/python/searcher/searcher_utils.py b/express_relay/sdk/python/searcher/searcher_utils.py index 14fdb1111e..ff32c99691 100644 --- a/express_relay/sdk/python/searcher/searcher_utils.py +++ b/express_relay/sdk/python/searcher/searcher_utils.py @@ -10,12 +10,13 @@ import websockets import json -from utils.types_liquidation_adapter import OpportunityBid, LiquidationOpportunity +from openapi_client.models.opportunity_bid import OpportunityBid +from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata - -class BidInfo(TypedDict): - bid: int - valid_until: int +class BidInfo: + def __init__(self, opportunity_id: str, opportunity_bid: OpportunityBid): + self.opportunity_id = opportunity_id + self.opportunity_bid = opportunity_bid class SearcherClient: def __init__(self, private_key: str, chain_id: str, liquidation_server_url: str): @@ -31,7 +32,7 @@ def __init__(self, private_key: str, chain_id: str, liquidation_server_url: str) raise ValueError("Invalid liquidation server URL") self.ws_msg_counter = 0 - async def get_liquidation_opportunities(self) -> list[LiquidationOpportunity]: + async def get_liquidation_opportunities(self) -> list[OpportunityParamsWithMetadata]: async with httpx.AsyncClient() as client: opportunities = ( await client.get( @@ -42,6 +43,8 @@ async def get_liquidation_opportunities(self) -> list[LiquidationOpportunity]: ) ).json() + opportunities = [OpportunityParamsWithMetadata.from_dict(opp) for opp in opportunities] + return opportunities async def ws_liquidation_opportunities(self, opportunity_handler): @@ -49,7 +52,7 @@ async def ws_liquidation_opportunities(self, opportunity_handler): Connects to the liquidation server's websocket and handles new liquidation opportunities. Args: - opportunity_handler (async func): An async function that defines how to handle new liquidation opportunities. Should take in one external argument of type LiquidationOpportunity. + opportunity_handler (async func): An async function that defines how to handle new liquidation opportunities. Should take in one external argument of type OpportunityParamsWithMetadata. """ async with websockets.connect(self.ws_endpoint) as ws: json_subscribe = { @@ -73,41 +76,42 @@ async def ws_liquidation_opportunities(self, opportunity_handler): continue opportunity = msg["opportunity"] + opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) await opportunity_handler(opportunity) except Exception as e: raise Exception(f"Error in websocket message: {e}") - async def submit_bid(self, opportunity_bid: OpportunityBid): + async def submit_bid(self, bid_info: BidInfo): async with httpx.AsyncClient() as client: resp = await client.post( urllib.parse.urljoin( self.liquidation_server_url, - f"/v1/liquidation/opportunities/{opportunity_bid['opportunity_id']}/bids", + f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids", ), - json=opportunity_bid, + json=bid_info.opportunity_bid.to_dict(), timeout=20, ) return resp def construct_signature_liquidator( self, - liquidation_opportunity: LiquidationOpportunity, - bid_info: BidInfo, + liquidation_opportunity: OpportunityParamsWithMetadata, + bid: int, + valid_until: int, ) -> SignedMessage: """ Constructs a signature for a liquidator's bid to submit to the liquidation server. Args: - liquidation_opportunity: An object of type LiquidationOpportnity. + liquidation_opportunity: An object representing the liquidation opportunity, of type OpportunityParamsWithMetadata. bid_info: An object of type BidInfo representing the bid amount and validity constraints set by the liquidator. Returns: A SignedMessage object, representing the liquidator's signature. """ - - repay_tokens = [(token["contract"], int(token["amount"])) for token in liquidation_opportunity["repay_tokens"]] - receipt_tokens = [(token["contract"], int(token["amount"])) for token in liquidation_opportunity["receipt_tokens"]] - calldata = bytes.fromhex(liquidation_opportunity["calldata"].replace("0x", "")) + repay_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.repay_tokens] + receipt_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.receipt_tokens] + calldata = bytes.fromhex(liquidation_opportunity.calldata.replace("0x", "")) digest = encode( [ @@ -122,11 +126,11 @@ def construct_signature_liquidator( [ repay_tokens, receipt_tokens, - liquidation_opportunity["contract"], + liquidation_opportunity.contract, calldata, - int(liquidation_opportunity["value"]), - bid_info["bid"], - bid_info["valid_until"], + int(liquidation_opportunity.value), + bid, + valid_until, ], ) msg_data = web3.Web3.solidity_keccak(["bytes"], [digest]) diff --git a/express_relay/sdk/python/utils/types_liquidation_adapter.py b/express_relay/sdk/python/utils/types_liquidation_adapter.py deleted file mode 100644 index 56c2244437..0000000000 --- a/express_relay/sdk/python/utils/types_liquidation_adapter.py +++ /dev/null @@ -1,56 +0,0 @@ -from typing import TypedDict - - -class TokenQty(TypedDict): - contract: str - amount: str - - -class LiquidationOpportunity(TypedDict): - # The unique id of the opportunity - opportunity_id: str - # The id of the chain where the opportunity was found - chain_id: str - # Address of the contract where the liquidation method is called - contract: str - # The calldata that needs to be passed in with the liquidation method call - calldata: str - # The value that needs to be passed in with the liquidation method call - value: str - # The permission key necessary to call the liquidation method - permission_key: str - # A list of tokens that can be used to repay this account's debt. Each entry in the list is a tuple (token address, hex string of repay amount) - repay_tokens: list[TokenQty] - # A list of tokens that ought to be received by the liquidator in exchange for the repay tokens. Each entry in the list is a tuple (token address, hex string of receipt amount) - receipt_tokens: list[TokenQty] - # Opportunity format version, used to determine how to interpret the opportunity data - version: str - -class OpportunityBid(TypedDict): - opportunity_id: str - permission_key: str - # The bid amount - amount: str - # The last block number at which the transaction will be valid - valid_until: str - liquidator: str - signature: str - - -class LiquidationAdapterCalldata(TypedDict): - repay_tokens: list[(str, int)] - expected_receipt_tokens: list[(str, int)] - liquidator: str - contract: str - data: bytes - valid_until: int - bid: int - signature_liquidator: bytes - - -class LiquidationAdapterTransaction(TypedDict): - bid: str - calldata: str - chain_id: str - contract: str - permission_key: str From 6cab8b5d2ba2987f4783936e83992b674bd868b2 Mon Sep 17 00:00:00 2001 From: ani Date: Sat, 24 Feb 2024 19:10:37 -0500 Subject: [PATCH 08/58] minor precommit changes --- express_relay/sdk/js/searcher/src/index.ts | 2 +- express_relay/sdk/js/searcher/src/types.d.ts | 32 ++++++++++++++----- express_relay/sdk/js/searcher/tsconfig.json | 5 +-- express_relay/sdk/python/searcher/README.md | 4 +-- .../searcher/examples/simple_searcher.py | 4 +-- .../sdk/python/searcher/pyproject.toml | 2 +- .../sdk/python/searcher/searcher_utils.py | 4 +-- 7 files changed, 35 insertions(+), 18 deletions(-) diff --git a/express_relay/sdk/js/searcher/src/index.ts b/express_relay/sdk/js/searcher/src/index.ts index 8e9eb387af..afd729c8a7 100644 --- a/express_relay/sdk/js/searcher/src/index.ts +++ b/express_relay/sdk/js/searcher/src/index.ts @@ -158,7 +158,7 @@ export class Client { websocketEndpoint.pathname = "/v1/ws"; this.websocket = new WebSocket(websocketEndpoint.toString()); - this.websocket.on("message", async (data) => { + this.websocket.on("message", async (data: string) => { const message: | components["schemas"]["ServerResultResponse"] | components["schemas"]["ServerUpdateResponse"] = JSON.parse( diff --git a/express_relay/sdk/js/searcher/src/types.d.ts b/express_relay/sdk/js/searcher/src/types.d.ts index 9f5901c62b..bbd2e02d38 100644 --- a/express_relay/sdk/js/searcher/src/types.d.ts +++ b/express_relay/sdk/js/searcher/src/types.d.ts @@ -77,14 +77,14 @@ export interface components { /** @enum {string} */ method: "subscribe"; params: { - chain_ids: components["schemas"]["ChainId"][]; + chain_ids: string[]; }; } | { /** @enum {string} */ method: "unsubscribe"; params: { - chain_ids: components["schemas"]["ChainId"][]; + chain_ids: string[]; }; }; ClientRequest: components["schemas"]["ClientMessage"] & { @@ -157,8 +157,16 @@ export interface components { value: string; }; /** @description Similar to OpportunityParams, but with the opportunity id included. */ - OpportunityParamsWithMetadata: components["schemas"]["OpportunityParams"] & { - creation_time: components["schemas"]["UnixTimestamp"]; + OpportunityParamsWithMetadata: (components["schemas"]["OpportunityParamsV1"] & { + /** @enum {string} */ + version: "v1"; + }) & { + /** + * Format: int64 + * @description Creation time of the opportunity + * @example 1700000000 + */ + creation_time: number; /** * @description The opportunity unique id * @example f47ac10b-58cc-4372-a567-0e02b2c3d479 @@ -220,8 +228,16 @@ export interface components { /** @description Similar to OpportunityParams, but with the opportunity id included. */ OpportunityParamsWithMetadata: { content: { - "application/json": components["schemas"]["OpportunityParams"] & { - creation_time: components["schemas"]["UnixTimestamp"]; + "application/json": (components["schemas"]["OpportunityParamsV1"] & { + /** @enum {string} */ + version: "v1"; + }) & { + /** + * Format: int64 + * @description Creation time of the opportunity + * @example 1700000000 + */ + creation_time: number; /** * @description The opportunity unique id * @example f47ac10b-58cc-4372-a567-0e02b2c3d479 @@ -286,7 +302,7 @@ export interface operations { /** @description Array of liquidation opportunities ready for bidding */ 200: { content: { - "application/json": components["schemas"]["OpportunityParamsWithId"][]; + "application/json": components["schemas"]["OpportunityParamsWithMetadata"][]; }; }; 400: components["responses"]["ErrorBodyResponse"]; @@ -315,7 +331,7 @@ export interface operations { /** @description The created opportunity */ 200: { content: { - "application/json": components["schemas"]["OpportunityParamsWithId"]; + "application/json": components["schemas"]["OpportunityParamsWithMetadata"]; }; }; 400: components["responses"]["ErrorBodyResponse"]; diff --git a/express_relay/sdk/js/searcher/tsconfig.json b/express_relay/sdk/js/searcher/tsconfig.json index 0947cd6409..f977bab2ee 100644 --- a/express_relay/sdk/js/searcher/tsconfig.json +++ b/express_relay/sdk/js/searcher/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "target": "esnext", "module": "commonjs", @@ -8,7 +8,8 @@ "outDir": "./lib", "strict": true, "esModuleInterop": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "typeRoots": ["node_modules/@types"] }, "include": ["src"], "exclude": ["node_modules", "**/__tests__/*"] diff --git a/express_relay/sdk/python/searcher/README.md b/express_relay/sdk/python/searcher/README.md index 1c627a0bdf..fb2c23597b 100644 --- a/express_relay/sdk/python/searcher/README.md +++ b/express_relay/sdk/python/searcher/README.md @@ -27,7 +27,7 @@ $ openapi-generator generate -i https://per-staging.dourolabs.app/docs/openapi.j To run the simple searcher script, you can run ``` -$ python3 -m examples.simple_searcher --private-key --chain-id development --verbose --liquidation-server-url https://per-staging.dourolabs.app/ +$ python3 -m examples.simple_searcher --private-key --chain-id development --verbose --liquidation-server-url https://per-staging.dourolabs.app/ ``` -This simple example runs a searcher that queries the Express Relay liquidation server for available liquidation opportunities and naively submits a bid on each available opportunity. \ No newline at end of file +This simple example runs a searcher that queries the Express Relay liquidation server for available liquidation opportunities and naively submits a bid on each available opportunity. diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py index 08620db429..65bbdd0032 100644 --- a/express_relay/sdk/python/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -4,8 +4,8 @@ from searcher_utils import BidInfo, SearcherClient -from openapi_client.models.opportunity_bid import OpportunityBid -from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata +from schema.openapi_client.models.opportunity_bid import OpportunityBid +from schema.openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata logger = logging.getLogger(__name__) diff --git a/express_relay/sdk/python/searcher/pyproject.toml b/express_relay/sdk/python/searcher/pyproject.toml index 85c6333c07..e0a20f838c 100644 --- a/express_relay/sdk/python/searcher/pyproject.toml +++ b/express_relay/sdk/python/searcher/pyproject.toml @@ -10,4 +10,4 @@ name = "express-relay-searcher-utils" version = 0.0.1 description = "Utilities for searchers to interact with the express relay protocol." license = "Proprietary" -readme = "README.md" \ No newline at end of file +readme = "README.md" diff --git a/express_relay/sdk/python/searcher/searcher_utils.py b/express_relay/sdk/python/searcher/searcher_utils.py index ff32c99691..59593b41f5 100644 --- a/express_relay/sdk/python/searcher/searcher_utils.py +++ b/express_relay/sdk/python/searcher/searcher_utils.py @@ -10,8 +10,8 @@ import websockets import json -from openapi_client.models.opportunity_bid import OpportunityBid -from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata +from schema.openapi_client.models.opportunity_bid import OpportunityBid +from schema.openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata class BidInfo: def __init__(self, opportunity_id: str, opportunity_bid: OpportunityBid): From c14569e2c4a30e525c28856a36c01afbd9c995a2 Mon Sep 17 00:00:00 2001 From: ani Date: Tue, 27 Feb 2024 00:49:05 -0500 Subject: [PATCH 09/58] address comments 1 --- express_relay/sdk/python/searcher/README.md | 10 +- .../searcher/examples/simple_searcher.py | 95 +++--------- .../sdk/python/searcher/pyproject.toml | 9 ++ .../sdk/python/searcher/searcher_utils.py | 141 ++++++++++++------ 4 files changed, 133 insertions(+), 122 deletions(-) diff --git a/express_relay/sdk/python/searcher/README.md b/express_relay/sdk/python/searcher/README.md index fb2c23597b..93fb7f5030 100644 --- a/express_relay/sdk/python/searcher/README.md +++ b/express_relay/sdk/python/searcher/README.md @@ -1,8 +1,10 @@ -# Pyth Express Relay Searcher Python SDK +# Pyth Express Relay Python SDK -Utility library for searchers to interact with the Pyth Express Relay API. +Utility library for searchers and protocols to interact with the Pyth Express Relay API. -The SDK includes a basic Searcher client for connecting to the Express Relay server as well as an example SimpleSearcher class that provides a simple workflow for assessing and bidding on liquidation opportunities. +The SDK includes searcher-side utilities and protocol-side utilities. The searcher-side utilities include a basic Searcher client for connecting to the Express Relay server as well as an example SimpleSearcher class that provides a simple workflow for assessing and bidding on liquidation opportunities. + +# Searcher ## Installation @@ -19,7 +21,7 @@ $ brew install openapi-generator To generate the types from the Express Relay server, run ``` -$ openapi-generator generate -i https://per-staging.dourolabs.app/docs/openapi.json -g python -o schema +$ openapi-generator generate -i https://per-staging.dourolabs.app/docs/openapi.json -g python --additional-properties=generateSourceCodeOnly=true --global-property models,modelTests=false,modelDocs=false ``` ## Quickstart diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py index 65bbdd0032..193c713029 100644 --- a/express_relay/sdk/python/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -1,19 +1,22 @@ import argparse import asyncio import logging +from eth_account.account import Account from searcher_utils import BidInfo, SearcherClient -from schema.openapi_client.models.opportunity_bid import OpportunityBid -from schema.openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata +from openapi_client.models.opportunity_bid import OpportunityBid +from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata logger = logging.getLogger(__name__) VALID_UNTIL = 1_000_000_000_000 class SimpleSearcher(SearcherClient): - def __init__(self, private_key: str, chain_id: str, liquidation_server_url: str, default_bid: int): - super().__init__(private_key, chain_id, liquidation_server_url) + def __init__(self, liquidation_server_url: str, private_key: str, default_bid: int): + super().__init__(liquidation_server_url) + self.private_key = private_key + self.liquidator = Account.from_key(private_key).address self.default_bid = default_bid def assess_liquidation_opportunity( @@ -27,29 +30,15 @@ def assess_liquidation_opportunity( Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation. In this simple searcher, the function always (naively) returns a BidInfo object with the default bid and a valid_until timestamp. Args: - default_bid: The default amount of bid for liquidation opportunities. opp: A OpportunityParamsWithMetadata object, representing a single liquidation opportunity. Returns: If the opportunity is deemed worthwhile, this function can return a BidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. """ - signature_liquidator = self.construct_signature_liquidator( + bid_info = self.sign_bid( opp, self.default_bid, VALID_UNTIL, - ) - - opportunity_bid = { - "permission_key": opp.permission_key, - "amount": str(self.default_bid), - "valid_until": str(VALID_UNTIL), - "liquidator": self.liquidator, - "signature": bytes(signature_liquidator.signature).hex(), - } - opportunity_bid = OpportunityBid.from_dict(opportunity_bid) - - bid_info = BidInfo( - opportunity_id=opp.opportunity_id, - opportunity_bid=opportunity_bid, + self.private_key ) return bid_info @@ -58,13 +47,12 @@ async def ws_opportunity_handler( self, opp: OpportunityParamsWithMetadata ): bid_info = self.assess_liquidation_opportunity(opp) - resp = await self.submit_bid(bid_info) - logger.info( - "Submitted bid amount %s for opportunity %s, server response: %s", - bid_info.opportunity_bid.amount, - bid_info.opportunity_id, - resp.text, - ) + if bid_info: + try: + await self.submit_bid(bid_info) + logger.info(f"Submitted bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}") + except Exception as e: + logger.error(f"Error submitting bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}: {e}") async def main(): @@ -77,10 +65,11 @@ async def main(): help="Private key of the searcher for signing calldata", ) parser.add_argument( - "--chain-id", + "--chain-ids", type=str, required=True, - help="Chain ID of the network to monitor for liquidation opportunities", + nargs="+", + help="Chain ID(s) of the network(s) to monitor for liquidation opportunities", ) parser.add_argument( "--bid", @@ -94,13 +83,6 @@ async def main(): required=True, help="Liquidation server endpoint to use for fetching opportunities and submitting bids", ) - parser.add_argument( - "--use-ws", - action="store_true", - dest="use_ws", - default=False, - help="Use websocket to fetch liquidation opportunities", - ) args = parser.parse_args() logger.setLevel(logging.INFO if args.verbose == 0 else logging.DEBUG) @@ -114,47 +96,10 @@ async def main(): sk_liquidator = args.private_key - simple_searcher = SimpleSearcher(sk_liquidator, args.chain_id, args.liquidation_server_url, args.bid) + simple_searcher = SimpleSearcher(args.liquidation_server_url, sk_liquidator, args.bid) logger.info("Liquidator address: %s", simple_searcher.liquidator) - if args.use_ws: - logging.debug("Using websocket to fetch liquidation opportunities") - await simple_searcher.ws_liquidation_opportunities(simple_searcher.ws_opportunity_handler) - else: - logging.debug("Using http to fetch liquidation opportunities") - - while True: - try: - liquidation_opportunities = await simple_searcher.get_liquidation_opportunities() - except Exception as e: - logger.error(e) - await asyncio.sleep(5) - continue - - logger.debug("Found %d liquidation opportunities", len(liquidation_opportunities)) - - for liquidation_opportunity in liquidation_opportunities: - opp_id = liquidation_opportunity.opportunity_id - if liquidation_opportunity.version != "v1": - logger.warning( - "Opportunity %s has unsupported version %s", - opp_id, - liquidation_opportunity.version, - ) - continue - bid_info = simple_searcher.assess_liquidation_opportunity(liquidation_opportunity) - - if bid_info is not None: - resp = await simple_searcher.submit_bid(bid_info) - logger.info( - "Submitted bid amount %s for opportunity %s, server response: %s", - bid_info.opportunity_bid.amount, - bid_info.opportunity_id, - resp.text, - ) - - await asyncio.sleep(1) - + await simple_searcher.ws_liquidation_opportunities(args.chain_ids, simple_searcher.ws_opportunity_handler) if __name__ == "__main__": asyncio.run(main()) diff --git a/express_relay/sdk/python/searcher/pyproject.toml b/express_relay/sdk/python/searcher/pyproject.toml index e0a20f838c..351be37029 100644 --- a/express_relay/sdk/python/searcher/pyproject.toml +++ b/express_relay/sdk/python/searcher/pyproject.toml @@ -8,6 +8,15 @@ build-backend = "setuptools.build_meta" [tool.poetry] name = "express-relay-searcher-utils" version = 0.0.1 +dependencies = [ + "web3", + "eth_abi", + "eth_account", + "httpx", + "websockets", + "asyncio", + "argparse" +] description = "Utilities for searchers to interact with the express relay protocol." license = "Proprietary" readme = "README.md" diff --git a/express_relay/sdk/python/searcher/searcher_utils.py b/express_relay/sdk/python/searcher/searcher_utils.py index 59593b41f5..8702e49a93 100644 --- a/express_relay/sdk/python/searcher/searcher_utils.py +++ b/express_relay/sdk/python/searcher/searcher_utils.py @@ -1,64 +1,91 @@ -from typing import TypedDict - import web3 from eth_abi import encode -from eth_account.datastructures import SignedMessage -from eth_account import Account +from eth_account.account import Account from web3.auto import w3 import httpx import urllib.parse import websockets import json +from typing import Callable -from schema.openapi_client.models.opportunity_bid import OpportunityBid -from schema.openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata +from openapi_client.models.opportunity_bid import OpportunityBid +from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata class BidInfo: def __init__(self, opportunity_id: str, opportunity_bid: OpportunityBid): self.opportunity_id = opportunity_id self.opportunity_bid = opportunity_bid +class WebsocketTimeoutConfig: + """ + A class to hold the timeout configuration for the websocket connection. + + Args: + open_timeout (int): The timeout for opening the websocket connection. + ping_interval (int): The interval at which to send ping messages to the server. + ping_timeout (int): The timeout for the ping messages. + close_timeout (int): The timeout for closing the websocket connection. + """ + def __init__(self, open_timeout: int = 10, ping_interval: int = 20, ping_timeout: int = 20, close_timeout: int = 10): + self.open_timeout = open_timeout + self.ping_interval = ping_interval + self.ping_timeout = ping_timeout + self.close_timeout = close_timeout + +class SearcherClientException(Exception): + pass + class SearcherClient: - def __init__(self, private_key: str, chain_id: str, liquidation_server_url: str): - self.private_key = private_key - self.liquidator = Account.from_key(private_key).address - self.chain_id = chain_id - self.liquidation_server_url = liquidation_server_url - if self.liquidation_server_url.startswith("https"): - self.ws_endpoint = f"wss{self.liquidation_server_url[5:]}/v1/ws" - elif self.liquidation_server_url.startswith("http"): - self.ws_endpoint = f"ws{self.liquidation_server_url[4:]}/v1/ws" + def __init__(self, server_url: str): + self.server_url = server_url + if self.server_url.startswith("https"): + self.ws_endpoint = urllib.parse.urljoin(f"wss{self.server_url[5:]}", "v1/ws") + elif self.server_url.startswith("http"): + self.ws_endpoint = urllib.parse.urljoin(f"ws{self.server_url[4:]}", "v1/ws") else: raise ValueError("Invalid liquidation server URL") self.ws_msg_counter = 0 - async def get_liquidation_opportunities(self) -> list[OpportunityParamsWithMetadata]: + async def get_liquidation_opportunities(self, chain_id: str, timeout: int = 10) -> list[OpportunityParamsWithMetadata]: + """ + Connects to the liquidation server and fetches liquidation opportunities for a given chain ID. + + Args: + chain_id (str): The chain ID to fetch liquidation opportunities for. + timeout (int): The timeout for the HTTP request. + Returns: + list[OpportunityParamsWithMetadata]: A list of liquidation opportunities. + """ async with httpx.AsyncClient() as client: - opportunities = ( + resp = ( await client.get( urllib.parse.urljoin( - self.liquidation_server_url, "/v1/liquidation/opportunities" + self.server_url, "/v1/liquidation/opportunities" ), - params={"chain_id": self.chain_id}, + params={"chain_id": chain_id}, + timeout=timeout, ) - ).json() + ) - opportunities = [OpportunityParamsWithMetadata.from_dict(opp) for opp in opportunities] + resp.raise_for_status() + + opportunities = [OpportunityParamsWithMetadata.from_dict(opportunity) for opportunity in resp.json()] return opportunities - async def ws_liquidation_opportunities(self, opportunity_handler): + async def ws_liquidation_opportunities(self, chain_ids: list[str], opportunity_handler: Callable[[OpportunityParamsWithMetadata], None], ws_timeout_config: WebsocketTimeoutConfig = WebsocketTimeoutConfig()): """ Connects to the liquidation server's websocket and handles new liquidation opportunities. Args: - opportunity_handler (async func): An async function that defines how to handle new liquidation opportunities. Should take in one external argument of type OpportunityParamsWithMetadata. + chain_ids (list[str]): A list of chain IDs to subscribe to for liquidation opportunities. + opportunity_handler (async func): An async function that defines how to handle new liquidation opportunities. Should take in one external argument of type OpportunityParamsWithMetadata and return nothing. """ - async with websockets.connect(self.ws_endpoint) as ws: + async with websockets.connect(self.ws_endpoint, open_timeout=ws_timeout_config.open_timeout, ping_interval=ws_timeout_config.ping_interval, ping_timeout=ws_timeout_config.ping_timeout, close_timeout=ws_timeout_config.close_timeout) as ws: json_subscribe = { "method": "subscribe", "params": { - "chain_ids": [self.chain_id], + "chain_ids": chain_ids, }, "id": str(self.ws_msg_counter), } @@ -69,45 +96,59 @@ async def ws_liquidation_opportunities(self, opportunity_handler): msg = json.loads(await ws.recv()) status = msg.get("status") if status and status != "success": - raise Exception(f"Error in websocket subscription: {msg.get('result')}") + raise SearcherClientException(f"Error in websocket subscription: {msg.get('result')}") - try: - if msg.get("type") != "new_opportunity": - continue + if msg.get("type") != "new_opportunity": + continue - opportunity = msg["opportunity"] - opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) + opportunity = msg["opportunity"] + opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) + try: await opportunity_handler(opportunity) - except Exception as e: - raise Exception(f"Error in websocket message: {e}") + raise SearcherClientException(f"Error in opportunity handler: {e}") - async def submit_bid(self, bid_info: BidInfo): + async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> httpx.Response: + """ + Submits a bid to the liquidation server. + + Args: + bid_info (BidInfo): An object representing the bid to submit. + timeout (int): The timeout for the HTTP request. + Returns: + httpx.Response: The server's response to the bid submission. Throws an exception if the response is not successful. + """ async with httpx.AsyncClient() as client: resp = await client.post( urllib.parse.urljoin( - self.liquidation_server_url, + self.server_url, f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids", ), json=bid_info.opportunity_bid.to_dict(), - timeout=20, + timeout=timeout, ) + + resp.raise_for_status() + return resp - def construct_signature_liquidator( + def sign_bid( self, liquidation_opportunity: OpportunityParamsWithMetadata, bid: int, valid_until: int, - ) -> SignedMessage: + private_key: str, + ) -> BidInfo: """ - Constructs a signature for a liquidator's bid to submit to the liquidation server. + Constructs a signature for a liquidator's bid and returns the BidInfo object to be submitted to the liquidation server. Args: liquidation_opportunity: An object representing the liquidation opportunity, of type OpportunityParamsWithMetadata. - bid_info: An object of type BidInfo representing the bid amount and validity constraints set by the liquidator. + bid: An integer representing the amount of the bid. + valid_until: An integer representing the block until which the bid is valid. + private_key: A string representing the liquidator's private key. Returns: - A SignedMessage object, representing the liquidator's signature. + A BidInfo object, representing the transaction to submit to the liquidation server. This object contains the liquidator's signature. """ repay_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.repay_tokens] receipt_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.receipt_tokens] @@ -134,6 +175,20 @@ def construct_signature_liquidator( ], ) msg_data = web3.Web3.solidity_keccak(["bytes"], [digest]) - signature = w3.eth.account.signHash(msg_data, private_key=self.private_key) + signature = w3.eth.account.signHash(msg_data, private_key=private_key) + + opportunity_bid = { + "permission_key": liquidation_opportunity.permission_key, + "amount": str(bid), + "valid_until": str(valid_until), + "liquidator": Account.from_key(private_key).address, + "signature": bytes(signature.signature).hex(), + } + opportunity_bid = OpportunityBid.from_dict(opportunity_bid) + + bid_info = BidInfo( + opportunity_id=liquidation_opportunity.opportunity_id, + opportunity_bid=opportunity_bid, + ) - return signature + return bid_info From 2b819bb1e514bc3ea890f9d3a96bcb4ffda266f1 Mon Sep 17 00:00:00 2001 From: ani Date: Tue, 27 Feb 2024 19:23:06 -0500 Subject: [PATCH 10/58] add openapi type generations --- .../searcher/openapi_client/models/bid.py | 95 ++++++++++++ .../openapi_client/models/bid400_response.py | 87 +++++++++++ .../openapi_client/models/bid_result.py | 87 +++++++++++ .../openapi_client/models/client_message.py | 141 ++++++++++++++++++ .../models/client_message_one_of.py | 100 +++++++++++++ .../models/client_message_one_of1.py | 100 +++++++++++++ .../models/client_message_one_of_params.py | 87 +++++++++++ .../openapi_client/models/client_request.py | 94 ++++++++++++ .../models/error_body_response.py | 87 +++++++++++ .../openapi_client/models/opportunity_bid.py | 95 ++++++++++++ .../models/opportunity_params.py | 126 ++++++++++++++++ .../models/opportunity_params_one_of.py | 123 +++++++++++++++ .../models/opportunity_params_v1.py | 114 ++++++++++++++ .../opportunity_params_with_metadata.py | 127 ++++++++++++++++ .../models/server_result_message.py | 141 ++++++++++++++++++ .../models/server_result_message_one_of.py | 94 ++++++++++++ .../models/server_result_message_one_of1.py | 96 ++++++++++++ .../models/server_result_response.py | 95 ++++++++++++ .../models/server_update_response.py | 126 ++++++++++++++++ .../models/server_update_response_one_of.py | 100 +++++++++++++ .../openapi_client/models/token_qty.py | 89 +++++++++++ 21 files changed, 2204 insertions(+) create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/bid.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/bid_result.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/client_message.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/client_request.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py create mode 100644 express_relay/sdk/python/searcher/openapi_client/models/token_qty.py diff --git a/express_relay/sdk/python/searcher/openapi_client/models/bid.py b/express_relay/sdk/python/searcher/openapi_client/models/bid.py new file mode 100644 index 0000000000..c553992250 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/bid.py @@ -0,0 +1,95 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, Field, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class Bid(BaseModel): + """ + Bid + """ # noqa: E501 + amount: StrictStr = Field(description="Amount of bid in wei.") + calldata: StrictStr = Field(description="Calldata for the contract call.") + chain_id: StrictStr = Field(description="The chain id to bid on.") + contract: StrictStr = Field(description="The contract address to call.") + permission_key: StrictStr = Field(description="The permission key to bid on.") + __properties: ClassVar[List[str]] = ["amount", "calldata", "chain_id", "contract", "permission_key"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of Bid from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of Bid from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "amount": obj.get("amount"), + "calldata": obj.get("calldata"), + "chain_id": obj.get("chain_id"), + "contract": obj.get("contract"), + "permission_key": obj.get("permission_key") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py b/express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py new file mode 100644 index 0000000000..d3a315612f --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py @@ -0,0 +1,87 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class Bid400Response(BaseModel): + """ + Bid400Response + """ # noqa: E501 + error: StrictStr + __properties: ClassVar[List[str]] = ["error"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of Bid400Response from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of Bid400Response from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "error": obj.get("error") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/bid_result.py b/express_relay/sdk/python/searcher/openapi_client/models/bid_result.py new file mode 100644 index 0000000000..add5fcc5dc --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/bid_result.py @@ -0,0 +1,87 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class BidResult(BaseModel): + """ + BidResult + """ # noqa: E501 + status: StrictStr + __properties: ClassVar[List[str]] = ["status"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BidResult from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BidResult from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "status": obj.get("status") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message.py b/express_relay/sdk/python/searcher/openapi_client/models/client_message.py new file mode 100644 index 0000000000..edfa13d190 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_message.py @@ -0,0 +1,141 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.client_message_one_of import ClientMessageOneOf +from openapi_client.models.client_message_one_of1 import ClientMessageOneOf1 +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +CLIENTMESSAGE_ONE_OF_SCHEMAS = ["ClientMessageOneOf", "ClientMessageOneOf1"] + +class ClientMessage(BaseModel): + """ + ClientMessage + """ + # data type: ClientMessageOneOf + oneof_schema_1_validator: Optional[ClientMessageOneOf] = None + # data type: ClientMessageOneOf1 + oneof_schema_2_validator: Optional[ClientMessageOneOf1] = None + actual_instance: Optional[Union[ClientMessageOneOf, ClientMessageOneOf1]] = None + one_of_schemas: List[str] = Field(default=Literal["ClientMessageOneOf", "ClientMessageOneOf1"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + discriminator_value_class_map: Dict[str, str] = { + 'ClientRequest': 'ClientRequest' + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = ClientMessage.model_construct() + error_messages = [] + match = 0 + # validate data type: ClientMessageOneOf + if not isinstance(v, ClientMessageOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `ClientMessageOneOf`") + else: + match += 1 + # validate data type: ClientMessageOneOf1 + if not isinstance(v, ClientMessageOneOf1): + error_messages.append(f"Error! Input type `{type(v)}` is not `ClientMessageOneOf1`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into ClientMessageOneOf + try: + instance.actual_instance = ClientMessageOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into ClientMessageOneOf1 + try: + instance.actual_instance = ClientMessageOneOf1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMessageOneOf1]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py new file mode 100644 index 0000000000..90c209c48c --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py @@ -0,0 +1,100 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from openapi_client.models.client_message_one_of_params import ClientMessageOneOfParams +from typing import Optional, Set +from typing_extensions import Self + +class ClientMessageOneOf(BaseModel): + """ + ClientMessageOneOf + """ # noqa: E501 + method: StrictStr + params: ClientMessageOneOfParams + __properties: ClassVar[List[str]] = ["method", "params"] + + @field_validator('method') + def method_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['subscribe']): + raise ValueError("must be one of enum values ('subscribe')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ClientMessageOneOf from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of params + if self.params: + _dict['params'] = self.params.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ClientMessageOneOf from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "method": obj.get("method"), + "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py new file mode 100644 index 0000000000..c81be44468 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py @@ -0,0 +1,100 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from openapi_client.models.client_message_one_of_params import ClientMessageOneOfParams +from typing import Optional, Set +from typing_extensions import Self + +class ClientMessageOneOf1(BaseModel): + """ + ClientMessageOneOf1 + """ # noqa: E501 + method: StrictStr + params: ClientMessageOneOfParams + __properties: ClassVar[List[str]] = ["method", "params"] + + @field_validator('method') + def method_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['unsubscribe']): + raise ValueError("must be one of enum values ('unsubscribe')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ClientMessageOneOf1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of params + if self.params: + _dict['params'] = self.params.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ClientMessageOneOf1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "method": obj.get("method"), + "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py new file mode 100644 index 0000000000..1fb29ed740 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py @@ -0,0 +1,87 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class ClientMessageOneOfParams(BaseModel): + """ + ClientMessageOneOfParams + """ # noqa: E501 + chain_ids: List[StrictStr] + __properties: ClassVar[List[str]] = ["chain_ids"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ClientMessageOneOfParams from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ClientMessageOneOfParams from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "chain_ids": obj.get("chain_ids") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_request.py b/express_relay/sdk/python/searcher/openapi_client/models/client_request.py new file mode 100644 index 0000000000..be0651f5d6 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_request.py @@ -0,0 +1,94 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import StrictStr +from typing import Any, ClassVar, Dict, List +from openapi_client.models.client_message import ClientMessage +from openapi_client.models.client_message_one_of_params import ClientMessageOneOfParams +from typing import Optional, Set +from typing_extensions import Self + +class ClientRequest(ClientMessage): + """ + ClientRequest + """ # noqa: E501 + id: StrictStr + __properties: ClassVar[List[str]] = ["method", "params", "id"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ClientRequest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of params + if self.params: + _dict['params'] = self.params.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ClientRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "method": obj.get("method"), + "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None, + "id": obj.get("id") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py b/express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py new file mode 100644 index 0000000000..3924355890 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py @@ -0,0 +1,87 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class ErrorBodyResponse(BaseModel): + """ + ErrorBodyResponse + """ # noqa: E501 + error: StrictStr + __properties: ClassVar[List[str]] = ["error"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ErrorBodyResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ErrorBodyResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "error": obj.get("error") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py new file mode 100644 index 0000000000..e8acebcf0b --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py @@ -0,0 +1,95 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, Field, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class OpportunityBid(BaseModel): + """ + OpportunityBid + """ # noqa: E501 + amount: StrictStr = Field(description="The bid amount in wei.") + liquidator: StrictStr = Field(description="Liquidator address") + permission_key: StrictStr = Field(description="The opportunity permission key") + signature: StrictStr + valid_until: StrictStr = Field(description="How long the bid will be valid for.") + __properties: ClassVar[List[str]] = ["amount", "liquidator", "permission_key", "signature", "valid_until"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of OpportunityBid from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of OpportunityBid from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "amount": obj.get("amount"), + "liquidator": obj.get("liquidator"), + "permission_key": obj.get("permission_key"), + "signature": obj.get("signature"), + "valid_until": obj.get("valid_until") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py new file mode 100644 index 0000000000..43b38493d9 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py @@ -0,0 +1,126 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.opportunity_params_one_of import OpportunityParamsOneOf +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +OPPORTUNITYPARAMS_ONE_OF_SCHEMAS = ["OpportunityParamsOneOf"] + +class OpportunityParams(BaseModel): + """ + OpportunityParams + """ + # data type: OpportunityParamsOneOf + oneof_schema_1_validator: Optional[OpportunityParamsOneOf] = None + actual_instance: Optional[Union[OpportunityParamsOneOf]] = None + one_of_schemas: List[str] = Field(default=Literal["OpportunityParamsOneOf"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + discriminator_value_class_map: Dict[str, str] = { + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = OpportunityParams.model_construct() + error_messages = [] + match = 0 + # validate data type: OpportunityParamsOneOf + if not isinstance(v, OpportunityParamsOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `OpportunityParamsOneOf`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in OpportunityParams with oneOf schemas: OpportunityParamsOneOf. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in OpportunityParams with oneOf schemas: OpportunityParamsOneOf. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into OpportunityParamsOneOf + try: + instance.actual_instance = OpportunityParamsOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into OpportunityParams with oneOf schemas: OpportunityParamsOneOf. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into OpportunityParams with oneOf schemas: OpportunityParamsOneOf. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], OpportunityParamsOneOf]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py new file mode 100644 index 0000000000..5cf761f268 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py @@ -0,0 +1,123 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, Field, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from openapi_client.models.token_qty import TokenQty +from typing import Optional, Set +from typing_extensions import Self + +class OpportunityParamsOneOf(BaseModel): + """ + OpportunityParamsOneOf + """ # noqa: E501 + calldata: StrictStr = Field(description="Calldata for the contract call.") + chain_id: StrictStr = Field(description="The chain id where the liquidation will be executed.") + contract: StrictStr = Field(description="The contract address to call for execution of the liquidation.") + permission_key: StrictStr = Field(description="The permission key required for succesful execution of the liquidation.") + receipt_tokens: List[TokenQty] + repay_tokens: List[TokenQty] + value: StrictStr = Field(description="The value to send with the contract call.") + version: StrictStr + __properties: ClassVar[List[str]] = ["calldata", "chain_id", "contract", "permission_key", "receipt_tokens", "repay_tokens", "value", "version"] + + @field_validator('version') + def version_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['v1']): + raise ValueError("must be one of enum values ('v1')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of OpportunityParamsOneOf from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in receipt_tokens (list) + _items = [] + if self.receipt_tokens: + for _item in self.receipt_tokens: + if _item: + _items.append(_item.to_dict()) + _dict['receipt_tokens'] = _items + # override the default output from pydantic by calling `to_dict()` of each item in repay_tokens (list) + _items = [] + if self.repay_tokens: + for _item in self.repay_tokens: + if _item: + _items.append(_item.to_dict()) + _dict['repay_tokens'] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of OpportunityParamsOneOf from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "calldata": obj.get("calldata"), + "chain_id": obj.get("chain_id"), + "contract": obj.get("contract"), + "permission_key": obj.get("permission_key"), + "receipt_tokens": [TokenQty.from_dict(_item) for _item in obj["receipt_tokens"]] if obj.get("receipt_tokens") is not None else None, + "repay_tokens": [TokenQty.from_dict(_item) for _item in obj["repay_tokens"]] if obj.get("repay_tokens") is not None else None, + "value": obj.get("value"), + "version": obj.get("version") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py new file mode 100644 index 0000000000..8b8887e877 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py @@ -0,0 +1,114 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, Field, StrictStr +from typing import Any, ClassVar, Dict, List +from openapi_client.models.token_qty import TokenQty +from typing import Optional, Set +from typing_extensions import Self + +class OpportunityParamsV1(BaseModel): + """ + Opportunity parameters needed for on-chain execution If a searcher signs the opportunity and have approved enough tokens to liquidation adapter, by calling this contract with the given calldata and structures, they will receive the tokens specified in the receipt_tokens field, and will send the tokens specified in the repay_tokens field. + """ # noqa: E501 + calldata: StrictStr = Field(description="Calldata for the contract call.") + chain_id: StrictStr = Field(description="The chain id where the liquidation will be executed.") + contract: StrictStr = Field(description="The contract address to call for execution of the liquidation.") + permission_key: StrictStr = Field(description="The permission key required for succesful execution of the liquidation.") + receipt_tokens: List[TokenQty] + repay_tokens: List[TokenQty] + value: StrictStr = Field(description="The value to send with the contract call.") + __properties: ClassVar[List[str]] = ["calldata", "chain_id", "contract", "permission_key", "receipt_tokens", "repay_tokens", "value"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of OpportunityParamsV1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in receipt_tokens (list) + _items = [] + if self.receipt_tokens: + for _item in self.receipt_tokens: + if _item: + _items.append(_item.to_dict()) + _dict['receipt_tokens'] = _items + # override the default output from pydantic by calling `to_dict()` of each item in repay_tokens (list) + _items = [] + if self.repay_tokens: + for _item in self.repay_tokens: + if _item: + _items.append(_item.to_dict()) + _dict['repay_tokens'] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of OpportunityParamsV1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "calldata": obj.get("calldata"), + "chain_id": obj.get("chain_id"), + "contract": obj.get("contract"), + "permission_key": obj.get("permission_key"), + "receipt_tokens": [TokenQty.from_dict(_item) for _item in obj["receipt_tokens"]] if obj.get("receipt_tokens") is not None else None, + "repay_tokens": [TokenQty.from_dict(_item) for _item in obj["repay_tokens"]] if obj.get("repay_tokens") is not None else None, + "value": obj.get("value") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py new file mode 100644 index 0000000000..5a8ce7ae09 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py @@ -0,0 +1,127 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, Field, StrictInt, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from openapi_client.models.token_qty import TokenQty +from typing import Optional, Set +from typing_extensions import Self + +class OpportunityParamsWithMetadata(BaseModel): + """ + Similar to OpportunityParams, but with the opportunity id included. + """ # noqa: E501 + calldata: StrictStr = Field(description="Calldata for the contract call.") + chain_id: StrictStr = Field(description="The chain id where the liquidation will be executed.") + contract: StrictStr = Field(description="The contract address to call for execution of the liquidation.") + permission_key: StrictStr = Field(description="The permission key required for succesful execution of the liquidation.") + receipt_tokens: List[TokenQty] + repay_tokens: List[TokenQty] + value: StrictStr = Field(description="The value to send with the contract call.") + version: StrictStr + creation_time: StrictInt = Field(description="Creation time of the opportunity") + opportunity_id: StrictStr = Field(description="The opportunity unique id") + __properties: ClassVar[List[str]] = ["calldata", "chain_id", "contract", "permission_key", "receipt_tokens", "repay_tokens", "value", "version", "creation_time", "opportunity_id"] + + @field_validator('version') + def version_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['v1']): + raise ValueError("must be one of enum values ('v1')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of OpportunityParamsWithMetadata from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in receipt_tokens (list) + _items = [] + if self.receipt_tokens: + for _item in self.receipt_tokens: + if _item: + _items.append(_item.to_dict()) + _dict['receipt_tokens'] = _items + # override the default output from pydantic by calling `to_dict()` of each item in repay_tokens (list) + _items = [] + if self.repay_tokens: + for _item in self.repay_tokens: + if _item: + _items.append(_item.to_dict()) + _dict['repay_tokens'] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of OpportunityParamsWithMetadata from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "calldata": obj.get("calldata"), + "chain_id": obj.get("chain_id"), + "contract": obj.get("contract"), + "permission_key": obj.get("permission_key"), + "receipt_tokens": [TokenQty.from_dict(_item) for _item in obj["receipt_tokens"]] if obj.get("receipt_tokens") is not None else None, + "repay_tokens": [TokenQty.from_dict(_item) for _item in obj["repay_tokens"]] if obj.get("repay_tokens") is not None else None, + "value": obj.get("value"), + "version": obj.get("version"), + "creation_time": obj.get("creation_time"), + "opportunity_id": obj.get("opportunity_id") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py new file mode 100644 index 0000000000..ecc4cd7241 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py @@ -0,0 +1,141 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.server_result_message_one_of import ServerResultMessageOneOf +from openapi_client.models.server_result_message_one_of1 import ServerResultMessageOneOf1 +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +SERVERRESULTMESSAGE_ONE_OF_SCHEMAS = ["ServerResultMessageOneOf", "ServerResultMessageOneOf1"] + +class ServerResultMessage(BaseModel): + """ + ServerResultMessage + """ + # data type: ServerResultMessageOneOf + oneof_schema_1_validator: Optional[ServerResultMessageOneOf] = None + # data type: ServerResultMessageOneOf1 + oneof_schema_2_validator: Optional[ServerResultMessageOneOf1] = None + actual_instance: Optional[Union[ServerResultMessageOneOf, ServerResultMessageOneOf1]] = None + one_of_schemas: List[str] = Field(default=Literal["ServerResultMessageOneOf", "ServerResultMessageOneOf1"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + discriminator_value_class_map: Dict[str, str] = { + 'ServerResultResponse': 'ServerResultResponse' + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = ServerResultMessage.model_construct() + error_messages = [] + match = 0 + # validate data type: ServerResultMessageOneOf + if not isinstance(v, ServerResultMessageOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `ServerResultMessageOneOf`") + else: + match += 1 + # validate data type: ServerResultMessageOneOf1 + if not isinstance(v, ServerResultMessageOneOf1): + error_messages.append(f"Error! Input type `{type(v)}` is not `ServerResultMessageOneOf1`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in ServerResultMessage with oneOf schemas: ServerResultMessageOneOf, ServerResultMessageOneOf1. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in ServerResultMessage with oneOf schemas: ServerResultMessageOneOf, ServerResultMessageOneOf1. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into ServerResultMessageOneOf + try: + instance.actual_instance = ServerResultMessageOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into ServerResultMessageOneOf1 + try: + instance.actual_instance = ServerResultMessageOneOf1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into ServerResultMessage with oneOf schemas: ServerResultMessageOneOf, ServerResultMessageOneOf1. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into ServerResultMessage with oneOf schemas: ServerResultMessageOneOf, ServerResultMessageOneOf1. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], ServerResultMessageOneOf, ServerResultMessageOneOf1]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py new file mode 100644 index 0000000000..b75edf0188 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py @@ -0,0 +1,94 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class ServerResultMessageOneOf(BaseModel): + """ + ServerResultMessageOneOf + """ # noqa: E501 + status: StrictStr + __properties: ClassVar[List[str]] = ["status"] + + @field_validator('status') + def status_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['success']): + raise ValueError("must be one of enum values ('success')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ServerResultMessageOneOf from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ServerResultMessageOneOf from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "status": obj.get("status") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py new file mode 100644 index 0000000000..94ff886045 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py @@ -0,0 +1,96 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class ServerResultMessageOneOf1(BaseModel): + """ + ServerResultMessageOneOf1 + """ # noqa: E501 + result: StrictStr + status: StrictStr + __properties: ClassVar[List[str]] = ["result", "status"] + + @field_validator('status') + def status_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['error']): + raise ValueError("must be one of enum values ('error')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ServerResultMessageOneOf1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ServerResultMessageOneOf1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "result": obj.get("result"), + "status": obj.get("status") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py b/express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py new file mode 100644 index 0000000000..cfb531a50d --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py @@ -0,0 +1,95 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import StrictStr +from typing import Any, ClassVar, Dict, List, Optional +from openapi_client.models.server_result_message import ServerResultMessage +from typing import Optional, Set +from typing_extensions import Self + +class ServerResultResponse(ServerResultMessage): + """ + This enum is used to send the result for a specific client request with the same id id is only None when the client message is invalid + """ # noqa: E501 + id: Optional[StrictStr] = None + __properties: ClassVar[List[str]] = ["status", "result", "id"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ServerResultResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if id (nullable) is None + # and model_fields_set contains the field + if self.id is None and "id" in self.model_fields_set: + _dict['id'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ServerResultResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "status": obj.get("status"), + "result": obj.get("result"), + "id": obj.get("id") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py b/express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py new file mode 100644 index 0000000000..5b91a0b189 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py @@ -0,0 +1,126 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.server_update_response_one_of import ServerUpdateResponseOneOf +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +SERVERUPDATERESPONSE_ONE_OF_SCHEMAS = ["ServerUpdateResponseOneOf"] + +class ServerUpdateResponse(BaseModel): + """ + This enum is used to send an update to the client for any subscriptions made + """ + # data type: ServerUpdateResponseOneOf + oneof_schema_1_validator: Optional[ServerUpdateResponseOneOf] = None + actual_instance: Optional[Union[ServerUpdateResponseOneOf]] = None + one_of_schemas: List[str] = Field(default=Literal["ServerUpdateResponseOneOf"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + discriminator_value_class_map: Dict[str, str] = { + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = ServerUpdateResponse.model_construct() + error_messages = [] + match = 0 + # validate data type: ServerUpdateResponseOneOf + if not isinstance(v, ServerUpdateResponseOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `ServerUpdateResponseOneOf`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into ServerUpdateResponseOneOf + try: + instance.actual_instance = ServerUpdateResponseOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py b/express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py new file mode 100644 index 0000000000..8b30652b65 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py @@ -0,0 +1,100 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata +from typing import Optional, Set +from typing_extensions import Self + +class ServerUpdateResponseOneOf(BaseModel): + """ + ServerUpdateResponseOneOf + """ # noqa: E501 + opportunity: OpportunityParamsWithMetadata + type: StrictStr + __properties: ClassVar[List[str]] = ["opportunity", "type"] + + @field_validator('type') + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['new_opportunity']): + raise ValueError("must be one of enum values ('new_opportunity')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ServerUpdateResponseOneOf from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of opportunity + if self.opportunity: + _dict['opportunity'] = self.opportunity.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ServerUpdateResponseOneOf from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "opportunity": OpportunityParamsWithMetadata.from_dict(obj["opportunity"]) if obj.get("opportunity") is not None else None, + "type": obj.get("type") + }) + return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/token_qty.py b/express_relay/sdk/python/searcher/openapi_client/models/token_qty.py new file mode 100644 index 0000000000..af736ecb53 --- /dev/null +++ b/express_relay/sdk/python/searcher/openapi_client/models/token_qty.py @@ -0,0 +1,89 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.5 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, Field, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class TokenQty(BaseModel): + """ + TokenQty + """ # noqa: E501 + amount: StrictStr = Field(description="Token amount") + contract: StrictStr = Field(description="Token contract address") + __properties: ClassVar[List[str]] = ["amount", "contract"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of TokenQty from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of TokenQty from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "amount": obj.get("amount"), + "contract": obj.get("contract") + }) + return _obj + + From 7ad0165840c53c3953f87542229e38cbfa75913d Mon Sep 17 00:00:00 2001 From: ani Date: Tue, 27 Feb 2024 17:54:35 -0700 Subject: [PATCH 11/58] fixed precommit issues on generated type files --- express_relay/sdk/python/searcher/openapi_client/models/bid.py | 2 -- .../python/searcher/openapi_client/models/bid400_response.py | 2 -- .../sdk/python/searcher/openapi_client/models/bid_result.py | 2 -- .../sdk/python/searcher/openapi_client/models/client_message.py | 2 -- .../searcher/openapi_client/models/client_message_one_of.py | 2 -- .../searcher/openapi_client/models/client_message_one_of1.py | 2 -- .../openapi_client/models/client_message_one_of_params.py | 2 -- .../sdk/python/searcher/openapi_client/models/client_request.py | 2 -- .../searcher/openapi_client/models/error_body_response.py | 2 -- .../python/searcher/openapi_client/models/opportunity_bid.py | 2 -- .../python/searcher/openapi_client/models/opportunity_params.py | 2 -- .../searcher/openapi_client/models/opportunity_params_one_of.py | 2 -- .../searcher/openapi_client/models/opportunity_params_v1.py | 2 -- .../openapi_client/models/opportunity_params_with_metadata.py | 2 -- .../searcher/openapi_client/models/server_result_message.py | 2 -- .../openapi_client/models/server_result_message_one_of.py | 2 -- .../openapi_client/models/server_result_message_one_of1.py | 2 -- .../searcher/openapi_client/models/server_result_response.py | 2 -- .../searcher/openapi_client/models/server_update_response.py | 2 -- .../openapi_client/models/server_update_response_one_of.py | 2 -- .../sdk/python/searcher/openapi_client/models/token_qty.py | 2 -- 21 files changed, 42 deletions(-) diff --git a/express_relay/sdk/python/searcher/openapi_client/models/bid.py b/express_relay/sdk/python/searcher/openapi_client/models/bid.py index c553992250..56be015658 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/bid.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/bid.py @@ -91,5 +91,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "permission_key": obj.get("permission_key") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py b/express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py index d3a315612f..8777cdcdd7 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/bid_result.py b/express_relay/sdk/python/searcher/openapi_client/models/bid_result.py index add5fcc5dc..4e69eba6ed 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/bid_result.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/bid_result.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message.py b/express_relay/sdk/python/searcher/openapi_client/models/client_message.py index edfa13d190..997d3275aa 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_message.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_message.py @@ -137,5 +137,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMe def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py index 90c209c48c..3b1dc79308 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py index c81be44468..2fb72ca279 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py index 1fb29ed740..1676ed421e 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "chain_ids": obj.get("chain_ids") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_request.py b/express_relay/sdk/python/searcher/openapi_client/models/client_request.py index be0651f5d6..119ea52fcf 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_request.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/client_request.py @@ -90,5 +90,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py b/express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py index 3924355890..4804dcd884 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py index e8acebcf0b..d73b3f907a 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py @@ -91,5 +91,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "valid_until": obj.get("valid_until") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py index 43b38493d9..ce12170d5c 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py @@ -122,5 +122,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], OpportunityParamsOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py index 5cf761f268..2d09d3b3aa 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py @@ -119,5 +119,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "version": obj.get("version") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py index 8b8887e877..db9db72476 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py @@ -110,5 +110,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "value": obj.get("value") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py index 5a8ce7ae09..a2cb12e7ab 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py @@ -123,5 +123,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "opportunity_id": obj.get("opportunity_id") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py index ecc4cd7241..63dcfc5369 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py @@ -137,5 +137,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerResultMessageOneOf, Se def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py index b75edf0188..2f279fd945 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py @@ -90,5 +90,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py index 94ff886045..dd60aff854 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py @@ -92,5 +92,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py b/express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py index cfb531a50d..9cd548d03b 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py @@ -91,5 +91,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py b/express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py index 5b91a0b189..23ad99afce 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py @@ -122,5 +122,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py b/express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py index 8b30652b65..87e0ef5397 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "type": obj.get("type") }) return _obj - - diff --git a/express_relay/sdk/python/searcher/openapi_client/models/token_qty.py b/express_relay/sdk/python/searcher/openapi_client/models/token_qty.py index af736ecb53..1356639fc7 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/token_qty.py +++ b/express_relay/sdk/python/searcher/openapi_client/models/token_qty.py @@ -85,5 +85,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "contract": obj.get("contract") }) return _obj - - From 069398f491e44c57941d1e02bc19d22bf2f394bd Mon Sep 17 00:00:00 2001 From: ani Date: Mon, 4 Mar 2024 09:30:14 -0500 Subject: [PATCH 12/58] reorg --- .../sdk/js/{searcher => }/.eslintrc.js | 0 .../sdk/js/{searcher => }/.gitignore | 0 express_relay/sdk/js/{searcher => }/README.md | 0 express_relay/sdk/js/package-lock.json | 5667 +++++++++++++++++ .../sdk/js/{searcher => }/package.json | 6 +- .../src/examples/SimpleSearcher.ts | 0 .../sdk/js/{searcher => }/src/index.ts | 0 .../sdk/js/{searcher => }/src/types.d.ts | 0 .../sdk/js/{searcher => }/tsconfig.json | 0 .../sdk/python/{searcher => }/README.md | 0 .../sdk/python/express_relay_client.py | 251 + .../openapi_client/models/bid.py | 2 + .../openapi_client/models/bid400_response.py | 2 + .../openapi_client/models/bid_result.py | 2 + .../openapi_client/models/client_message.py | 2 + .../models/client_message_one_of.py | 2 + .../models/client_message_one_of1.py | 2 + .../models/client_message_one_of_params.py | 2 + .../openapi_client/models/client_request.py | 2 + .../models/error_body_response.py | 2 + .../openapi_client/models/opportunity_bid.py | 2 + .../models/opportunity_params.py | 2 + .../models/opportunity_params_one_of.py | 2 + .../models/opportunity_params_v1.py | 2 + .../opportunity_params_with_metadata.py | 2 + .../models/server_result_message.py | 2 + .../models/server_result_message_one_of.py | 2 + .../models/server_result_message_one_of1.py | 2 + .../models/server_result_response.py | 2 + .../models/server_update_response.py | 2 + .../models/server_update_response_one_of.py | 2 + .../openapi_client/models/token_qty.py | 2 + .../sdk/python/{searcher => }/pyproject.toml | 4 +- .../searcher/examples/simple_searcher.py | 29 +- .../sdk/python/searcher/searcher_utils.py | 194 - 35 files changed, 5983 insertions(+), 210 deletions(-) rename express_relay/sdk/js/{searcher => }/.eslintrc.js (100%) rename express_relay/sdk/js/{searcher => }/.gitignore (100%) rename express_relay/sdk/js/{searcher => }/README.md (100%) create mode 100644 express_relay/sdk/js/package-lock.json rename express_relay/sdk/js/{searcher => }/package.json (95%) rename express_relay/sdk/js/{searcher => }/src/examples/SimpleSearcher.ts (100%) rename express_relay/sdk/js/{searcher => }/src/index.ts (100%) rename express_relay/sdk/js/{searcher => }/src/types.d.ts (100%) rename express_relay/sdk/js/{searcher => }/tsconfig.json (100%) rename express_relay/sdk/python/{searcher => }/README.md (100%) create mode 100644 express_relay/sdk/python/express_relay_client.py rename express_relay/sdk/python/{searcher => }/openapi_client/models/bid.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/bid400_response.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/bid_result.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/client_message.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/client_message_one_of.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/client_message_one_of1.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/client_message_one_of_params.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/client_request.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/error_body_response.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/opportunity_bid.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/opportunity_params.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/opportunity_params_one_of.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/opportunity_params_v1.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/opportunity_params_with_metadata.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/server_result_message.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/server_result_message_one_of.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/server_result_message_one_of1.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/server_result_response.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/server_update_response.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/server_update_response_one_of.py (99%) rename express_relay/sdk/python/{searcher => }/openapi_client/models/token_qty.py (99%) rename express_relay/sdk/python/{searcher => }/pyproject.toml (70%) delete mode 100644 express_relay/sdk/python/searcher/searcher_utils.py diff --git a/express_relay/sdk/js/searcher/.eslintrc.js b/express_relay/sdk/js/.eslintrc.js similarity index 100% rename from express_relay/sdk/js/searcher/.eslintrc.js rename to express_relay/sdk/js/.eslintrc.js diff --git a/express_relay/sdk/js/searcher/.gitignore b/express_relay/sdk/js/.gitignore similarity index 100% rename from express_relay/sdk/js/searcher/.gitignore rename to express_relay/sdk/js/.gitignore diff --git a/express_relay/sdk/js/searcher/README.md b/express_relay/sdk/js/README.md similarity index 100% rename from express_relay/sdk/js/searcher/README.md rename to express_relay/sdk/js/README.md diff --git a/express_relay/sdk/js/package-lock.json b/express_relay/sdk/js/package-lock.json new file mode 100644 index 0000000000..03146c5cfe --- /dev/null +++ b/express_relay/sdk/js/package-lock.json @@ -0,0 +1,5667 @@ +{ + "name": "@pythnetwork/express-relay-evm-js", + "version": "0.1.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@pythnetwork/express-relay-evm-js", + "version": "0.1.1", + "license": "Apache-2.0", + "dependencies": { + "isomorphic-ws": "^5.0.0", + "openapi-client-axios": "^7.5.4", + "openapi-fetch": "^0.8.2", + "openapi-typescript": "^6.5.5", + "viem": "^2.7.6", + "ws": "^8.16.0" + }, + "devDependencies": { + "@types/yargs": "^17.0.10", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", + "eslint": "^8.56.0", + "jest": "^27.5.1", + "prettier": "^2.6.2", + "typescript": "^5.3.3", + "yargs": "^17.4.1" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", + "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", + "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", + "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@scure/base": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", + "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", + "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", + "dependencies": { + "@noble/curves": "~1.2.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", + "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", + "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, + "node_modules/abitype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.0.tgz", + "integrity": "sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ==", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "peer": true, + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dev": true, + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bath-es5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bath-es5/-/bath-es5-3.0.3.tgz", + "integrity": "sha512-PdCioDToH3t84lP40kUFCKWCOCH389Dl1kbC8FGoqOwamxsmqxxnJSXdkTOsPoNHXjem4+sJ+bbNoQm5zeCqxg==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001589", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz", + "integrity": "sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dereference-json-schema": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/dereference-json-schema/-/dereference-json-schema-0.2.1.tgz", + "integrity": "sha512-uzJsrg225owJyRQ8FNTPHIuBOdSzIZlHhss9u6W8mp7jJldHqGuLv9cULagP/E26QVJDnjtG8U7Dw139mM1ydA==" + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.679", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.679.tgz", + "integrity": "sha512-NhQMsz5k0d6m9z3qAxnsOR/ebal4NAGsrNVRwcDo4Kc/zQ7KdsTKZUxZoygHcVRb0QDW3waEDIcE3isZ79RP6g==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "peer": true, + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/isows": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.3.tgz", + "integrity": "sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-cli/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dev": true, + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-client-axios": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/openapi-client-axios/-/openapi-client-axios-7.5.4.tgz", + "integrity": "sha512-ZVzGRobfNjV1EWVlQ8R1ycpx+36bn6uAxrQotII6gy9fFmEoYgkMBM2qlrbfa3JgPO8G3A2bLF4dV7oyCJK2Pg==", + "dependencies": { + "bath-es5": "^3.0.3", + "dereference-json-schema": "^0.2.1", + "openapi-types": "^12.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/anttiviljami" + }, + "peerDependencies": { + "axios": ">=0.25.0", + "js-yaml": "^4.1.0" + } + }, + "node_modules/openapi-fetch": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.8.2.tgz", + "integrity": "sha512-4g+NLK8FmQ51RW6zLcCBOVy/lwYmFJiiT+ckYZxJWxUxH4XFhsNcX2eeqVMfVOi+mDNFja6qDXIZAz2c5J/RVw==", + "dependencies": { + "openapi-typescript-helpers": "^0.0.5" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" + }, + "node_modules/openapi-typescript": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-6.5.5.tgz", + "integrity": "sha512-pMsA8GrMQKtNOPPjKnJbDotA2UpKsIcTHecMw2Bl3M/2eWTVs8zAYBm/cgaE9Qz5GrcVCDIru9GQX/P9vxtUFg==", + "dependencies": { + "ansi-colors": "^4.1.3", + "fast-glob": "^3.3.1", + "js-yaml": "^4.1.0", + "supports-color": "^9.4.0", + "undici": "^5.23.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + } + }, + "node_modules/openapi-typescript-helpers": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.5.tgz", + "integrity": "sha512-MRffg93t0hgGZbYTxg60hkRIK2sRuEOHEtCUgMuLgbCC33TMQ68AmxskzUlauzZYD47+ENeGV/ElI7qnWqrAxA==" + }, + "node_modules/openapi-typescript/node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "peer": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "5.28.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", + "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/viem": { + "version": "2.7.12", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.7.12.tgz", + "integrity": "sha512-NbV+Bycw0I4X8y6A04mgJ6+Imt7xXwflgnqisR3JXoJRNc77YSaQCscFN/dmwGLESTkgegJvi+j4nZY32GTpwQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@scure/bip32": "1.3.2", + "@scure/bip39": "1.2.1", + "abitype": "1.0.0", + "isows": "1.0.3", + "ws": "8.13.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/express_relay/sdk/js/searcher/package.json b/express_relay/sdk/js/package.json similarity index 95% rename from express_relay/sdk/js/searcher/package.json rename to express_relay/sdk/js/package.json index 125c07f20d..4220bbe3c2 100644 --- a/express_relay/sdk/js/searcher/package.json +++ b/express_relay/sdk/js/package.json @@ -33,12 +33,13 @@ "repository": { "type": "git", "url": "https://github.com/pyth-network/pyth-crosschain", - "directory": "express_relay/sdk/js" + "directory": "express_relay/sdk/js/searcher" }, "dependencies": { "isomorphic-ws": "^5.0.0", "openapi-client-axios": "^7.5.4", "openapi-fetch": "^0.8.2", + "openapi-typescript": "^6.5.5", "viem": "^2.7.6", "ws": "^8.16.0" }, @@ -48,9 +49,8 @@ "@typescript-eslint/parser": "^5.21.0", "eslint": "^8.56.0", "jest": "^27.5.1", - "openapi-typescript": "^6.5.5", "prettier": "^2.6.2", - "typescript": "^5.1", + "typescript": "^5.3.3", "yargs": "^17.4.1" }, "license": "Apache-2.0" diff --git a/express_relay/sdk/js/searcher/src/examples/SimpleSearcher.ts b/express_relay/sdk/js/src/examples/SimpleSearcher.ts similarity index 100% rename from express_relay/sdk/js/searcher/src/examples/SimpleSearcher.ts rename to express_relay/sdk/js/src/examples/SimpleSearcher.ts diff --git a/express_relay/sdk/js/searcher/src/index.ts b/express_relay/sdk/js/src/index.ts similarity index 100% rename from express_relay/sdk/js/searcher/src/index.ts rename to express_relay/sdk/js/src/index.ts diff --git a/express_relay/sdk/js/searcher/src/types.d.ts b/express_relay/sdk/js/src/types.d.ts similarity index 100% rename from express_relay/sdk/js/searcher/src/types.d.ts rename to express_relay/sdk/js/src/types.d.ts diff --git a/express_relay/sdk/js/searcher/tsconfig.json b/express_relay/sdk/js/tsconfig.json similarity index 100% rename from express_relay/sdk/js/searcher/tsconfig.json rename to express_relay/sdk/js/tsconfig.json diff --git a/express_relay/sdk/python/searcher/README.md b/express_relay/sdk/python/README.md similarity index 100% rename from express_relay/sdk/python/searcher/README.md rename to express_relay/sdk/python/README.md diff --git a/express_relay/sdk/python/express_relay_client.py b/express_relay/sdk/python/express_relay_client.py new file mode 100644 index 0000000000..6248e473d2 --- /dev/null +++ b/express_relay/sdk/python/express_relay_client.py @@ -0,0 +1,251 @@ +import web3 +from eth_abi import encode +from eth_account.account import Account +from web3.auto import w3 +import httpx +import urllib.parse +import websockets +import json +from typing import Callable +import asyncio + +from openapi_client.models.opportunity_bid import OpportunityBid +from openapi_client.models.opportunity_params import OpportunityParams +from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata + +class BidInfo: + def __init__(self, opportunity_id: str, opportunity_bid: OpportunityBid): + self.opportunity_id = opportunity_id + self.opportunity_bid = opportunity_bid + +class WebsocketTimeoutConfig: + """ + A class to hold the timeout configuration for the websocket connection. + + Args: + open_timeout (int): The timeout for opening the websocket connection. + ping_interval (int): The interval at which to send ping messages to the server. + ping_timeout (int): The timeout for the ping messages. + close_timeout (int): The timeout for closing the websocket connection. + """ + def __init__(self, open_timeout: int = 10, ping_interval: int = 20, ping_timeout: int = 20, close_timeout: int = 10): + self.open_timeout = open_timeout + self.ping_interval = ping_interval + self.ping_timeout = ping_timeout + self.close_timeout = close_timeout + +class ExpressRelayClientException(Exception): + pass + +class ExpressRelayClient: + def __init__(self, server_url: str): + self.server_url = server_url + if self.server_url.startswith("https"): + self.ws_endpoint = urllib.parse.urljoin(f"wss{self.server_url[5:]}", "v1/ws") + elif self.server_url.startswith("http"): + self.ws_endpoint = urllib.parse.urljoin(f"ws{self.server_url[4:]}", "v1/ws") + else: + raise ValueError("Invalid liquidation server URL") + self.ws_msg_counter = 0 + self.ws = False + + async def start_ws(self, ws_timeout_config: WebsocketTimeoutConfig = WebsocketTimeoutConfig()): + """ + Initializes the websocket connection to the server. + + Args: + ws_timeout_config (WebsocketTimeoutConfig): The timeout configuration for the websocket connection. + """ + self.ws = await websockets.connect(self.ws_endpoint, open_timeout=ws_timeout_config.open_timeout, ping_interval=ws_timeout_config.ping_interval, ping_timeout=ws_timeout_config.ping_timeout, close_timeout=ws_timeout_config.close_timeout) + + async def get_liquidation_opportunities(self, chain_id: str, timeout: int = 10) -> list[OpportunityParamsWithMetadata]: + """ + Connects to the liquidation server and fetches liquidation opportunities for a given chain ID. + + Args: + chain_id (str): The chain ID to fetch liquidation opportunities for. + timeout (int): The timeout for the HTTP request. + Returns: + list[OpportunityParamsWithMetadata]: A list of liquidation opportunities. + """ + async with httpx.AsyncClient() as client: + resp = ( + await client.get( + urllib.parse.urljoin( + self.server_url, "/v1/liquidation/opportunities" + ), + params={"chain_id": chain_id}, + timeout=timeout, + ) + ) + + resp.raise_for_status() + + opportunities = [OpportunityParamsWithMetadata.from_dict(opportunity) for opportunity in resp.json()] + + return opportunities + + async def subscribe_chain_ids(self, chain_ids: list[str]): + """ + Subscribes to a list of chain IDs for new liquidation opportunities. + + Args: + chain_ids (list[str]): A list of chain IDs to subscribe to. + """ + if not self.ws: + raise ExpressRelayClientException("Websocket connection not yet open") + + json_subscribe = { + "method": "subscribe", + "params": { + "chain_ids": chain_ids, + }, + "id": str(self.ws_msg_counter), + } + await self.ws.send(json.dumps(json_subscribe)) + self.ws_msg_counter += 1 + + async def unsubscribe_chain_ids(self, chain_ids: str): + if not self.ws: + raise ExpressRelayClientException("Websocket connection not yet open") + + json_subscribe = { + "method": "unsubscribe", + "params": { + "chain_ids": chain_ids, + }, + "id": str(self.ws_msg_counter), + } + await self.ws.send(json.dumps(json_subscribe)) + self.ws_msg_counter += 1 + + async def ws_liquidation_opportunities(self, opportunity_handler: Callable[[OpportunityParamsWithMetadata], None]): + """ + Connects to the liquidation server's websocket and handles new liquidation opportunities. + + Args: + opportunity_handler (async func): An async function that defines how to handle new liquidation opportunities. Should take in one external argument of type OpportunityParamsWithMetadata and return nothing. + """ + if not self.ws: + raise ExpressRelayClientException("Websocket connection not yet open") + + while True: + msg = json.loads(await self.ws.recv()) + status = msg.get("status") + if status and status != "success": + raise ExpressRelayClientException(f"Error in websocket subscription: {msg.get('result')}") + + if msg.get("type") != "new_opportunity": + continue + + opportunity = msg["opportunity"] + opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) + try: + await opportunity_handler(opportunity) + except Exception as e: + raise ExpressRelayClientException(f"Error in opportunity handler: {e}") + + async def submit_opportunity(self, opportunity: OpportunityParams, timeout: int = 10) -> httpx.Response: + """ + Submits an opportunity to the liquidation server. + + Args: + opportunity (OpportunityParams): An object representing the opportunity to submit. + timeout (int): The timeout for the HTTP request. + Returns: + httpx.Response: The server's response to the opportunity submission. Throws an exception if the response is not successful. + """ + async with httpx.AsyncClient() as client: + resp = await client.post( + urllib.parse.urljoin(self.server_url, "/v1/liquidation/opportunities"), + json=opportunity.to_dict(), + timeout=timeout, + ) + + resp.raise_for_status() + + return resp + + async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> httpx.Response: + """ + Submits a bid to the liquidation server. + + Args: + bid_info (BidInfo): An object representing the bid to submit. + timeout (int): The timeout for the HTTP request. + Returns: + httpx.Response: The server's response to the bid submission. Throws an exception if the response is not successful. + """ + async with httpx.AsyncClient() as client: + resp = await client.post( + urllib.parse.urljoin( + self.server_url, + f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids", + ), + json=bid_info.opportunity_bid.to_dict(), + timeout=timeout, + ) + + resp.raise_for_status() + + return resp + +def sign_bid( + liquidation_opportunity: OpportunityParamsWithMetadata, + bid: int, + valid_until: int, + private_key: str, +) -> BidInfo: + """ + Constructs a signature for a liquidator's bid and returns the BidInfo object to be submitted to the liquidation server. + + Args: + liquidation_opportunity: An object representing the liquidation opportunity, of type OpportunityParamsWithMetadata. + bid: An integer representing the amount of the bid. + valid_until: An integer representing the block until which the bid is valid. + private_key: A string representing the liquidator's private key. + Returns: + A BidInfo object, representing the transaction to submit to the liquidation server. This object contains the liquidator's signature. + """ + repay_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.repay_tokens] + receipt_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.receipt_tokens] + calldata = bytes.fromhex(liquidation_opportunity.calldata.replace("0x", "")) + + digest = encode( + [ + "(address,uint256)[]", + "(address,uint256)[]", + "address", + "bytes", + "uint256", + "uint256", + "uint256", + ], + [ + repay_tokens, + receipt_tokens, + liquidation_opportunity.contract, + calldata, + int(liquidation_opportunity.value), + bid, + valid_until, + ], + ) + msg_data = web3.Web3.solidity_keccak(["bytes"], [digest]) + signature = w3.eth.account.signHash(msg_data, private_key=private_key) + + opportunity_bid = { + "permission_key": liquidation_opportunity.permission_key, + "amount": str(bid), + "valid_until": str(valid_until), + "liquidator": Account.from_key(private_key).address, + "signature": bytes(signature.signature).hex(), + } + opportunity_bid = OpportunityBid.from_dict(opportunity_bid) + + bid_info = BidInfo( + opportunity_id=liquidation_opportunity.opportunity_id, + opportunity_bid=opportunity_bid, + ) + + return bid_info diff --git a/express_relay/sdk/python/searcher/openapi_client/models/bid.py b/express_relay/sdk/python/openapi_client/models/bid.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/bid.py rename to express_relay/sdk/python/openapi_client/models/bid.py index 56be015658..c553992250 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/bid.py +++ b/express_relay/sdk/python/openapi_client/models/bid.py @@ -91,3 +91,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "permission_key": obj.get("permission_key") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py b/express_relay/sdk/python/openapi_client/models/bid400_response.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py rename to express_relay/sdk/python/openapi_client/models/bid400_response.py index 8777cdcdd7..d3a315612f 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/bid400_response.py +++ b/express_relay/sdk/python/openapi_client/models/bid400_response.py @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/bid_result.py b/express_relay/sdk/python/openapi_client/models/bid_result.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/bid_result.py rename to express_relay/sdk/python/openapi_client/models/bid_result.py index 4e69eba6ed..add5fcc5dc 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/bid_result.py +++ b/express_relay/sdk/python/openapi_client/models/bid_result.py @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message.py b/express_relay/sdk/python/openapi_client/models/client_message.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/client_message.py rename to express_relay/sdk/python/openapi_client/models/client_message.py index 997d3275aa..edfa13d190 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_message.py +++ b/express_relay/sdk/python/openapi_client/models/client_message.py @@ -137,3 +137,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMe def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py b/express_relay/sdk/python/openapi_client/models/client_message_one_of.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py rename to express_relay/sdk/python/openapi_client/models/client_message_one_of.py index 3b1dc79308..90c209c48c 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of.py +++ b/express_relay/sdk/python/openapi_client/models/client_message_one_of.py @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py b/express_relay/sdk/python/openapi_client/models/client_message_one_of1.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py rename to express_relay/sdk/python/openapi_client/models/client_message_one_of1.py index 2fb72ca279..c81be44468 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of1.py +++ b/express_relay/sdk/python/openapi_client/models/client_message_one_of1.py @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py b/express_relay/sdk/python/openapi_client/models/client_message_one_of_params.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py rename to express_relay/sdk/python/openapi_client/models/client_message_one_of_params.py index 1676ed421e..1fb29ed740 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_message_one_of_params.py +++ b/express_relay/sdk/python/openapi_client/models/client_message_one_of_params.py @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "chain_ids": obj.get("chain_ids") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/client_request.py b/express_relay/sdk/python/openapi_client/models/client_request.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/client_request.py rename to express_relay/sdk/python/openapi_client/models/client_request.py index 119ea52fcf..be0651f5d6 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/client_request.py +++ b/express_relay/sdk/python/openapi_client/models/client_request.py @@ -90,3 +90,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py b/express_relay/sdk/python/openapi_client/models/error_body_response.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py rename to express_relay/sdk/python/openapi_client/models/error_body_response.py index 4804dcd884..3924355890 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/error_body_response.py +++ b/express_relay/sdk/python/openapi_client/models/error_body_response.py @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py b/express_relay/sdk/python/openapi_client/models/opportunity_bid.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py rename to express_relay/sdk/python/openapi_client/models/opportunity_bid.py index d73b3f907a..e8acebcf0b 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_bid.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_bid.py @@ -91,3 +91,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "valid_until": obj.get("valid_until") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py b/express_relay/sdk/python/openapi_client/models/opportunity_params.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py rename to express_relay/sdk/python/openapi_client/models/opportunity_params.py index ce12170d5c..43b38493d9 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_params.py @@ -122,3 +122,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], OpportunityParamsOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py b/express_relay/sdk/python/openapi_client/models/opportunity_params_one_of.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py rename to express_relay/sdk/python/openapi_client/models/opportunity_params_one_of.py index 2d09d3b3aa..5cf761f268 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_one_of.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_params_one_of.py @@ -119,3 +119,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "version": obj.get("version") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py b/express_relay/sdk/python/openapi_client/models/opportunity_params_v1.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py rename to express_relay/sdk/python/openapi_client/models/opportunity_params_v1.py index db9db72476..8b8887e877 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_v1.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_params_v1.py @@ -110,3 +110,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "value": obj.get("value") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py b/express_relay/sdk/python/openapi_client/models/opportunity_params_with_metadata.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py rename to express_relay/sdk/python/openapi_client/models/opportunity_params_with_metadata.py index a2cb12e7ab..5a8ce7ae09 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/opportunity_params_with_metadata.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_params_with_metadata.py @@ -123,3 +123,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "opportunity_id": obj.get("opportunity_id") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py b/express_relay/sdk/python/openapi_client/models/server_result_message.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py rename to express_relay/sdk/python/openapi_client/models/server_result_message.py index 63dcfc5369..ecc4cd7241 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message.py +++ b/express_relay/sdk/python/openapi_client/models/server_result_message.py @@ -137,3 +137,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerResultMessageOneOf, Se def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py b/express_relay/sdk/python/openapi_client/models/server_result_message_one_of.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py rename to express_relay/sdk/python/openapi_client/models/server_result_message_one_of.py index 2f279fd945..b75edf0188 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of.py +++ b/express_relay/sdk/python/openapi_client/models/server_result_message_one_of.py @@ -90,3 +90,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py b/express_relay/sdk/python/openapi_client/models/server_result_message_one_of1.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py rename to express_relay/sdk/python/openapi_client/models/server_result_message_one_of1.py index dd60aff854..94ff886045 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_result_message_one_of1.py +++ b/express_relay/sdk/python/openapi_client/models/server_result_message_one_of1.py @@ -92,3 +92,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py b/express_relay/sdk/python/openapi_client/models/server_result_response.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py rename to express_relay/sdk/python/openapi_client/models/server_result_response.py index 9cd548d03b..cfb531a50d 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_result_response.py +++ b/express_relay/sdk/python/openapi_client/models/server_result_response.py @@ -91,3 +91,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py b/express_relay/sdk/python/openapi_client/models/server_update_response.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py rename to express_relay/sdk/python/openapi_client/models/server_update_response.py index 23ad99afce..5b91a0b189 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response.py +++ b/express_relay/sdk/python/openapi_client/models/server_update_response.py @@ -122,3 +122,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py b/express_relay/sdk/python/openapi_client/models/server_update_response_one_of.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py rename to express_relay/sdk/python/openapi_client/models/server_update_response_one_of.py index 87e0ef5397..8b30652b65 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/server_update_response_one_of.py +++ b/express_relay/sdk/python/openapi_client/models/server_update_response_one_of.py @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "type": obj.get("type") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/openapi_client/models/token_qty.py b/express_relay/sdk/python/openapi_client/models/token_qty.py similarity index 99% rename from express_relay/sdk/python/searcher/openapi_client/models/token_qty.py rename to express_relay/sdk/python/openapi_client/models/token_qty.py index 1356639fc7..af736ecb53 100644 --- a/express_relay/sdk/python/searcher/openapi_client/models/token_qty.py +++ b/express_relay/sdk/python/openapi_client/models/token_qty.py @@ -85,3 +85,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "contract": obj.get("contract") }) return _obj + + diff --git a/express_relay/sdk/python/searcher/pyproject.toml b/express_relay/sdk/python/pyproject.toml similarity index 70% rename from express_relay/sdk/python/searcher/pyproject.toml rename to express_relay/sdk/python/pyproject.toml index 351be37029..a39f41224d 100644 --- a/express_relay/sdk/python/searcher/pyproject.toml +++ b/express_relay/sdk/python/pyproject.toml @@ -6,7 +6,7 @@ requires = [ build-backend = "setuptools.build_meta" [tool.poetry] -name = "express-relay-searcher-utils" +name = "express-relay-utils" version = 0.0.1 dependencies = [ "web3", @@ -17,6 +17,6 @@ dependencies = [ "asyncio", "argparse" ] -description = "Utilities for searchers to interact with the express relay protocol." +description = "Utilities for searchers and protocols to interact with the express relay protocol." license = "Proprietary" readme = "README.md" diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py index 193c713029..3c7afd4363 100644 --- a/express_relay/sdk/python/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -3,18 +3,17 @@ import logging from eth_account.account import Account -from searcher_utils import BidInfo, SearcherClient +from express_relay_client import BidInfo, ExpressRelayClient, sign_bid -from openapi_client.models.opportunity_bid import OpportunityBid from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata logger = logging.getLogger(__name__) VALID_UNTIL = 1_000_000_000_000 -class SimpleSearcher(SearcherClient): - def __init__(self, liquidation_server_url: str, private_key: str, default_bid: int): - super().__init__(liquidation_server_url) +class SimpleSearcher(): + def __init__(self, server_url: str, private_key: str, default_bid: int): + self.client = ExpressRelayClient(server_url) self.private_key = private_key self.liquidator = Account.from_key(private_key).address self.default_bid = default_bid @@ -34,7 +33,7 @@ def assess_liquidation_opportunity( Returns: If the opportunity is deemed worthwhile, this function can return a BidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. """ - bid_info = self.sign_bid( + bid_info = sign_bid( opp, self.default_bid, VALID_UNTIL, @@ -49,7 +48,7 @@ async def ws_opportunity_handler( bid_info = self.assess_liquidation_opportunity(opp) if bid_info: try: - await self.submit_bid(bid_info) + await self.client.submit_bid(bid_info) logger.info(f"Submitted bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}") except Exception as e: logger.error(f"Error submitting bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}: {e}") @@ -78,10 +77,10 @@ async def main(): help="Default amount of bid for liquidation opportunities", ) parser.add_argument( - "--liquidation-server-url", + "--server-url", type=str, required=True, - help="Liquidation server endpoint to use for fetching opportunities and submitting bids", + help="Server endpoint to use for fetching opportunities and submitting bids", ) args = parser.parse_args() @@ -96,10 +95,18 @@ async def main(): sk_liquidator = args.private_key - simple_searcher = SimpleSearcher(args.liquidation_server_url, sk_liquidator, args.bid) + simple_searcher = SimpleSearcher(args.server_url, sk_liquidator, args.bid) logger.info("Liquidator address: %s", simple_searcher.liquidator) - await simple_searcher.ws_liquidation_opportunities(args.chain_ids, simple_searcher.ws_opportunity_handler) + await simple_searcher.client.start_ws() + + ws_call = simple_searcher.client.ws_liquidation_opportunities(simple_searcher.ws_opportunity_handler) + asyncio.create_task(ws_call) + + await simple_searcher.client.subscribe_chain_ids(args.chain_ids) + + while True: + await asyncio.sleep(5) if __name__ == "__main__": asyncio.run(main()) diff --git a/express_relay/sdk/python/searcher/searcher_utils.py b/express_relay/sdk/python/searcher/searcher_utils.py deleted file mode 100644 index 8702e49a93..0000000000 --- a/express_relay/sdk/python/searcher/searcher_utils.py +++ /dev/null @@ -1,194 +0,0 @@ -import web3 -from eth_abi import encode -from eth_account.account import Account -from web3.auto import w3 -import httpx -import urllib.parse -import websockets -import json -from typing import Callable - -from openapi_client.models.opportunity_bid import OpportunityBid -from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata - -class BidInfo: - def __init__(self, opportunity_id: str, opportunity_bid: OpportunityBid): - self.opportunity_id = opportunity_id - self.opportunity_bid = opportunity_bid - -class WebsocketTimeoutConfig: - """ - A class to hold the timeout configuration for the websocket connection. - - Args: - open_timeout (int): The timeout for opening the websocket connection. - ping_interval (int): The interval at which to send ping messages to the server. - ping_timeout (int): The timeout for the ping messages. - close_timeout (int): The timeout for closing the websocket connection. - """ - def __init__(self, open_timeout: int = 10, ping_interval: int = 20, ping_timeout: int = 20, close_timeout: int = 10): - self.open_timeout = open_timeout - self.ping_interval = ping_interval - self.ping_timeout = ping_timeout - self.close_timeout = close_timeout - -class SearcherClientException(Exception): - pass - -class SearcherClient: - def __init__(self, server_url: str): - self.server_url = server_url - if self.server_url.startswith("https"): - self.ws_endpoint = urllib.parse.urljoin(f"wss{self.server_url[5:]}", "v1/ws") - elif self.server_url.startswith("http"): - self.ws_endpoint = urllib.parse.urljoin(f"ws{self.server_url[4:]}", "v1/ws") - else: - raise ValueError("Invalid liquidation server URL") - self.ws_msg_counter = 0 - - async def get_liquidation_opportunities(self, chain_id: str, timeout: int = 10) -> list[OpportunityParamsWithMetadata]: - """ - Connects to the liquidation server and fetches liquidation opportunities for a given chain ID. - - Args: - chain_id (str): The chain ID to fetch liquidation opportunities for. - timeout (int): The timeout for the HTTP request. - Returns: - list[OpportunityParamsWithMetadata]: A list of liquidation opportunities. - """ - async with httpx.AsyncClient() as client: - resp = ( - await client.get( - urllib.parse.urljoin( - self.server_url, "/v1/liquidation/opportunities" - ), - params={"chain_id": chain_id}, - timeout=timeout, - ) - ) - - resp.raise_for_status() - - opportunities = [OpportunityParamsWithMetadata.from_dict(opportunity) for opportunity in resp.json()] - - return opportunities - - async def ws_liquidation_opportunities(self, chain_ids: list[str], opportunity_handler: Callable[[OpportunityParamsWithMetadata], None], ws_timeout_config: WebsocketTimeoutConfig = WebsocketTimeoutConfig()): - """ - Connects to the liquidation server's websocket and handles new liquidation opportunities. - - Args: - chain_ids (list[str]): A list of chain IDs to subscribe to for liquidation opportunities. - opportunity_handler (async func): An async function that defines how to handle new liquidation opportunities. Should take in one external argument of type OpportunityParamsWithMetadata and return nothing. - """ - async with websockets.connect(self.ws_endpoint, open_timeout=ws_timeout_config.open_timeout, ping_interval=ws_timeout_config.ping_interval, ping_timeout=ws_timeout_config.ping_timeout, close_timeout=ws_timeout_config.close_timeout) as ws: - json_subscribe = { - "method": "subscribe", - "params": { - "chain_ids": chain_ids, - }, - "id": str(self.ws_msg_counter), - } - await ws.send(json.dumps(json_subscribe)) - self.ws_msg_counter += 1 - - while True: - msg = json.loads(await ws.recv()) - status = msg.get("status") - if status and status != "success": - raise SearcherClientException(f"Error in websocket subscription: {msg.get('result')}") - - if msg.get("type") != "new_opportunity": - continue - - opportunity = msg["opportunity"] - opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) - try: - await opportunity_handler(opportunity) - except Exception as e: - raise SearcherClientException(f"Error in opportunity handler: {e}") - - async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> httpx.Response: - """ - Submits a bid to the liquidation server. - - Args: - bid_info (BidInfo): An object representing the bid to submit. - timeout (int): The timeout for the HTTP request. - Returns: - httpx.Response: The server's response to the bid submission. Throws an exception if the response is not successful. - """ - async with httpx.AsyncClient() as client: - resp = await client.post( - urllib.parse.urljoin( - self.server_url, - f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids", - ), - json=bid_info.opportunity_bid.to_dict(), - timeout=timeout, - ) - - resp.raise_for_status() - - return resp - - def sign_bid( - self, - liquidation_opportunity: OpportunityParamsWithMetadata, - bid: int, - valid_until: int, - private_key: str, - ) -> BidInfo: - """ - Constructs a signature for a liquidator's bid and returns the BidInfo object to be submitted to the liquidation server. - - Args: - liquidation_opportunity: An object representing the liquidation opportunity, of type OpportunityParamsWithMetadata. - bid: An integer representing the amount of the bid. - valid_until: An integer representing the block until which the bid is valid. - private_key: A string representing the liquidator's private key. - Returns: - A BidInfo object, representing the transaction to submit to the liquidation server. This object contains the liquidator's signature. - """ - repay_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.repay_tokens] - receipt_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.receipt_tokens] - calldata = bytes.fromhex(liquidation_opportunity.calldata.replace("0x", "")) - - digest = encode( - [ - "(address,uint256)[]", - "(address,uint256)[]", - "address", - "bytes", - "uint256", - "uint256", - "uint256", - ], - [ - repay_tokens, - receipt_tokens, - liquidation_opportunity.contract, - calldata, - int(liquidation_opportunity.value), - bid, - valid_until, - ], - ) - msg_data = web3.Web3.solidity_keccak(["bytes"], [digest]) - signature = w3.eth.account.signHash(msg_data, private_key=private_key) - - opportunity_bid = { - "permission_key": liquidation_opportunity.permission_key, - "amount": str(bid), - "valid_until": str(valid_until), - "liquidator": Account.from_key(private_key).address, - "signature": bytes(signature.signature).hex(), - } - opportunity_bid = OpportunityBid.from_dict(opportunity_bid) - - bid_info = BidInfo( - opportunity_id=liquidation_opportunity.opportunity_id, - opportunity_bid=opportunity_bid, - ) - - return bid_info From e2acc998f9715915a871b6265f6ae0c4bedda337 Mon Sep 17 00:00:00 2001 From: ani Date: Mon, 4 Mar 2024 09:45:43 -0500 Subject: [PATCH 13/58] fixed openapi_client generated precommit --- express_relay/sdk/python/openapi_client/models/bid.py | 2 -- .../sdk/python/openapi_client/models/bid400_response.py | 2 -- express_relay/sdk/python/openapi_client/models/bid_result.py | 2 -- .../sdk/python/openapi_client/models/client_message.py | 2 -- .../sdk/python/openapi_client/models/client_message_one_of.py | 2 -- .../sdk/python/openapi_client/models/client_message_one_of1.py | 2 -- .../openapi_client/models/client_message_one_of_params.py | 2 -- .../sdk/python/openapi_client/models/client_request.py | 2 -- .../sdk/python/openapi_client/models/error_body_response.py | 2 -- .../sdk/python/openapi_client/models/opportunity_bid.py | 2 -- .../sdk/python/openapi_client/models/opportunity_params.py | 2 -- .../python/openapi_client/models/opportunity_params_one_of.py | 2 -- .../sdk/python/openapi_client/models/opportunity_params_v1.py | 2 -- .../openapi_client/models/opportunity_params_with_metadata.py | 2 -- .../sdk/python/openapi_client/models/server_result_message.py | 2 -- .../openapi_client/models/server_result_message_one_of.py | 2 -- .../openapi_client/models/server_result_message_one_of1.py | 2 -- .../sdk/python/openapi_client/models/server_result_response.py | 2 -- .../sdk/python/openapi_client/models/server_update_response.py | 2 -- .../openapi_client/models/server_update_response_one_of.py | 2 -- express_relay/sdk/python/openapi_client/models/token_qty.py | 2 -- 21 files changed, 42 deletions(-) diff --git a/express_relay/sdk/python/openapi_client/models/bid.py b/express_relay/sdk/python/openapi_client/models/bid.py index c553992250..56be015658 100644 --- a/express_relay/sdk/python/openapi_client/models/bid.py +++ b/express_relay/sdk/python/openapi_client/models/bid.py @@ -91,5 +91,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "permission_key": obj.get("permission_key") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/bid400_response.py b/express_relay/sdk/python/openapi_client/models/bid400_response.py index d3a315612f..8777cdcdd7 100644 --- a/express_relay/sdk/python/openapi_client/models/bid400_response.py +++ b/express_relay/sdk/python/openapi_client/models/bid400_response.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/bid_result.py b/express_relay/sdk/python/openapi_client/models/bid_result.py index add5fcc5dc..4e69eba6ed 100644 --- a/express_relay/sdk/python/openapi_client/models/bid_result.py +++ b/express_relay/sdk/python/openapi_client/models/bid_result.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/client_message.py b/express_relay/sdk/python/openapi_client/models/client_message.py index edfa13d190..997d3275aa 100644 --- a/express_relay/sdk/python/openapi_client/models/client_message.py +++ b/express_relay/sdk/python/openapi_client/models/client_message.py @@ -137,5 +137,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMe def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/openapi_client/models/client_message_one_of.py b/express_relay/sdk/python/openapi_client/models/client_message_one_of.py index 90c209c48c..3b1dc79308 100644 --- a/express_relay/sdk/python/openapi_client/models/client_message_one_of.py +++ b/express_relay/sdk/python/openapi_client/models/client_message_one_of.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/client_message_one_of1.py b/express_relay/sdk/python/openapi_client/models/client_message_one_of1.py index c81be44468..2fb72ca279 100644 --- a/express_relay/sdk/python/openapi_client/models/client_message_one_of1.py +++ b/express_relay/sdk/python/openapi_client/models/client_message_one_of1.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/client_message_one_of_params.py b/express_relay/sdk/python/openapi_client/models/client_message_one_of_params.py index 1fb29ed740..1676ed421e 100644 --- a/express_relay/sdk/python/openapi_client/models/client_message_one_of_params.py +++ b/express_relay/sdk/python/openapi_client/models/client_message_one_of_params.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "chain_ids": obj.get("chain_ids") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/client_request.py b/express_relay/sdk/python/openapi_client/models/client_request.py index be0651f5d6..119ea52fcf 100644 --- a/express_relay/sdk/python/openapi_client/models/client_request.py +++ b/express_relay/sdk/python/openapi_client/models/client_request.py @@ -90,5 +90,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/error_body_response.py b/express_relay/sdk/python/openapi_client/models/error_body_response.py index 3924355890..4804dcd884 100644 --- a/express_relay/sdk/python/openapi_client/models/error_body_response.py +++ b/express_relay/sdk/python/openapi_client/models/error_body_response.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_bid.py b/express_relay/sdk/python/openapi_client/models/opportunity_bid.py index e8acebcf0b..d73b3f907a 100644 --- a/express_relay/sdk/python/openapi_client/models/opportunity_bid.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_bid.py @@ -91,5 +91,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "valid_until": obj.get("valid_until") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_params.py b/express_relay/sdk/python/openapi_client/models/opportunity_params.py index 43b38493d9..ce12170d5c 100644 --- a/express_relay/sdk/python/openapi_client/models/opportunity_params.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_params.py @@ -122,5 +122,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], OpportunityParamsOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_params_one_of.py b/express_relay/sdk/python/openapi_client/models/opportunity_params_one_of.py index 5cf761f268..2d09d3b3aa 100644 --- a/express_relay/sdk/python/openapi_client/models/opportunity_params_one_of.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_params_one_of.py @@ -119,5 +119,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "version": obj.get("version") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_params_v1.py b/express_relay/sdk/python/openapi_client/models/opportunity_params_v1.py index 8b8887e877..db9db72476 100644 --- a/express_relay/sdk/python/openapi_client/models/opportunity_params_v1.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_params_v1.py @@ -110,5 +110,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "value": obj.get("value") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_params_with_metadata.py b/express_relay/sdk/python/openapi_client/models/opportunity_params_with_metadata.py index 5a8ce7ae09..a2cb12e7ab 100644 --- a/express_relay/sdk/python/openapi_client/models/opportunity_params_with_metadata.py +++ b/express_relay/sdk/python/openapi_client/models/opportunity_params_with_metadata.py @@ -123,5 +123,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "opportunity_id": obj.get("opportunity_id") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/server_result_message.py b/express_relay/sdk/python/openapi_client/models/server_result_message.py index ecc4cd7241..63dcfc5369 100644 --- a/express_relay/sdk/python/openapi_client/models/server_result_message.py +++ b/express_relay/sdk/python/openapi_client/models/server_result_message.py @@ -137,5 +137,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerResultMessageOneOf, Se def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/openapi_client/models/server_result_message_one_of.py b/express_relay/sdk/python/openapi_client/models/server_result_message_one_of.py index b75edf0188..2f279fd945 100644 --- a/express_relay/sdk/python/openapi_client/models/server_result_message_one_of.py +++ b/express_relay/sdk/python/openapi_client/models/server_result_message_one_of.py @@ -90,5 +90,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/server_result_message_one_of1.py b/express_relay/sdk/python/openapi_client/models/server_result_message_one_of1.py index 94ff886045..dd60aff854 100644 --- a/express_relay/sdk/python/openapi_client/models/server_result_message_one_of1.py +++ b/express_relay/sdk/python/openapi_client/models/server_result_message_one_of1.py @@ -92,5 +92,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/server_result_response.py b/express_relay/sdk/python/openapi_client/models/server_result_response.py index cfb531a50d..9cd548d03b 100644 --- a/express_relay/sdk/python/openapi_client/models/server_result_response.py +++ b/express_relay/sdk/python/openapi_client/models/server_result_response.py @@ -91,5 +91,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/server_update_response.py b/express_relay/sdk/python/openapi_client/models/server_update_response.py index 5b91a0b189..23ad99afce 100644 --- a/express_relay/sdk/python/openapi_client/models/server_update_response.py +++ b/express_relay/sdk/python/openapi_client/models/server_update_response.py @@ -122,5 +122,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/openapi_client/models/server_update_response_one_of.py b/express_relay/sdk/python/openapi_client/models/server_update_response_one_of.py index 8b30652b65..87e0ef5397 100644 --- a/express_relay/sdk/python/openapi_client/models/server_update_response_one_of.py +++ b/express_relay/sdk/python/openapi_client/models/server_update_response_one_of.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "type": obj.get("type") }) return _obj - - diff --git a/express_relay/sdk/python/openapi_client/models/token_qty.py b/express_relay/sdk/python/openapi_client/models/token_qty.py index af736ecb53..1356639fc7 100644 --- a/express_relay/sdk/python/openapi_client/models/token_qty.py +++ b/express_relay/sdk/python/openapi_client/models/token_qty.py @@ -85,5 +85,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "contract": obj.get("contract") }) return _obj - - From 616ff4aab06628c9174646e163c39d1b6458796a Mon Sep 17 00:00:00 2001 From: ani Date: Mon, 4 Mar 2024 09:55:54 -0500 Subject: [PATCH 14/58] fix js filepath issue --- express_relay/sdk/js/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/express_relay/sdk/js/tsconfig.json b/express_relay/sdk/js/tsconfig.json index f977bab2ee..3614216b35 100644 --- a/express_relay/sdk/js/tsconfig.json +++ b/express_relay/sdk/js/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "target": "esnext", "module": "commonjs", From 26bc8d25e0c2bd31b63288cac2c8bdfa72226179 Mon Sep 17 00:00:00 2001 From: ani Date: Mon, 4 Mar 2024 10:33:40 -0500 Subject: [PATCH 15/58] added close ws --- express_relay/sdk/python/express_relay_client.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/express_relay/sdk/python/express_relay_client.py b/express_relay/sdk/python/express_relay_client.py index 6248e473d2..92811f3ea6 100644 --- a/express_relay/sdk/python/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_client.py @@ -58,6 +58,12 @@ async def start_ws(self, ws_timeout_config: WebsocketTimeoutConfig = WebsocketTi """ self.ws = await websockets.connect(self.ws_endpoint, open_timeout=ws_timeout_config.open_timeout, ping_interval=ws_timeout_config.ping_interval, ping_timeout=ws_timeout_config.ping_timeout, close_timeout=ws_timeout_config.close_timeout) + async def close_ws(self): + """ + Closes the websocket connection to the server. + """ + await self.ws.close() + async def get_liquidation_opportunities(self, chain_id: str, timeout: int = 10) -> list[OpportunityParamsWithMetadata]: """ Connects to the liquidation server and fetches liquidation opportunities for a given chain ID. From 74b3200768049919ddd75c3c63d93f6cb104fc39 Mon Sep 17 00:00:00 2001 From: ani Date: Mon, 4 Mar 2024 14:35:08 -0500 Subject: [PATCH 16/58] renamings and add send_ws_message method --- .../sdk/python/express_relay_client.py | 50 ++++++++++++------- .../searcher/examples/simple_searcher.py | 6 +-- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/express_relay/sdk/python/express_relay_client.py b/express_relay/sdk/python/express_relay_client.py index 92811f3ea6..e50b2a8180 100644 --- a/express_relay/sdk/python/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_client.py @@ -64,7 +64,7 @@ async def close_ws(self): """ await self.ws.close() - async def get_liquidation_opportunities(self, chain_id: str, timeout: int = 10) -> list[OpportunityParamsWithMetadata]: + async def get_opportunities(self, chain_id: str, timeout: int = 10) -> list[OpportunityParamsWithMetadata]: """ Connects to the liquidation server and fetches liquidation opportunities for a given chain ID. @@ -91,46 +91,60 @@ async def get_liquidation_opportunities(self, chain_id: str, timeout: int = 10) return opportunities - async def subscribe_chain_ids(self, chain_ids: list[str]): + async def send_ws_message(self, msg: dict): """ - Subscribes to a list of chain IDs for new liquidation opportunities. + Sends a message to the server via websocket. Args: - chain_ids (list[str]): A list of chain IDs to subscribe to. + msg (dict): The message to send. """ if not self.ws: raise ExpressRelayClientException("Websocket connection not yet open") + msg["id"] = str(self.ws_msg_counter) + self.ws_msg_counter += 1 + + await self.ws.send(json.dumps(msg)) + resp = json.loads(await self.ws.recv()) + if resp.get("status") == "error": + raise ExpressRelayClientException(f"Error in sending websocket message: {resp.get('result')}") + + async def subscribe_chains(self, chain_ids: list[str]): + """ + Subscribes websocket to a list of chain IDs for new liquidation opportunities. + + Args: + chain_ids (list[str]): A list of chain IDs to subscribe to. + """ json_subscribe = { "method": "subscribe", "params": { "chain_ids": chain_ids, }, - "id": str(self.ws_msg_counter), } - await self.ws.send(json.dumps(json_subscribe)) - self.ws_msg_counter += 1 + await self.send_ws_message(json_subscribe) - async def unsubscribe_chain_ids(self, chain_ids: str): - if not self.ws: - raise ExpressRelayClientException("Websocket connection not yet open") + async def unsubscribe_chains(self, chain_ids: str): + """ + Unsubscribes websocket from a list of chain IDs for new liquidation opportunities. - json_subscribe = { + Args: + chain_ids (list[str]): A list of chain IDs to unsubscribe from. + """ + json_unsubscribe = { "method": "unsubscribe", "params": { "chain_ids": chain_ids, }, - "id": str(self.ws_msg_counter), } - await self.ws.send(json.dumps(json_subscribe)) - self.ws_msg_counter += 1 + await self.send_ws_message(json_unsubscribe) - async def ws_liquidation_opportunities(self, opportunity_handler: Callable[[OpportunityParamsWithMetadata], None]): + async def ws_opportunities_handler(self, opportunity_callback: Callable[[OpportunityParamsWithMetadata], None]): """ - Connects to the liquidation server's websocket and handles new liquidation opportunities. + Continuously handles new liquidation opportunities as they are received from the server via websocket. Args: - opportunity_handler (async func): An async function that defines how to handle new liquidation opportunities. Should take in one external argument of type OpportunityParamsWithMetadata and return nothing. + opportunity_callback (async func): An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. """ if not self.ws: raise ExpressRelayClientException("Websocket connection not yet open") @@ -147,7 +161,7 @@ async def ws_liquidation_opportunities(self, opportunity_handler: Callable[[Oppo opportunity = msg["opportunity"] opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) try: - await opportunity_handler(opportunity) + await opportunity_callback(opportunity) except Exception as e: raise ExpressRelayClientException(f"Error in opportunity handler: {e}") diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py index 3c7afd4363..e486055a36 100644 --- a/express_relay/sdk/python/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -42,7 +42,7 @@ def assess_liquidation_opportunity( return bid_info - async def ws_opportunity_handler( + async def opportunity_callback( self, opp: OpportunityParamsWithMetadata ): bid_info = self.assess_liquidation_opportunity(opp) @@ -100,10 +100,10 @@ async def main(): await simple_searcher.client.start_ws() - ws_call = simple_searcher.client.ws_liquidation_opportunities(simple_searcher.ws_opportunity_handler) + ws_call = simple_searcher.client.ws_opportunities_handler(simple_searcher.opportunity_callback) asyncio.create_task(ws_call) - await simple_searcher.client.subscribe_chain_ids(args.chain_ids) + await simple_searcher.client.subscribe_chains(args.chain_ids) while True: await asyncio.sleep(5) From 206f481588462260afae728924853b958899f3cb Mon Sep 17 00:00:00 2001 From: ani Date: Mon, 4 Mar 2024 14:46:49 -0500 Subject: [PATCH 17/58] get rid of duplicate error parsing --- express_relay/sdk/python/express_relay_client.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/express_relay/sdk/python/express_relay_client.py b/express_relay/sdk/python/express_relay_client.py index e50b2a8180..5975421631 100644 --- a/express_relay/sdk/python/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_client.py @@ -105,9 +105,6 @@ async def send_ws_message(self, msg: dict): self.ws_msg_counter += 1 await self.ws.send(json.dumps(msg)) - resp = json.loads(await self.ws.recv()) - if resp.get("status") == "error": - raise ExpressRelayClientException(f"Error in sending websocket message: {resp.get('result')}") async def subscribe_chains(self, chain_ids: list[str]): """ From 3e66a846148c082044fa2a40b66536825e1c6085 Mon Sep 17 00:00:00 2001 From: ani Date: Mon, 4 Mar 2024 16:10:19 -0500 Subject: [PATCH 18/58] cleanup --- express_relay/sdk/js/package.json | 2 +- express_relay/sdk/python/README.md | 2 +- .../sdk/python/express_relay_client.py | 41 +++++++++---------- .../searcher/examples/simple_searcher.py | 14 ++++++- 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/express_relay/sdk/js/package.json b/express_relay/sdk/js/package.json index 4220bbe3c2..c3895cf636 100644 --- a/express_relay/sdk/js/package.json +++ b/express_relay/sdk/js/package.json @@ -33,7 +33,7 @@ "repository": { "type": "git", "url": "https://github.com/pyth-network/pyth-crosschain", - "directory": "express_relay/sdk/js/searcher" + "directory": "express_relay/sdk/js" }, "dependencies": { "isomorphic-ws": "^5.0.0", diff --git a/express_relay/sdk/python/README.md b/express_relay/sdk/python/README.md index 93fb7f5030..0db80b5ddc 100644 --- a/express_relay/sdk/python/README.md +++ b/express_relay/sdk/python/README.md @@ -29,7 +29,7 @@ $ openapi-generator generate -i https://per-staging.dourolabs.app/docs/openapi.j To run the simple searcher script, you can run ``` -$ python3 -m examples.simple_searcher --private-key --chain-id development --verbose --liquidation-server-url https://per-staging.dourolabs.app/ +$ python3 -m searcher.examples.simple_searcher --private-key --chain-id development --verbose --server-url https://per-staging.dourolabs.app/ ``` This simple example runs a searcher that queries the Express Relay liquidation server for available liquidation opportunities and naively submits a bid on each available opportunity. diff --git a/express_relay/sdk/python/express_relay_client.py b/express_relay/sdk/python/express_relay_client.py index 5975421631..598c28e018 100644 --- a/express_relay/sdk/python/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_client.py @@ -1,13 +1,12 @@ import web3 +from web3.auto import w3 from eth_abi import encode from eth_account.account import Account -from web3.auto import w3 import httpx import urllib.parse import websockets import json from typing import Callable -import asyncio from openapi_client.models.opportunity_bid import OpportunityBid from openapi_client.models.opportunity_params import OpportunityParams @@ -23,10 +22,10 @@ class WebsocketTimeoutConfig: A class to hold the timeout configuration for the websocket connection. Args: - open_timeout (int): The timeout for opening the websocket connection. - ping_interval (int): The interval at which to send ping messages to the server. - ping_timeout (int): The timeout for the ping messages. - close_timeout (int): The timeout for closing the websocket connection. + open_timeout (int): The timeout for opening the websocket connection in seconds. + ping_interval (int): The interval at which to send ping messages to the server in seconds. + ping_timeout (int): The timeout for the ping messages in seconds. + close_timeout (int): The timeout for closing the websocket connection in seconds. """ def __init__(self, open_timeout: int = 10, ping_interval: int = 20, ping_timeout: int = 20, close_timeout: int = 10): self.open_timeout = open_timeout @@ -39,13 +38,16 @@ class ExpressRelayClientException(Exception): class ExpressRelayClient: def __init__(self, server_url: str): - self.server_url = server_url - if self.server_url.startswith("https"): - self.ws_endpoint = urllib.parse.urljoin(f"wss{self.server_url[5:]}", "v1/ws") - elif self.server_url.startswith("http"): - self.ws_endpoint = urllib.parse.urljoin(f"ws{self.server_url[4:]}", "v1/ws") + parsed_url = urllib.parse.urlparse(server_url) + if parsed_url.scheme == "https": + ws_scheme = "wss" + elif parsed_url.scheme == "http": + ws_scheme = "ws" else: raise ValueError("Invalid liquidation server URL") + + self.server_url = server_url + self.ws_endpoint = parsed_url._replace(scheme=ws_scheme, path="/v1/ws").geturl() self.ws_msg_counter = 0 self.ws = False @@ -70,16 +72,14 @@ async def get_opportunities(self, chain_id: str, timeout: int = 10) -> list[Oppo Args: chain_id (str): The chain ID to fetch liquidation opportunities for. - timeout (int): The timeout for the HTTP request. + timeout (int): The timeout for the HTTP request in seconds. Returns: list[OpportunityParamsWithMetadata]: A list of liquidation opportunities. """ async with httpx.AsyncClient() as client: resp = ( await client.get( - urllib.parse.urljoin( - self.server_url, "/v1/liquidation/opportunities" - ), + urllib.parse.urlparse(self.server_url)._replace(path="/v1/liquidation/opportunities").geturl(), params={"chain_id": chain_id}, timeout=timeout, ) @@ -168,13 +168,13 @@ async def submit_opportunity(self, opportunity: OpportunityParams, timeout: int Args: opportunity (OpportunityParams): An object representing the opportunity to submit. - timeout (int): The timeout for the HTTP request. + timeout (int): The timeout for the HTTP request in seconds. Returns: httpx.Response: The server's response to the opportunity submission. Throws an exception if the response is not successful. """ async with httpx.AsyncClient() as client: resp = await client.post( - urllib.parse.urljoin(self.server_url, "/v1/liquidation/opportunities"), + urllib.parse.urlparse(self.server_url)._replace(path="/v1/liquidation/opportunities").geturl(), json=opportunity.to_dict(), timeout=timeout, ) @@ -189,16 +189,13 @@ async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> httpx.Respon Args: bid_info (BidInfo): An object representing the bid to submit. - timeout (int): The timeout for the HTTP request. + timeout (int): The timeout for the HTTP request in seconds. Returns: httpx.Response: The server's response to the bid submission. Throws an exception if the response is not successful. """ async with httpx.AsyncClient() as client: resp = await client.post( - urllib.parse.urljoin( - self.server_url, - f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids", - ), + urllib.parse.urlparse(self.server_url)._replace(path=f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids").geturl(), json=bid_info.opportunity_bid.to_dict(), timeout=timeout, ) diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/searcher/examples/simple_searcher.py index e486055a36..3b8b7e91ba 100644 --- a/express_relay/sdk/python/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/searcher/examples/simple_searcher.py @@ -9,7 +9,8 @@ logger = logging.getLogger(__name__) -VALID_UNTIL = 1_000_000_000_000 +# Set validity (naively) to max uint256 +VALID_UNTIL = 2**256-1 class SimpleSearcher(): def __init__(self, server_url: str, private_key: str, default_bid: int): @@ -24,6 +25,7 @@ def assess_liquidation_opportunity( ) -> BidInfo | None: """ Assesses whether a liquidation opportunity is worth liquidating; if so, returns a BidInfo object. Otherwise returns None. + This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the liquidation. There are many ways to evaluate this, but the most common way is to check that the value of the amount the searcher will receive from the liquidation exceeds the value of the amount repaid. Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation. @@ -45,6 +47,12 @@ def assess_liquidation_opportunity( async def opportunity_callback( self, opp: OpportunityParamsWithMetadata ): + """ + Callback function to run when a new liquidation opportunity is found. + + Args: + opp: A OpportunityParamsWithMetadata object, representing a single liquidation opportunity. + """ bid_info = self.assess_liquidation_opportunity(opp) if bid_info: try: @@ -106,7 +114,9 @@ async def main(): await simple_searcher.client.subscribe_chains(args.chain_ids) while True: - await asyncio.sleep(5) + if simple_searcher.client.ws.closed: + logger.error("Websocket connection closed, exiting") + break if __name__ == "__main__": asyncio.run(main()) From 35abeca323b6b75f459c3723df93a2beea8728b2 Mon Sep 17 00:00:00 2001 From: ani Date: Mon, 4 Mar 2024 17:44:11 -0500 Subject: [PATCH 19/58] set up pypi workflow --- .../workflows/pypi-express-relay-utils.yml | 21 +++++++++++ express_relay/sdk/python/README.md | 4 +-- .../express_relay_client.py | 0 .../openapi_client/models/bid.py | 0 .../openapi_client/models/bid400_response.py | 0 .../openapi_client/models/bid_result.py | 0 .../openapi_client/models/client_message.py | 0 .../models/client_message_one_of.py | 0 .../models/client_message_one_of1.py | 0 .../models/client_message_one_of_params.py | 0 .../openapi_client/models/client_request.py | 0 .../models/error_body_response.py | 0 .../openapi_client/models/opportunity_bid.py | 0 .../models/opportunity_params.py | 0 .../models/opportunity_params_one_of.py | 0 .../models/opportunity_params_v1.py | 0 .../opportunity_params_with_metadata.py | 0 .../models/server_result_message.py | 0 .../models/server_result_message_one_of.py | 0 .../models/server_result_message_one_of1.py | 0 .../models/server_result_response.py | 0 .../models/server_update_response.py | 0 .../models/server_update_response_one_of.py | 0 .../openapi_client/models/token_qty.py | 0 .../searcher/examples/simple_searcher.py | 0 express_relay/sdk/python/pyproject.toml | 35 +++++++++---------- 26 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/pypi-express-relay-utils.yml rename express_relay/sdk/python/{ => express_relay_utils}/express_relay_client.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/bid.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/bid400_response.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/bid_result.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/client_message.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/client_message_one_of.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/client_message_one_of1.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/client_message_one_of_params.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/client_request.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/error_body_response.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/opportunity_bid.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/opportunity_params.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/opportunity_params_one_of.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/opportunity_params_v1.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/opportunity_params_with_metadata.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/server_result_message.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/server_result_message_one_of.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/server_result_message_one_of1.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/server_result_response.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/server_update_response.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/server_update_response_one_of.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/openapi_client/models/token_qty.py (100%) rename express_relay/sdk/python/{ => express_relay_utils}/searcher/examples/simple_searcher.py (100%) diff --git a/.github/workflows/pypi-express-relay-utils.yml b/.github/workflows/pypi-express-relay-utils.yml new file mode 100644 index 0000000000..36ff186ac3 --- /dev/null +++ b/.github/workflows/pypi-express-relay-utils.yml @@ -0,0 +1,21 @@ +name: Upload express-relay-utils Python Package to PyPI + +on: + push: + tags: + - "v*" + +jobs: + deploy: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - name: Install dependencies + run: | + python3 -m pip install --upgrade poetry + poetry build + - name: Build and publish + run: | + poetry build + poetry publish --username __token__ --password ${{ secrets.PYPI_TOKEN }} diff --git a/express_relay/sdk/python/README.md b/express_relay/sdk/python/README.md index 0db80b5ddc..17aa736bc6 100644 --- a/express_relay/sdk/python/README.md +++ b/express_relay/sdk/python/README.md @@ -8,7 +8,7 @@ The SDK includes searcher-side utilities and protocol-side utilities. The search ## Installation -### pip +### poetry ### openapi-generator @@ -26,7 +26,7 @@ $ openapi-generator generate -i https://per-staging.dourolabs.app/docs/openapi.j ## Quickstart -To run the simple searcher script, you can run +To run the simple searcher script, navigate to `express_relay_utils/` and run ``` $ python3 -m searcher.examples.simple_searcher --private-key --chain-id development --verbose --server-url https://per-staging.dourolabs.app/ diff --git a/express_relay/sdk/python/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py similarity index 100% rename from express_relay/sdk/python/express_relay_client.py rename to express_relay/sdk/python/express_relay_utils/express_relay_client.py diff --git a/express_relay/sdk/python/openapi_client/models/bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/bid.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py diff --git a/express_relay/sdk/python/openapi_client/models/bid400_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/bid400_response.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py diff --git a/express_relay/sdk/python/openapi_client/models/bid_result.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/bid_result.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py diff --git a/express_relay/sdk/python/openapi_client/models/client_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/client_message.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py diff --git a/express_relay/sdk/python/openapi_client/models/client_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/client_message_one_of.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py diff --git a/express_relay/sdk/python/openapi_client/models/client_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/client_message_one_of1.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py diff --git a/express_relay/sdk/python/openapi_client/models/client_message_one_of_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/client_message_one_of_params.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py diff --git a/express_relay/sdk/python/openapi_client/models/client_request.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/client_request.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py diff --git a/express_relay/sdk/python/openapi_client/models/error_body_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/error_body_response.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/opportunity_bid.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/opportunity_params.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_params_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/opportunity_params_one_of.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_params_v1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/opportunity_params_v1.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py diff --git a/express_relay/sdk/python/openapi_client/models/opportunity_params_with_metadata.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/opportunity_params_with_metadata.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py diff --git a/express_relay/sdk/python/openapi_client/models/server_result_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/server_result_message.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py diff --git a/express_relay/sdk/python/openapi_client/models/server_result_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/server_result_message_one_of.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py diff --git a/express_relay/sdk/python/openapi_client/models/server_result_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/server_result_message_one_of1.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py diff --git a/express_relay/sdk/python/openapi_client/models/server_result_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/server_result_response.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py diff --git a/express_relay/sdk/python/openapi_client/models/server_update_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/server_update_response.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py diff --git a/express_relay/sdk/python/openapi_client/models/server_update_response_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/server_update_response_one_of.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py diff --git a/express_relay/sdk/python/openapi_client/models/token_qty.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py similarity index 100% rename from express_relay/sdk/python/openapi_client/models/token_qty.py rename to express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py diff --git a/express_relay/sdk/python/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py similarity index 100% rename from express_relay/sdk/python/searcher/examples/simple_searcher.py rename to express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py diff --git a/express_relay/sdk/python/pyproject.toml b/express_relay/sdk/python/pyproject.toml index a39f41224d..8f33c82c6b 100644 --- a/express_relay/sdk/python/pyproject.toml +++ b/express_relay/sdk/python/pyproject.toml @@ -1,22 +1,21 @@ -[build-system] -requires = [ - "setuptools>=42", - "wheel" -] -build-backend = "setuptools.build_meta" - [tool.poetry] -name = "express-relay-utils" -version = 0.0.1 -dependencies = [ - "web3", - "eth_abi", - "eth_account", - "httpx", - "websockets", - "asyncio", - "argparse" -] +name = "express_relay_utils" +version = "0.0.1" description = "Utilities for searchers and protocols to interact with the express relay protocol." +authors = ["dourolabs"] license = "Proprietary" readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.11" +web3 = "^6.15.1" +eth_abi = "^4.2.1" +eth_account = "^0.10.0" +httpx = "^0.23.3" +websockets = "^11.0.3" +asyncio = "^3.4.3" +argparse = "^1.4.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" From ed32ff89db48f2685706965e0637b64e9d27427d Mon Sep 17 00:00:00 2001 From: ani Date: Tue, 5 Mar 2024 22:18:17 -0500 Subject: [PATCH 20/58] address comments --- .../express_relay_client.py | 155 ++++++++++-------- .../searcher/examples/simple_searcher.py | 41 ++--- 2 files changed, 106 insertions(+), 90 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 598c28e018..f30149eb44 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -1,41 +1,32 @@ -import web3 -from web3.auto import w3 -from eth_abi import encode -from eth_account.account import Account -import httpx -import urllib.parse -import websockets +import asyncio import json +import urllib.parse from typing import Callable +import httpx +import web3 +import websockets +from eth_abi import encode +from eth_account.account import Account +from openapi_client.models.client_message import ClientMessage from openapi_client.models.opportunity_bid import OpportunityBid from openapi_client.models.opportunity_params import OpportunityParams -from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata +from openapi_client.models.opportunity_params_with_metadata import ( + OpportunityParamsWithMetadata, +) +from web3.auto import w3 + class BidInfo: def __init__(self, opportunity_id: str, opportunity_bid: OpportunityBid): self.opportunity_id = opportunity_id self.opportunity_bid = opportunity_bid -class WebsocketTimeoutConfig: - """ - A class to hold the timeout configuration for the websocket connection. - - Args: - open_timeout (int): The timeout for opening the websocket connection in seconds. - ping_interval (int): The interval at which to send ping messages to the server in seconds. - ping_timeout (int): The timeout for the ping messages in seconds. - close_timeout (int): The timeout for closing the websocket connection in seconds. - """ - def __init__(self, open_timeout: int = 10, ping_interval: int = 20, ping_timeout: int = 20, close_timeout: int = 10): - self.open_timeout = open_timeout - self.ping_interval = ping_interval - self.ping_timeout = ping_timeout - self.close_timeout = close_timeout class ExpressRelayClientException(Exception): pass + class ExpressRelayClient: def __init__(self, server_url: str): parsed_url = urllib.parse.urlparse(server_url) @@ -51,14 +42,15 @@ def __init__(self, server_url: str): self.ws_msg_counter = 0 self.ws = False - async def start_ws(self, ws_timeout_config: WebsocketTimeoutConfig = WebsocketTimeoutConfig()): + async def start_ws(self, **kwargs): """ - Initializes the websocket connection to the server. + Initializes the websocket connection to the server, if not already connected. Args: - ws_timeout_config (WebsocketTimeoutConfig): The timeout configuration for the websocket connection. + kwargs: Keyword arguments to pass to the websocket connection. """ - self.ws = await websockets.connect(self.ws_endpoint, open_timeout=ws_timeout_config.open_timeout, ping_interval=ws_timeout_config.ping_interval, ping_timeout=ws_timeout_config.ping_timeout, close_timeout=ws_timeout_config.close_timeout) + if not self.ws: + self.ws = await websockets.connect(self.ws_endpoint, **kwargs) async def close_ws(self): """ @@ -66,28 +58,37 @@ async def close_ws(self): """ await self.ws.close() - async def get_opportunities(self, chain_id: str, timeout: int = 10) -> list[OpportunityParamsWithMetadata]: + async def get_opportunities( + self, chain_id: str | None = None, timeout: int = 10 + ) -> list[OpportunityParamsWithMetadata]: """ - Connects to the liquidation server and fetches liquidation opportunities for a given chain ID. + Connects to the liquidation server and fetches liquidation opportunities. Args: - chain_id (str): The chain ID to fetch liquidation opportunities for. - timeout (int): The timeout for the HTTP request in seconds. + chain_id: The chain ID to fetch liquidation opportunities for. If None, fetches opportunities across all chains. + timeout: The timeout for the HTTP request in seconds. Returns: - list[OpportunityParamsWithMetadata]: A list of liquidation opportunities. + A list of liquidation opportunities. """ + params = {} + if chain_id: + params["chain_id"] = chain_id + async with httpx.AsyncClient() as client: - resp = ( - await client.get( - urllib.parse.urlparse(self.server_url)._replace(path="/v1/liquidation/opportunities").geturl(), - params={"chain_id": chain_id}, - timeout=timeout, - ) + resp = await client.get( + urllib.parse.urlparse(self.server_url) + ._replace(path="/v1/liquidation/opportunities") + .geturl(), + params=params, + timeout=timeout, ) resp.raise_for_status() - opportunities = [OpportunityParamsWithMetadata.from_dict(opportunity) for opportunity in resp.json()] + opportunities = [ + OpportunityParamsWithMetadata.from_dict(opportunity) + for opportunity in resp.json() + ] return opportunities @@ -96,11 +97,13 @@ async def send_ws_message(self, msg: dict): Sends a message to the server via websocket. Args: - msg (dict): The message to send. + msg: The message to send. """ if not self.ws: - raise ExpressRelayClientException("Websocket connection not yet open") + await self.start_ws() + # validate the format of msg + msg = ClientMessage.from_dict(msg).to_dict() msg["id"] = str(self.ws_msg_counter) self.ws_msg_counter += 1 @@ -111,7 +114,7 @@ async def subscribe_chains(self, chain_ids: list[str]): Subscribes websocket to a list of chain IDs for new liquidation opportunities. Args: - chain_ids (list[str]): A list of chain IDs to subscribe to. + chain_ids: A list of chain IDs to subscribe to. """ json_subscribe = { "method": "subscribe", @@ -121,12 +124,12 @@ async def subscribe_chains(self, chain_ids: list[str]): } await self.send_ws_message(json_subscribe) - async def unsubscribe_chains(self, chain_ids: str): + async def unsubscribe_chains(self, chain_ids: list[str]): """ Unsubscribes websocket from a list of chain IDs for new liquidation opportunities. Args: - chain_ids (list[str]): A list of chain IDs to unsubscribe from. + chain_ids: A list of chain IDs to unsubscribe from. """ json_unsubscribe = { "method": "unsubscribe", @@ -136,73 +139,79 @@ async def unsubscribe_chains(self, chain_ids: str): } await self.send_ws_message(json_unsubscribe) - async def ws_opportunities_handler(self, opportunity_callback: Callable[[OpportunityParamsWithMetadata], None]): + async def ws_opportunities_handler( + self, opportunity_callback: Callable[[OpportunityParamsWithMetadata], None] + ): """ Continuously handles new liquidation opportunities as they are received from the server via websocket. Args: - opportunity_callback (async func): An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. + opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. """ if not self.ws: - raise ExpressRelayClientException("Websocket connection not yet open") + await self.start_ws() while True: msg = json.loads(await self.ws.recv()) status = msg.get("status") if status and status != "success": - raise ExpressRelayClientException(f"Error in websocket subscription: {msg.get('result')}") + raise ExpressRelayClientException( + f"Error in websocket subscription: {msg.get('result')}" + ) if msg.get("type") != "new_opportunity": continue opportunity = msg["opportunity"] opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) - try: - await opportunity_callback(opportunity) - except Exception as e: - raise ExpressRelayClientException(f"Error in opportunity handler: {e}") + asyncio.create_task(opportunity_callback(opportunity)) - async def submit_opportunity(self, opportunity: OpportunityParams, timeout: int = 10) -> httpx.Response: + async def submit_opportunity( + self, opportunity: OpportunityParams, timeout: int = 10 + ) -> str: """ Submits an opportunity to the liquidation server. Args: - opportunity (OpportunityParams): An object representing the opportunity to submit. - timeout (int): The timeout for the HTTP request in seconds. + opportunity: An object representing the opportunity to submit. + timeout: The timeout for the HTTP request in seconds. Returns: - httpx.Response: The server's response to the opportunity submission. Throws an exception if the response is not successful. + The ID of the submitted opportunity. """ async with httpx.AsyncClient() as client: resp = await client.post( - urllib.parse.urlparse(self.server_url)._replace(path="/v1/liquidation/opportunities").geturl(), + urllib.parse.urlparse(self.server_url) + ._replace(path="/v1/liquidation/opportunities") + .geturl(), json=opportunity.to_dict(), timeout=timeout, ) - resp.raise_for_status() + return resp.json()["opportunity_id"] - return resp - - async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> httpx.Response: + async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> str: """ Submits a bid to the liquidation server. Args: - bid_info (BidInfo): An object representing the bid to submit. - timeout (int): The timeout for the HTTP request in seconds. + bid_info: An object representing the bid to submit. + timeout: The timeout for the HTTP request in seconds. Returns: - httpx.Response: The server's response to the bid submission. Throws an exception if the response is not successful. + The ID of the submitted bid. """ async with httpx.AsyncClient() as client: resp = await client.post( - urllib.parse.urlparse(self.server_url)._replace(path=f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids").geturl(), + urllib.parse.urlparse(self.server_url) + ._replace( + path=f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids" + ) + .geturl(), json=bid_info.opportunity_bid.to_dict(), timeout=timeout, ) - resp.raise_for_status() + return resp.json()["id"] - return resp def sign_bid( liquidation_opportunity: OpportunityParamsWithMetadata, @@ -217,12 +226,18 @@ def sign_bid( liquidation_opportunity: An object representing the liquidation opportunity, of type OpportunityParamsWithMetadata. bid: An integer representing the amount of the bid. valid_until: An integer representing the block until which the bid is valid. - private_key: A string representing the liquidator's private key. + private_key: A 0x-prefixed hex string representing the liquidator's private key. Returns: A BidInfo object, representing the transaction to submit to the liquidation server. This object contains the liquidator's signature. """ - repay_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.repay_tokens] - receipt_tokens = [(token.contract, int(token.amount)) for token in liquidation_opportunity.receipt_tokens] + repay_tokens = [ + (token.contract, int(token.amount)) + for token in liquidation_opportunity.repay_tokens + ] + receipt_tokens = [ + (token.contract, int(token.amount)) + for token in liquidation_opportunity.receipt_tokens + ] calldata = bytes.fromhex(liquidation_opportunity.calldata.replace("0x", "")) digest = encode( diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index 3b8b7e91ba..8afa658378 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -1,18 +1,20 @@ import argparse import asyncio import logging -from eth_account.account import Account +from eth_account.account import Account from express_relay_client import BidInfo, ExpressRelayClient, sign_bid - -from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata +from openapi_client.models.opportunity_params_with_metadata import ( + OpportunityParamsWithMetadata, +) logger = logging.getLogger(__name__) # Set validity (naively) to max uint256 -VALID_UNTIL = 2**256-1 +VALID_UNTIL_MAX = 2**256 - 1 -class SimpleSearcher(): + +class SimpleSearcher: def __init__(self, server_url: str, private_key: str, default_bid: int): self.client = ExpressRelayClient(server_url) self.private_key = private_key @@ -35,18 +37,11 @@ def assess_liquidation_opportunity( Returns: If the opportunity is deemed worthwhile, this function can return a BidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. """ - bid_info = sign_bid( - opp, - self.default_bid, - VALID_UNTIL, - self.private_key - ) + bid_info = sign_bid(opp, self.default_bid, VALID_UNTIL_MAX, self.private_key) return bid_info - async def opportunity_callback( - self, opp: OpportunityParamsWithMetadata - ): + async def opportunity_callback(self, opp: OpportunityParamsWithMetadata): """ Callback function to run when a new liquidation opportunity is found. @@ -57,9 +52,13 @@ async def opportunity_callback( if bid_info: try: await self.client.submit_bid(bid_info) - logger.info(f"Submitted bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}") + logger.info( + f"Submitted bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}" + ) except Exception as e: - logger.error(f"Error submitting bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}: {e}") + logger.error( + f"Error submitting bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}: {e}" + ) async def main(): @@ -106,17 +105,19 @@ async def main(): simple_searcher = SimpleSearcher(args.server_url, sk_liquidator, args.bid) logger.info("Liquidator address: %s", simple_searcher.liquidator) - await simple_searcher.client.start_ws() + await simple_searcher.client.subscribe_chains(args.chain_ids) - ws_call = simple_searcher.client.ws_opportunities_handler(simple_searcher.opportunity_callback) + ws_call = simple_searcher.client.ws_opportunities_handler( + simple_searcher.opportunity_callback + ) asyncio.create_task(ws_call) - await simple_searcher.client.subscribe_chains(args.chain_ids) - while True: if simple_searcher.client.ws.closed: logger.error("Websocket connection closed, exiting") break + await asyncio.sleep(10) + if __name__ == "__main__": asyncio.run(main()) From e9e6e509ed501b6398c1df6844c0c837dbb69299 Mon Sep 17 00:00:00 2001 From: ani Date: Tue, 5 Mar 2024 22:55:55 -0500 Subject: [PATCH 21/58] add python precommit --- .pre-commit-config.yaml | 21 +++++++++++++++++++++ express_relay/sdk/python/pyproject.toml | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d9dd6b73d3..8a69a82b5b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -111,3 +111,24 @@ repos: entry: cargo +nightly-2023-03-01 clippy --manifest-path ./target_chains/solana/Cargo.toml --tests --fix --allow-dirty --allow-staged -- -D warnings pass_filenames: false files: target_chains/solana + # For express relay python files + - id: isort + name: isort + entry: poetry -C express_relay/sdk/python/express_relay_utils run isort --profile=black express_relay/sdk/python/express_relay_utils --skip openapi_client + pass_filenames: false + language: system + - id: black + name: black + entry: poetry -C express_relay/sdk/python/express_relay_utils run black express_relay/sdk/python/express_relay_utils --exclude openapi_client + pass_filenames: false + language: system + - id: pyflakes1 + name: pyflakes1 + entry: poetry -C express_relay/sdk/python/express_relay_utils run pyflakes express_relay/sdk/python/express_relay_utils/searcher + pass_filenames: false + language: system + - id: pyflakes2 + name: pyflakes2 + entry: poetry -C express_relay/sdk/python/express_relay_utils run pyflakes express_relay/sdk/python/express_relay_utils/express_relay_client.py + pass_filenames: false + language: system diff --git a/express_relay/sdk/python/pyproject.toml b/express_relay/sdk/python/pyproject.toml index 8f33c82c6b..3765c946ea 100644 --- a/express_relay/sdk/python/pyproject.toml +++ b/express_relay/sdk/python/pyproject.toml @@ -16,6 +16,11 @@ websockets = "^11.0.3" asyncio = "^3.4.3" argparse = "^1.4.0" +[tool.poetry.group.dev.dependencies] +black = "^24.1.1" +flake8 = "^7.0.0" +isort = "^5.13.2" + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" From e64296c8f59039485784154073ffb2dea946a7be Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 17:17:01 -0500 Subject: [PATCH 22/58] changes + precommit changes --- .github/workflows/ci-pre-commit.yml | 7 ++ .pre-commit-config.yaml | 21 ++-- .../express_relay_client.py | 95 +++++++++++++------ .../searcher/examples/simple_searcher.py | 13 +-- express_relay/sdk/python/pyproject.toml | 7 +- 5 files changed, 91 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index ad6c22bf6f..0c98210fce 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -24,6 +24,13 @@ jobs: profile: minimal toolchain: nightly-2023-07-23 components: rustfmt, clippy + - uses: actions/checkout@v4 + - name: Install poetry + run: pipx install poetry + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "poetry" - uses: pre-commit/action@v3.0.0 if: ${{ github.event_name == 'pull_request' }} with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8a69a82b5b..8ab7199b5c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -112,23 +112,18 @@ repos: pass_filenames: false files: target_chains/solana # For express relay python files - - id: isort - name: isort - entry: poetry -C express_relay/sdk/python/express_relay_utils run isort --profile=black express_relay/sdk/python/express_relay_utils --skip openapi_client - pass_filenames: false - language: system + # - id: isort + # name: isort + # entry: poetry -C express_relay/sdk/python/express_relay_utils run isort --profile=black express_relay/sdk/python/express_relay_utils --skip openapi_client + # pass_filenames: false + # language: system - id: black name: black entry: poetry -C express_relay/sdk/python/express_relay_utils run black express_relay/sdk/python/express_relay_utils --exclude openapi_client pass_filenames: false language: system - - id: pyflakes1 - name: pyflakes1 - entry: poetry -C express_relay/sdk/python/express_relay_utils run pyflakes express_relay/sdk/python/express_relay_utils/searcher - pass_filenames: false - language: system - - id: pyflakes2 - name: pyflakes2 - entry: poetry -C express_relay/sdk/python/express_relay_utils run pyflakes express_relay/sdk/python/express_relay_utils/express_relay_client.py + - id: ruff + name: ruff + entry: poetry -C express_relay/sdk/python/express_relay_utils run ruff express_relay/sdk/python/express_relay_utils --exclude openapi_client --fix pass_filenames: false language: system diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index f30149eb44..046c4bcf9c 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -40,23 +40,42 @@ def __init__(self, server_url: str): self.server_url = server_url self.ws_endpoint = parsed_url._replace(scheme=ws_scheme, path="/v1/ws").geturl() self.ws_msg_counter = 0 - self.ws = False - - async def start_ws(self, **kwargs): + self.ws = None + self.ws_lock = asyncio.Lock() + self.ws_task = None + self.ws_msg_futures = {} + + async def start_ws( + self, + opportunity_callback: ( + Callable[[OpportunityParamsWithMetadata], None] | None + ) = None, + **kwargs, + ) -> asyncio.Task: """ Initializes the websocket connection to the server, if not already connected. Args: kwargs: Keyword arguments to pass to the websocket connection. + Returns: + The websocket task. """ - if not self.ws: - self.ws = await websockets.connect(self.ws_endpoint, **kwargs) + async with self.ws_lock: + if self.ws is None: + self.ws = await websockets.connect(self.ws_endpoint, **kwargs) + + if self.ws_task is None: + ws_call = self.ws_handler(opportunity_callback) + self.ws_task = asyncio.create_task(ws_call) + + return self.ws_task async def close_ws(self): """ Closes the websocket connection to the server. """ - await self.ws.close() + async with self.ws_lock: + await self.ws.close() async def get_opportunities( self, chain_id: str | None = None, timeout: int = 10 @@ -99,15 +118,22 @@ async def send_ws_message(self, msg: dict): Args: msg: The message to send. """ - if not self.ws: - await self.start_ws() + await self.start_ws() # validate the format of msg msg = ClientMessage.from_dict(msg).to_dict() msg["id"] = str(self.ws_msg_counter) self.ws_msg_counter += 1 + future = asyncio.get_event_loop().create_future() + await self.ws.send(json.dumps(msg)) + self.ws_msg_futures[msg["id"]] = future + + # await the response msg for the subscription from the server + msg = await future + + self.process_msg(msg) async def subscribe_chains(self, chain_ids: list[str]): """ @@ -139,32 +165,47 @@ async def unsubscribe_chains(self, chain_ids: list[str]): } await self.send_ws_message(json_unsubscribe) - async def ws_opportunities_handler( - self, opportunity_callback: Callable[[OpportunityParamsWithMetadata], None] + def process_msg(self, msg: dict): + """ + Processes a message received from the server via websocket. + + Args: + msg: The message to process. + """ + if msg.get("status") and msg.get("status") != "success": + raise ExpressRelayClientException( + f"Error in websocket subscription: {msg.get('result')}" + ) + + async def ws_handler( + self, + opportunity_callback: ( + Callable[[OpportunityParamsWithMetadata], None] | None + ) = None, ): """ - Continuously handles new liquidation opportunities as they are received from the server via websocket. + Continuously handles new ws messages as they are received from the server via websocket. Args: opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. """ if not self.ws: - await self.start_ws() - - while True: - msg = json.loads(await self.ws.recv()) - status = msg.get("status") - if status and status != "success": - raise ExpressRelayClientException( - f"Error in websocket subscription: {msg.get('result')}" - ) - - if msg.get("type") != "new_opportunity": - continue - - opportunity = msg["opportunity"] - opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) - asyncio.create_task(opportunity_callback(opportunity)) + raise ExpressRelayClientException("Websocket not connected") + + async for msg in self.ws: + msg = json.loads(msg) + if msg.get("id"): + future = self.ws_msg_futures.pop(msg["id"]) + future.set_result(msg) + else: + self.process_msg(msg) + + if msg.get("type") == "new_opportunity": + opportunity = msg["opportunity"] + opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) + + if opportunity_callback is not None: + asyncio.create_task(opportunity_callback(opportunity)) async def submit_opportunity( self, opportunity: OpportunityParams, timeout: int = 10 diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index 8afa658378..c33f426e34 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -105,18 +105,11 @@ async def main(): simple_searcher = SimpleSearcher(args.server_url, sk_liquidator, args.bid) logger.info("Liquidator address: %s", simple_searcher.liquidator) - await simple_searcher.client.subscribe_chains(args.chain_ids) + task = await simple_searcher.client.start_ws(simple_searcher.opportunity_callback) - ws_call = simple_searcher.client.ws_opportunities_handler( - simple_searcher.opportunity_callback - ) - asyncio.create_task(ws_call) + await simple_searcher.client.subscribe_chains(args.chain_ids) - while True: - if simple_searcher.client.ws.closed: - logger.error("Websocket connection closed, exiting") - break - await asyncio.sleep(10) + await task if __name__ == "__main__": diff --git a/express_relay/sdk/python/pyproject.toml b/express_relay/sdk/python/pyproject.toml index 3765c946ea..84fd7c655e 100644 --- a/express_relay/sdk/python/pyproject.toml +++ b/express_relay/sdk/python/pyproject.toml @@ -18,8 +18,11 @@ argparse = "^1.4.0" [tool.poetry.group.dev.dependencies] black = "^24.1.1" -flake8 = "^7.0.0" -isort = "^5.13.2" +ruff = "^0.3.0" + +[tool.ruff.lint] +select = ["F", "E", "W", "I001"] +ignore = ["E501"] [build-system] requires = ["poetry-core>=1.0.0"] From a3a5961a5b4e7823544657a1b18303fd20200ced Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 17:20:37 -0500 Subject: [PATCH 23/58] test precommit change --- .github/workflows/ci-pre-commit.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index 0c98210fce..60c20870fb 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -13,7 +13,6 @@ jobs: with: # Need to grab the history of the PR fetch-depth: 0 - - uses: actions/setup-python@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal @@ -27,7 +26,7 @@ jobs: - uses: actions/checkout@v4 - name: Install poetry run: pipx install poetry - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v2 with: python-version: "3.11" cache: "poetry" From ef8a447dfa30251f75b64d55196bd0cca6f3d8c9 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 17:26:19 -0500 Subject: [PATCH 24/58] test precommit change --- .github/workflows/ci-pre-commit.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index 60c20870fb..a6fc4c8a77 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -13,6 +13,9 @@ jobs: with: # Need to grab the history of the PR fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: "3.11" - uses: actions-rs/toolchain@v1 with: profile: minimal @@ -25,7 +28,7 @@ jobs: components: rustfmt, clippy - uses: actions/checkout@v4 - name: Install poetry - run: pipx install poetry + run: python3 -m pip install poetry - uses: actions/setup-python@v2 with: python-version: "3.11" From 0252a7ddafdd463a12d9cb2c76d377490fc663dc Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 17:46:20 -0500 Subject: [PATCH 25/58] test precommit change --- .github/workflows/ci-pre-commit.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index a6fc4c8a77..838a5e325e 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -8,14 +8,14 @@ on: jobs: pre-commit: runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ '3.11' ] steps: - uses: actions/checkout@v3 with: # Need to grab the history of the PR fetch-depth: 0 - - uses: actions/setup-python@v2 - with: - python-version: "3.11" - uses: actions-rs/toolchain@v1 with: profile: minimal @@ -28,10 +28,14 @@ jobs: components: rustfmt, clippy - uses: actions/checkout@v4 - name: Install poetry - run: python3 -m pip install poetry + run: | + curl -O -sSL https://install.python-poetry.org/install-poetry.py + python install-poetry.py -y --version 1.1.12 + echo "PATH=${HOME}/.poetry/bin:${PATH}" >> $GITHUB_ENV + rm install-poetry.py - uses: actions/setup-python@v2 with: - python-version: "3.11" + python-version: ${{ matrix.python-version }} cache: "poetry" - uses: pre-commit/action@v3.0.0 if: ${{ github.event_name == 'pull_request' }} From 458f483b3e04b8ea9b83b3c3cc3bac535fdcd389 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 18:24:37 -0500 Subject: [PATCH 26/58] test precommit change --- .github/workflows/ci-pre-commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index 838a5e325e..c85f65b937 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -30,7 +30,7 @@ jobs: - name: Install poetry run: | curl -O -sSL https://install.python-poetry.org/install-poetry.py - python install-poetry.py -y --version 1.1.12 + python${{ matrix.python-version }} install-poetry.py -y --version 1.1.12 echo "PATH=${HOME}/.poetry/bin:${PATH}" >> $GITHUB_ENV rm install-poetry.py - uses: actions/setup-python@v2 From b0c086d68631e0f834dfb8b1800ce46ccdfccdf7 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 18:43:54 -0500 Subject: [PATCH 27/58] test precommit change --- .github/workflows/ci-pre-commit.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index c85f65b937..7c575f7e67 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -28,14 +28,10 @@ jobs: components: rustfmt, clippy - uses: actions/checkout@v4 - name: Install poetry - run: | - curl -O -sSL https://install.python-poetry.org/install-poetry.py - python${{ matrix.python-version }} install-poetry.py -y --version 1.1.12 - echo "PATH=${HOME}/.poetry/bin:${PATH}" >> $GITHUB_ENV - rm install-poetry.py + run: pipx install poetry - uses: actions/setup-python@v2 with: - python-version: ${{ matrix.python-version }} + python-version: "3.11" cache: "poetry" - uses: pre-commit/action@v3.0.0 if: ${{ github.event_name == 'pull_request' }} From a6b640e8564a4a37ec196b0396f5947b3f1d5ced Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 18:45:31 -0500 Subject: [PATCH 28/58] test precommit change --- .github/workflows/ci-pre-commit.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index 7c575f7e67..c111dd8279 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -8,9 +8,6 @@ on: jobs: pre-commit: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ '3.11' ] steps: - uses: actions/checkout@v3 with: @@ -31,7 +28,7 @@ jobs: run: pipx install poetry - uses: actions/setup-python@v2 with: - python-version: "3.11" + python-version: "3.10" cache: "poetry" - uses: pre-commit/action@v3.0.0 if: ${{ github.event_name == 'pull_request' }} From f70eefc8797750c2f25ffdd4070d816596ed4087 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 18:47:12 -0500 Subject: [PATCH 29/58] test precommit change --- .github/workflows/ci-pre-commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index c111dd8279..851b2258ec 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -28,7 +28,7 @@ jobs: run: pipx install poetry - uses: actions/setup-python@v2 with: - python-version: "3.10" + python-version: "3.10.12" cache: "poetry" - uses: pre-commit/action@v3.0.0 if: ${{ github.event_name == 'pull_request' }} From 7d263f05ec826a521618105ff74712486adb243a Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 18:52:59 -0500 Subject: [PATCH 30/58] test precommit change --- .github/workflows/ci-pre-commit.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index 851b2258ec..ec0d6b50d4 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -5,6 +5,10 @@ on: push: branches: [main] +env: + PYTHON_VERSION: "3.11" + POETRY_VERSION: "1.4.2" + jobs: pre-commit: runs-on: ubuntu-latest @@ -28,8 +32,13 @@ jobs: run: pipx install poetry - uses: actions/setup-python@v2 with: - python-version: "3.10.12" - cache: "poetry" + python-version: ${{ env.PYTHON_VERSION }} + id: setup_python + - name: Cache Poetry cache + uses: actions/cache@v3 + with: + path: ~/.cache/pypoetry + key: poetry-cache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}-${{ env.POETRY_VERSION }} - uses: pre-commit/action@v3.0.0 if: ${{ github.event_name == 'pull_request' }} with: From 09023412ff0bb10ee36fe4d7fb39942a2eac2008 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 18:59:37 -0500 Subject: [PATCH 31/58] test precommit change --- .github/workflows/ci-pre-commit.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index ec0d6b50d4..a757326e69 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -39,6 +39,9 @@ jobs: with: path: ~/.cache/pypoetry key: poetry-cache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}-${{ env.POETRY_VERSION }} + - name: Install poetry dependencies + run: poetry -C express_relay/sdk/python/express_relay_utils install + shell: sh - uses: pre-commit/action@v3.0.0 if: ${{ github.event_name == 'pull_request' }} with: From 028b870d3902ac9b812dbca8c227b14dd26469f9 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 19:17:44 -0500 Subject: [PATCH 32/58] correct directory for poetry in pypi wf --- .github/workflows/pypi-express-relay-utils.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pypi-express-relay-utils.yml b/.github/workflows/pypi-express-relay-utils.yml index 36ff186ac3..889d74a465 100644 --- a/.github/workflows/pypi-express-relay-utils.yml +++ b/.github/workflows/pypi-express-relay-utils.yml @@ -14,8 +14,8 @@ jobs: - name: Install dependencies run: | python3 -m pip install --upgrade poetry - poetry build + poetry -C express_relay/sdk/python/express_relay_utils build - name: Build and publish run: | - poetry build - poetry publish --username __token__ --password ${{ secrets.PYPI_TOKEN }} + poetry -C express_relay/sdk/python/express_relay_utils build + poetry -C express_relay/sdk/python/express_relay_utils publish --username __token__ --password ${{ secrets.PYPI_TOKEN }} From 6b8bc9fbc287372d5fc466c509c01194133b22a8 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 19:19:29 -0500 Subject: [PATCH 33/58] remove isort --- .pre-commit-config.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8ab7199b5c..09919ffd2a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -112,11 +112,6 @@ repos: pass_filenames: false files: target_chains/solana # For express relay python files - # - id: isort - # name: isort - # entry: poetry -C express_relay/sdk/python/express_relay_utils run isort --profile=black express_relay/sdk/python/express_relay_utils --skip openapi_client - # pass_filenames: false - # language: system - id: black name: black entry: poetry -C express_relay/sdk/python/express_relay_utils run black express_relay/sdk/python/express_relay_utils --exclude openapi_client From 20bdd30e729ad2b74081040353b70c4e64d1e0a5 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 19:32:19 -0500 Subject: [PATCH 34/58] rename package --- express_relay/sdk/python/README.md | 12 +++++++++++- express_relay/sdk/python/pyproject.toml | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/express_relay/sdk/python/README.md b/express_relay/sdk/python/README.md index 17aa736bc6..0e12eb094d 100644 --- a/express_relay/sdk/python/README.md +++ b/express_relay/sdk/python/README.md @@ -8,8 +8,18 @@ The SDK includes searcher-side utilities and protocol-side utilities. The search ## Installation +### pip + +``` +$ pip install express-relay-utils +``` + ### poetry +``` +$ poetry add express-relay-utils +``` + ### openapi-generator You can use the `openapi-generator` command line tool to auto generate the types you will need for the client. To install, run @@ -18,7 +28,7 @@ You can use the `openapi-generator` command line tool to auto generate the types $ brew install openapi-generator ``` -To generate the types from the Express Relay server, run +To generate the types from the Express Relay server, navigate to `express_relay_utils/` and run ``` $ openapi-generator generate -i https://per-staging.dourolabs.app/docs/openapi.json -g python --additional-properties=generateSourceCodeOnly=true --global-property models,modelTests=false,modelDocs=false diff --git a/express_relay/sdk/python/pyproject.toml b/express_relay/sdk/python/pyproject.toml index 84fd7c655e..638b8811e1 100644 --- a/express_relay/sdk/python/pyproject.toml +++ b/express_relay/sdk/python/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "express_relay_utils" +name = "express-relay-utils" version = "0.0.1" description = "Utilities for searchers and protocols to interact with the express relay protocol." authors = ["dourolabs"] From d62498d6de724f5fc5bc1f8b8514f351570e1fde Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 6 Mar 2024 23:38:40 -0500 Subject: [PATCH 35/58] add UUID and some cleanup --- .../express_relay_client.py | 35 ++++++++++--------- .../searcher/examples/simple_searcher.py | 20 ++++------- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 046c4bcf9c..95c08a4a82 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -2,6 +2,7 @@ import json import urllib.parse from typing import Callable +from uuid import UUID import httpx import web3 @@ -18,7 +19,7 @@ class BidInfo: - def __init__(self, opportunity_id: str, opportunity_bid: OpportunityBid): + def __init__(self, opportunity_id: UUID, opportunity_bid: OpportunityBid): self.opportunity_id = opportunity_id self.opportunity_bid = opportunity_bid @@ -56,6 +57,7 @@ async def start_ws( Initializes the websocket connection to the server, if not already connected. Args: + opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. kwargs: Keyword arguments to pass to the websocket connection. Returns: The websocket task. @@ -126,14 +128,14 @@ async def send_ws_message(self, msg: dict): self.ws_msg_counter += 1 future = asyncio.get_event_loop().create_future() + self.ws_msg_futures[msg["id"]] = future await self.ws.send(json.dumps(msg)) - self.ws_msg_futures[msg["id"]] = future - # await the response msg for the subscription from the server + # await the response for the subscription from the server msg = await future - self.process_msg(msg) + self.process_response_msg(msg) async def subscribe_chains(self, chain_ids: list[str]): """ @@ -165,16 +167,16 @@ async def unsubscribe_chains(self, chain_ids: list[str]): } await self.send_ws_message(json_unsubscribe) - def process_msg(self, msg: dict): + def process_response_msg(self, msg: dict): """ - Processes a message received from the server via websocket. + Processes a response message received from the server via websocket. Args: msg: The message to process. """ if msg.get("status") and msg.get("status") != "success": raise ExpressRelayClientException( - f"Error in websocket subscription: {msg.get('result')}" + f"Error in websocket with message id {msg.get('id')}: {msg.get('result')}" ) async def ws_handler( @@ -197,19 +199,18 @@ async def ws_handler( if msg.get("id"): future = self.ws_msg_futures.pop(msg["id"]) future.set_result(msg) - else: - self.process_msg(msg) if msg.get("type") == "new_opportunity": - opportunity = msg["opportunity"] - opportunity = OpportunityParamsWithMetadata.from_dict(opportunity) + opportunity = OpportunityParamsWithMetadata.from_dict( + msg["opportunity"] + ) if opportunity_callback is not None: asyncio.create_task(opportunity_callback(opportunity)) async def submit_opportunity( self, opportunity: OpportunityParams, timeout: int = 10 - ) -> str: + ) -> UUID: """ Submits an opportunity to the liquidation server. @@ -228,9 +229,9 @@ async def submit_opportunity( timeout=timeout, ) resp.raise_for_status() - return resp.json()["opportunity_id"] + return UUID(resp.json()["opportunity_id"]) - async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> str: + async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> UUID: """ Submits a bid to the liquidation server. @@ -244,14 +245,14 @@ async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> str: resp = await client.post( urllib.parse.urlparse(self.server_url) ._replace( - path=f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids" + path=f"/v1/liquidation/opportunities/{str(bid_info.opportunity_id)}/bids" ) .geturl(), json=bid_info.opportunity_bid.to_dict(), timeout=timeout, ) resp.raise_for_status() - return resp.json()["id"] + return UUID(resp.json()["id"]) def sign_bid( @@ -314,7 +315,7 @@ def sign_bid( opportunity_bid = OpportunityBid.from_dict(opportunity_bid) bid_info = BidInfo( - opportunity_id=liquidation_opportunity.opportunity_id, + opportunity_id=UUID(liquidation_opportunity.opportunity_id), opportunity_bid=opportunity_bid, ) diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index c33f426e34..a7bc8c9f9a 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -10,16 +10,16 @@ logger = logging.getLogger(__name__) +NAIVE_BID = 10 # Set validity (naively) to max uint256 VALID_UNTIL_MAX = 2**256 - 1 class SimpleSearcher: - def __init__(self, server_url: str, private_key: str, default_bid: int): + def __init__(self, server_url: str, private_key: str): self.client = ExpressRelayClient(server_url) self.private_key = private_key self.liquidator = Account.from_key(private_key).address - self.default_bid = default_bid def assess_liquidation_opportunity( self, @@ -31,13 +31,13 @@ def assess_liquidation_opportunity( This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the liquidation. There are many ways to evaluate this, but the most common way is to check that the value of the amount the searcher will receive from the liquidation exceeds the value of the amount repaid. Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation. - In this simple searcher, the function always (naively) returns a BidInfo object with the default bid and a valid_until timestamp. + In this simple searcher, the function always (naively) returns a BidInfo object with a default bid and valid_until timestamp. Args: opp: A OpportunityParamsWithMetadata object, representing a single liquidation opportunity. Returns: If the opportunity is deemed worthwhile, this function can return a BidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. """ - bid_info = sign_bid(opp, self.default_bid, VALID_UNTIL_MAX, self.private_key) + bid_info = sign_bid(opp, NAIVE_BID, VALID_UNTIL_MAX, self.private_key) return bid_info @@ -53,11 +53,11 @@ async def opportunity_callback(self, opp: OpportunityParamsWithMetadata): try: await self.client.submit_bid(bid_info) logger.info( - f"Submitted bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}" + f"Submitted bid amount {bid_info.opportunity_bid.amount} for opportunity {str(bid_info.opportunity_id)}" ) except Exception as e: logger.error( - f"Error submitting bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}: {e}" + f"Error submitting bid amount {bid_info.opportunity_bid.amount} for opportunity {str(bid_info.opportunity_id)}: {e}" ) @@ -77,12 +77,6 @@ async def main(): nargs="+", help="Chain ID(s) of the network(s) to monitor for liquidation opportunities", ) - parser.add_argument( - "--bid", - type=int, - default=10, - help="Default amount of bid for liquidation opportunities", - ) parser.add_argument( "--server-url", type=str, @@ -102,7 +96,7 @@ async def main(): sk_liquidator = args.private_key - simple_searcher = SimpleSearcher(args.server_url, sk_liquidator, args.bid) + simple_searcher = SimpleSearcher(args.server_url, sk_liquidator) logger.info("Liquidator address: %s", simple_searcher.liquidator) task = await simple_searcher.client.start_ws(simple_searcher.opportunity_callback) From 6af2df14d681973664db2a203717d2d7f59e793f Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 7 Mar 2024 20:30:49 -0500 Subject: [PATCH 36/58] new openapi typings --- .../openapi_client/models/api_response.py | 123 ++++++++++++++ .../openapi_client/models/api_resposne.py | 123 ++++++++++++++ .../openapi_client/models/bid.py | 4 +- .../openapi_client/models/bid400_response.py | 4 +- .../openapi_client/models/bid_result.py | 10 +- .../openapi_client/models/bid_status.py | 154 ++++++++++++++++++ .../models/bid_status_one_of.py | 94 +++++++++++ .../models/bid_status_one_of1.py | 96 +++++++++++ .../models/bid_status_one_of2.py | 94 +++++++++++ .../models/bid_status_with_id.py | 93 +++++++++++ .../models/bid_status_with_id_bid_status.py | 154 ++++++++++++++++++ .../models/bid_status_with_id_status.py | 154 ++++++++++++++++++ .../openapi_client/models/client_message.py | 48 +++++- .../models/client_message_one_of.py | 4 +- .../models/client_message_one_of1.py | 4 +- .../models/client_message_one_of2.py | 100 ++++++++++++ .../models/client_message_one_of2_params.py | 91 +++++++++++ .../models/client_message_one_of3.py | 100 ++++++++++++ .../models/client_message_one_of3_params.py | 93 +++++++++++ .../models/client_message_one_of_params.py | 4 +- .../openapi_client/models/client_request.py | 8 +- .../models/error_body_response.py | 4 +- .../openapi_client/models/opportunity_bid.py | 4 +- .../models/opportunity_params.py | 4 +- .../models/opportunity_params_one_of.py | 4 +- .../models/opportunity_params_v1.py | 4 +- .../opportunity_params_with_metadata.py | 4 +- .../models/server_result_message.py | 4 +- .../models/server_result_message_one_of.py | 19 ++- .../models/server_result_message_one_of1.py | 4 +- .../models/server_result_response.py | 8 +- .../models/server_update_response.py | 34 +++- .../models/server_update_response_one_of.py | 4 +- .../models/server_update_response_one_of1.py | 100 ++++++++++++ .../openapi_client/models/token_qty.py | 4 +- 35 files changed, 1711 insertions(+), 45 deletions(-) create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py new file mode 100644 index 0000000000..e60f023cad --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py @@ -0,0 +1,123 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.bid_result import BidResult +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +APIRESPONSE_ONE_OF_SCHEMAS = ["BidResult"] + +class APIResponse(BaseModel): + """ + APIResponse + """ + # data type: BidResult + oneof_schema_1_validator: Optional[BidResult] = None + actual_instance: Optional[Union[BidResult]] = None + one_of_schemas: List[str] = Field(default=Literal["BidResult"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = APIResponse.model_construct() + error_messages = [] + match = 0 + # validate data type: BidResult + if not isinstance(v, BidResult): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidResult`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into BidResult + try: + instance.actual_instance = BidResult.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py new file mode 100644 index 0000000000..264d3d7823 --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py @@ -0,0 +1,123 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.bid_result import BidResult +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +APIRESPOSNE_ONE_OF_SCHEMAS = ["BidResult"] + +class APIResposne(BaseModel): + """ + APIResposne + """ + # data type: BidResult + oneof_schema_1_validator: Optional[BidResult] = None + actual_instance: Optional[Union[BidResult]] = None + one_of_schemas: List[str] = Field(default=Literal["BidResult"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = APIResposne.model_construct() + error_messages = [] + match = 0 + # validate data type: BidResult + if not isinstance(v, BidResult): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidResult`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in APIResposne with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in APIResposne with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into BidResult + try: + instance.actual_instance = BidResult.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into APIResposne with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into APIResposne with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py index 56be015658..ca8ac69cb9 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -91,3 +91,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "permission_key": obj.get("permission_key") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py index 8777cdcdd7..4f105c4701 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py index 4e69eba6ed..7a8831d97e 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -17,7 +17,7 @@ import re # noqa: F401 import json -from pydantic import BaseModel, StrictStr +from pydantic import BaseModel, Field, StrictStr from typing import Any, ClassVar, Dict, List from typing import Optional, Set from typing_extensions import Self @@ -26,8 +26,9 @@ class BidResult(BaseModel): """ BidResult """ # noqa: E501 + id: StrictStr = Field(description="The unique id created to identify the bid. This id can be used to query the status of the bid.") status: StrictStr - __properties: ClassVar[List[str]] = ["status"] + __properties: ClassVar[List[str]] = ["id", "status"] model_config = { "populate_by_name": True, @@ -80,6 +81,9 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ + "id": obj.get("id"), "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py new file mode 100644 index 0000000000..0e36c51c10 --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py @@ -0,0 +1,154 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.bid_status_one_of import BidStatusOneOf +from openapi_client.models.bid_status_one_of1 import BidStatusOneOf1 +from openapi_client.models.bid_status_one_of2 import BidStatusOneOf2 +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +BIDSTATUS_ONE_OF_SCHEMAS = ["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"] + +class BidStatus(BaseModel): + """ + BidStatus + """ + # data type: BidStatusOneOf + oneof_schema_1_validator: Optional[BidStatusOneOf] = None + # data type: BidStatusOneOf1 + oneof_schema_2_validator: Optional[BidStatusOneOf1] = None + # data type: BidStatusOneOf2 + oneof_schema_3_validator: Optional[BidStatusOneOf2] = None + actual_instance: Optional[Union[BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]] = None + one_of_schemas: List[str] = Field(default=Literal["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + discriminator_value_class_map: Dict[str, str] = { + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = BidStatus.model_construct() + error_messages = [] + match = 0 + # validate data type: BidStatusOneOf + if not isinstance(v, BidStatusOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf`") + else: + match += 1 + # validate data type: BidStatusOneOf1 + if not isinstance(v, BidStatusOneOf1): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf1`") + else: + match += 1 + # validate data type: BidStatusOneOf2 + if not isinstance(v, BidStatusOneOf2): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf2`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in BidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in BidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into BidStatusOneOf + try: + instance.actual_instance = BidStatusOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into BidStatusOneOf1 + try: + instance.actual_instance = BidStatusOneOf1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into BidStatusOneOf2 + try: + instance.actual_instance = BidStatusOneOf2.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into BidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into BidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py new file mode 100644 index 0000000000..6fc96fc6d5 --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py @@ -0,0 +1,94 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class BidStatusOneOf(BaseModel): + """ + BidStatusOneOf + """ # noqa: E501 + status: StrictStr + __properties: ClassVar[List[str]] = ["status"] + + @field_validator('status') + def status_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['pending']): + raise ValueError("must be one of enum values ('pending')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BidStatusOneOf from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BidStatusOneOf from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "status": obj.get("status") + }) + return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py new file mode 100644 index 0000000000..abd0dd479c --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py @@ -0,0 +1,96 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, Field, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class BidStatusOneOf1(BaseModel): + """ + BidStatusOneOf1 + """ # noqa: E501 + result: StrictStr = Field(description="The bid won the auction and was submitted to the chain in a transaction with the given hash") + status: StrictStr + __properties: ClassVar[List[str]] = ["result", "status"] + + @field_validator('status') + def status_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['submitted']): + raise ValueError("must be one of enum values ('submitted')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BidStatusOneOf1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BidStatusOneOf1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "result": obj.get("result"), + "status": obj.get("status") + }) + return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py new file mode 100644 index 0000000000..205db3dbed --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py @@ -0,0 +1,94 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self + +class BidStatusOneOf2(BaseModel): + """ + BidStatusOneOf2 + """ # noqa: E501 + status: StrictStr + __properties: ClassVar[List[str]] = ["status"] + + @field_validator('status') + def status_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['lost']): + raise ValueError("must be one of enum values ('lost')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BidStatusOneOf2 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BidStatusOneOf2 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "status": obj.get("status") + }) + return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py new file mode 100644 index 0000000000..e7bbee11fa --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py @@ -0,0 +1,93 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr +from typing import Any, ClassVar, Dict, List +from openapi_client.models.bid_status import BidStatus +from typing import Optional, Set +from typing_extensions import Self + +class BidStatusWithId(BaseModel): + """ + BidStatusWithId + """ # noqa: E501 + bid_status: BidStatus + id: StrictStr + __properties: ClassVar[List[str]] = ["bid_status", "id"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BidStatusWithId from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of bid_status + if self.bid_status: + _dict['bid_status'] = self.bid_status.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BidStatusWithId from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "bid_status": BidStatus.from_dict(obj["bid_status"]) if obj.get("bid_status") is not None else None, + "id": obj.get("id") + }) + return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py new file mode 100644 index 0000000000..40bfad1898 --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py @@ -0,0 +1,154 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.bid_status_one_of import BidStatusOneOf +from openapi_client.models.bid_status_one_of1 import BidStatusOneOf1 +from openapi_client.models.bid_status_one_of2 import BidStatusOneOf2 +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +BIDSTATUSWITHIDBIDSTATUS_ONE_OF_SCHEMAS = ["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"] + +class BidStatusWithIdBidStatus(BaseModel): + """ + BidStatusWithIdBidStatus + """ + # data type: BidStatusOneOf + oneof_schema_1_validator: Optional[BidStatusOneOf] = None + # data type: BidStatusOneOf1 + oneof_schema_2_validator: Optional[BidStatusOneOf1] = None + # data type: BidStatusOneOf2 + oneof_schema_3_validator: Optional[BidStatusOneOf2] = None + actual_instance: Optional[Union[BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]] = None + one_of_schemas: List[str] = Field(default=Literal["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + discriminator_value_class_map: Dict[str, str] = { + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = BidStatusWithIdBidStatus.model_construct() + error_messages = [] + match = 0 + # validate data type: BidStatusOneOf + if not isinstance(v, BidStatusOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf`") + else: + match += 1 + # validate data type: BidStatusOneOf1 + if not isinstance(v, BidStatusOneOf1): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf1`") + else: + match += 1 + # validate data type: BidStatusOneOf2 + if not isinstance(v, BidStatusOneOf2): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf2`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in BidStatusWithIdBidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in BidStatusWithIdBidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into BidStatusOneOf + try: + instance.actual_instance = BidStatusOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into BidStatusOneOf1 + try: + instance.actual_instance = BidStatusOneOf1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into BidStatusOneOf2 + try: + instance.actual_instance = BidStatusOneOf2.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into BidStatusWithIdBidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into BidStatusWithIdBidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py new file mode 100644 index 0000000000..66fee6cdcd --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py @@ -0,0 +1,154 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.bid_status_one_of import BidStatusOneOf +from openapi_client.models.bid_status_one_of1 import BidStatusOneOf1 +from openapi_client.models.bid_status_one_of2 import BidStatusOneOf2 +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +BIDSTATUSWITHIDSTATUS_ONE_OF_SCHEMAS = ["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"] + +class BidStatusWithIdStatus(BaseModel): + """ + BidStatusWithIdStatus + """ + # data type: BidStatusOneOf + oneof_schema_1_validator: Optional[BidStatusOneOf] = None + # data type: BidStatusOneOf1 + oneof_schema_2_validator: Optional[BidStatusOneOf1] = None + # data type: BidStatusOneOf2 + oneof_schema_3_validator: Optional[BidStatusOneOf2] = None + actual_instance: Optional[Union[BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]] = None + one_of_schemas: List[str] = Field(default=Literal["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + discriminator_value_class_map: Dict[str, str] = { + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = BidStatusWithIdStatus.model_construct() + error_messages = [] + match = 0 + # validate data type: BidStatusOneOf + if not isinstance(v, BidStatusOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf`") + else: + match += 1 + # validate data type: BidStatusOneOf1 + if not isinstance(v, BidStatusOneOf1): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf1`") + else: + match += 1 + # validate data type: BidStatusOneOf2 + if not isinstance(v, BidStatusOneOf2): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf2`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in BidStatusWithIdStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in BidStatusWithIdStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into BidStatusOneOf + try: + instance.actual_instance = BidStatusOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into BidStatusOneOf1 + try: + instance.actual_instance = BidStatusOneOf1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into BidStatusOneOf2 + try: + instance.actual_instance = BidStatusOneOf2.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into BidStatusWithIdStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into BidStatusWithIdStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py index 997d3275aa..aa9a65e46c 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -19,11 +19,13 @@ from typing import Any, List, Optional from openapi_client.models.client_message_one_of import ClientMessageOneOf from openapi_client.models.client_message_one_of1 import ClientMessageOneOf1 +from openapi_client.models.client_message_one_of2 import ClientMessageOneOf2 +from openapi_client.models.client_message_one_of3 import ClientMessageOneOf3 from pydantic import StrictStr, Field from typing import Union, List, Optional, Dict from typing_extensions import Literal, Self -CLIENTMESSAGE_ONE_OF_SCHEMAS = ["ClientMessageOneOf", "ClientMessageOneOf1"] +CLIENTMESSAGE_ONE_OF_SCHEMAS = ["ClientMessageOneOf", "ClientMessageOneOf1", "ClientMessageOneOf2", "ClientMessageOneOf3"] class ClientMessage(BaseModel): """ @@ -33,8 +35,12 @@ class ClientMessage(BaseModel): oneof_schema_1_validator: Optional[ClientMessageOneOf] = None # data type: ClientMessageOneOf1 oneof_schema_2_validator: Optional[ClientMessageOneOf1] = None - actual_instance: Optional[Union[ClientMessageOneOf, ClientMessageOneOf1]] = None - one_of_schemas: List[str] = Field(default=Literal["ClientMessageOneOf", "ClientMessageOneOf1"]) + # data type: ClientMessageOneOf2 + oneof_schema_3_validator: Optional[ClientMessageOneOf2] = None + # data type: ClientMessageOneOf3 + oneof_schema_4_validator: Optional[ClientMessageOneOf3] = None + actual_instance: Optional[Union[ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3]] = None + one_of_schemas: List[str] = Field(default=Literal["ClientMessageOneOf", "ClientMessageOneOf1", "ClientMessageOneOf2", "ClientMessageOneOf3"]) model_config = { "validate_assignment": True, @@ -71,12 +77,22 @@ def actual_instance_must_validate_oneof(cls, v): error_messages.append(f"Error! Input type `{type(v)}` is not `ClientMessageOneOf1`") else: match += 1 + # validate data type: ClientMessageOneOf2 + if not isinstance(v, ClientMessageOneOf2): + error_messages.append(f"Error! Input type `{type(v)}` is not `ClientMessageOneOf2`") + else: + match += 1 + # validate data type: ClientMessageOneOf3 + if not isinstance(v, ClientMessageOneOf3): + error_messages.append(f"Error! Input type `{type(v)}` is not `ClientMessageOneOf3`") + else: + match += 1 if match > 1: # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1. Details: " + ", ".join(error_messages)) + raise ValueError("Multiple matches found when setting `actual_instance` in ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3. Details: " + ", ".join(error_messages)) elif match == 0: # no match - raise ValueError("No match found when setting `actual_instance` in ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1. Details: " + ", ".join(error_messages)) + raise ValueError("No match found when setting `actual_instance` in ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3. Details: " + ", ".join(error_messages)) else: return v @@ -103,13 +119,25 @@ def from_json(cls, json_str: str) -> Self: match += 1 except (ValidationError, ValueError) as e: error_messages.append(str(e)) + # deserialize data into ClientMessageOneOf2 + try: + instance.actual_instance = ClientMessageOneOf2.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into ClientMessageOneOf3 + try: + instance.actual_instance = ClientMessageOneOf3.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) if match > 1: # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1. Details: " + ", ".join(error_messages)) + raise ValueError("Multiple matches found when deserializing the JSON string into ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3. Details: " + ", ".join(error_messages)) elif match == 0: # no match - raise ValueError("No match found when deserializing the JSON string into ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1. Details: " + ", ".join(error_messages)) + raise ValueError("No match found when deserializing the JSON string into ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3. Details: " + ", ".join(error_messages)) else: return instance @@ -123,7 +151,7 @@ def to_json(self) -> str: else: return json.dumps(self.actual_instance) - def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMessageOneOf1]]: + def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3]]: """Returns the dict representation of the actual instance""" if self.actual_instance is None: return None @@ -137,3 +165,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMe def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py index 3b1dc79308..70c89a2b25 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py index 2fb72ca279..7afdd366a2 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py new file mode 100644 index 0000000000..d62f1859b3 --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py @@ -0,0 +1,100 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from openapi_client.models.client_message_one_of2_params import ClientMessageOneOf2Params +from typing import Optional, Set +from typing_extensions import Self + +class ClientMessageOneOf2(BaseModel): + """ + ClientMessageOneOf2 + """ # noqa: E501 + method: StrictStr + params: ClientMessageOneOf2Params + __properties: ClassVar[List[str]] = ["method", "params"] + + @field_validator('method') + def method_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['post_bid']): + raise ValueError("must be one of enum values ('post_bid')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ClientMessageOneOf2 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of params + if self.params: + _dict['params'] = self.params.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ClientMessageOneOf2 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "method": obj.get("method"), + "params": ClientMessageOneOf2Params.from_dict(obj["params"]) if obj.get("params") is not None else None + }) + return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py new file mode 100644 index 0000000000..1b76bacf93 --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py @@ -0,0 +1,91 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel +from typing import Any, ClassVar, Dict, List +from openapi_client.models.bid import Bid +from typing import Optional, Set +from typing_extensions import Self + +class ClientMessageOneOf2Params(BaseModel): + """ + ClientMessageOneOf2Params + """ # noqa: E501 + bid: Bid + __properties: ClassVar[List[str]] = ["bid"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ClientMessageOneOf2Params from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of bid + if self.bid: + _dict['bid'] = self.bid.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ClientMessageOneOf2Params from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "bid": Bid.from_dict(obj["bid"]) if obj.get("bid") is not None else None + }) + return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py new file mode 100644 index 0000000000..7ae631db79 --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py @@ -0,0 +1,100 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from openapi_client.models.client_message_one_of3_params import ClientMessageOneOf3Params +from typing import Optional, Set +from typing_extensions import Self + +class ClientMessageOneOf3(BaseModel): + """ + ClientMessageOneOf3 + """ # noqa: E501 + method: StrictStr + params: ClientMessageOneOf3Params + __properties: ClassVar[List[str]] = ["method", "params"] + + @field_validator('method') + def method_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['post_liquidation_bid']): + raise ValueError("must be one of enum values ('post_liquidation_bid')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ClientMessageOneOf3 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of params + if self.params: + _dict['params'] = self.params.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ClientMessageOneOf3 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "method": obj.get("method"), + "params": ClientMessageOneOf3Params.from_dict(obj["params"]) if obj.get("params") is not None else None + }) + return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py new file mode 100644 index 0000000000..325f0de00b --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py @@ -0,0 +1,93 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr +from typing import Any, ClassVar, Dict, List +from openapi_client.models.opportunity_bid import OpportunityBid +from typing import Optional, Set +from typing_extensions import Self + +class ClientMessageOneOf3Params(BaseModel): + """ + ClientMessageOneOf3Params + """ # noqa: E501 + opportunity_bid: OpportunityBid + opportunity_id: StrictStr + __properties: ClassVar[List[str]] = ["opportunity_bid", "opportunity_id"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ClientMessageOneOf3Params from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of opportunity_bid + if self.opportunity_bid: + _dict['opportunity_bid'] = self.opportunity_bid.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ClientMessageOneOf3Params from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "opportunity_bid": OpportunityBid.from_dict(obj["opportunity_bid"]) if obj.get("opportunity_bid") is not None else None, + "opportunity_id": obj.get("opportunity_id") + }) + return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py index 1676ed421e..c4b59cf971 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "chain_ids": obj.get("chain_ids") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py index 119ea52fcf..6ca1f7a88b 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -20,7 +20,7 @@ from pydantic import StrictStr from typing import Any, ClassVar, Dict, List from openapi_client.models.client_message import ClientMessage -from openapi_client.models.client_message_one_of_params import ClientMessageOneOfParams +from openapi_client.models.client_message_one_of3_params import ClientMessageOneOf3Params from typing import Optional, Set from typing_extensions import Self @@ -86,7 +86,9 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "method": obj.get("method"), - "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None, + "params": ClientMessageOneOf3Params.from_dict(obj["params"]) if obj.get("params") is not None else None, "id": obj.get("id") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py index 4804dcd884..7dc15e20e5 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py index d73b3f907a..5e4432b5e6 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -91,3 +91,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "valid_until": obj.get("valid_until") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py index ce12170d5c..f3aeaad06a 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -122,3 +122,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], OpportunityParamsOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py index 2d09d3b3aa..c346501c27 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -119,3 +119,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "version": obj.get("version") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py index db9db72476..9fe3b11a38 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -110,3 +110,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "value": obj.get("value") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py index a2cb12e7ab..524caa68fb 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -123,3 +123,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "opportunity_id": obj.get("opportunity_id") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py index 63dcfc5369..1654f23f2e 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -137,3 +137,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerResultMessageOneOf, Se def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py index 2f279fd945..49523bff2a 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -18,7 +18,8 @@ import json from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List +from typing import Any, ClassVar, Dict, List, Optional +from openapi_client.models.api_response import APIResponse from typing import Optional, Set from typing_extensions import Self @@ -26,8 +27,9 @@ class ServerResultMessageOneOf(BaseModel): """ ServerResultMessageOneOf """ # noqa: E501 + result: Optional[APIResponse] status: StrictStr - __properties: ClassVar[List[str]] = ["status"] + __properties: ClassVar[List[str]] = ["result", "status"] @field_validator('status') def status_validate_enum(cls, value): @@ -75,6 +77,14 @@ def to_dict(self) -> Dict[str, Any]: exclude=excluded_fields, exclude_none=True, ) + # override the default output from pydantic by calling `to_dict()` of result + if self.result: + _dict['result'] = self.result.to_dict() + # set to None if result (nullable) is None + # and model_fields_set contains the field + if self.result is None and "result" in self.model_fields_set: + _dict['result'] = None + return _dict @classmethod @@ -87,6 +97,9 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ + "result": APIResponse.from_dict(obj["result"]) if obj.get("result") is not None else None, "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py index dd60aff854..dc84c41f8e 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -92,3 +92,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py index 9cd548d03b..a761094f09 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -28,7 +28,7 @@ class ServerResultResponse(ServerResultMessage): This enum is used to send the result for a specific client request with the same id id is only None when the client message is invalid """ # noqa: E501 id: Optional[StrictStr] = None - __properties: ClassVar[List[str]] = ["status", "result", "id"] + __properties: ClassVar[List[str]] = ["result", "status", "id"] model_config = { "populate_by_name": True, @@ -86,8 +86,10 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ - "status": obj.get("status"), "result": obj.get("result"), + "status": obj.get("status"), "id": obj.get("id") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py index 23ad99afce..ff8efa7136 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -18,11 +18,12 @@ from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator from typing import Any, List, Optional from openapi_client.models.server_update_response_one_of import ServerUpdateResponseOneOf +from openapi_client.models.server_update_response_one_of1 import ServerUpdateResponseOneOf1 from pydantic import StrictStr, Field from typing import Union, List, Optional, Dict from typing_extensions import Literal, Self -SERVERUPDATERESPONSE_ONE_OF_SCHEMAS = ["ServerUpdateResponseOneOf"] +SERVERUPDATERESPONSE_ONE_OF_SCHEMAS = ["ServerUpdateResponseOneOf", "ServerUpdateResponseOneOf1"] class ServerUpdateResponse(BaseModel): """ @@ -30,8 +31,10 @@ class ServerUpdateResponse(BaseModel): """ # data type: ServerUpdateResponseOneOf oneof_schema_1_validator: Optional[ServerUpdateResponseOneOf] = None - actual_instance: Optional[Union[ServerUpdateResponseOneOf]] = None - one_of_schemas: List[str] = Field(default=Literal["ServerUpdateResponseOneOf"]) + # data type: ServerUpdateResponseOneOf1 + oneof_schema_2_validator: Optional[ServerUpdateResponseOneOf1] = None + actual_instance: Optional[Union[ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1]] = None + one_of_schemas: List[str] = Field(default=Literal["ServerUpdateResponseOneOf", "ServerUpdateResponseOneOf1"]) model_config = { "validate_assignment": True, @@ -62,12 +65,17 @@ def actual_instance_must_validate_oneof(cls, v): error_messages.append(f"Error! Input type `{type(v)}` is not `ServerUpdateResponseOneOf`") else: match += 1 + # validate data type: ServerUpdateResponseOneOf1 + if not isinstance(v, ServerUpdateResponseOneOf1): + error_messages.append(f"Error! Input type `{type(v)}` is not `ServerUpdateResponseOneOf1`") + else: + match += 1 if match > 1: # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf. Details: " + ", ".join(error_messages)) + raise ValueError("Multiple matches found when setting `actual_instance` in ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1. Details: " + ", ".join(error_messages)) elif match == 0: # no match - raise ValueError("No match found when setting `actual_instance` in ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf. Details: " + ", ".join(error_messages)) + raise ValueError("No match found when setting `actual_instance` in ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1. Details: " + ", ".join(error_messages)) else: return v @@ -88,13 +96,19 @@ def from_json(cls, json_str: str) -> Self: match += 1 except (ValidationError, ValueError) as e: error_messages.append(str(e)) + # deserialize data into ServerUpdateResponseOneOf1 + try: + instance.actual_instance = ServerUpdateResponseOneOf1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) if match > 1: # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf. Details: " + ", ".join(error_messages)) + raise ValueError("Multiple matches found when deserializing the JSON string into ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1. Details: " + ", ".join(error_messages)) elif match == 0: # no match - raise ValueError("No match found when deserializing the JSON string into ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf. Details: " + ", ".join(error_messages)) + raise ValueError("No match found when deserializing the JSON string into ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1. Details: " + ", ".join(error_messages)) else: return instance @@ -108,7 +122,7 @@ def to_json(self) -> str: else: return json.dumps(self.actual_instance) - def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf]]: + def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1]]: """Returns the dict representation of the actual instance""" if self.actual_instance is None: return None @@ -122,3 +136,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py index 87e0ef5397..160146ea35 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "type": obj.get("type") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py new file mode 100644 index 0000000000..731c607438 --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py @@ -0,0 +1,100 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List +from openapi_client.models.bid_status_with_id import BidStatusWithId +from typing import Optional, Set +from typing_extensions import Self + +class ServerUpdateResponseOneOf1(BaseModel): + """ + ServerUpdateResponseOneOf1 + """ # noqa: E501 + status: BidStatusWithId + type: StrictStr + __properties: ClassVar[List[str]] = ["status", "type"] + + @field_validator('type') + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in set(['bid_status_update']): + raise ValueError("must be one of enum values ('bid_status_update')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ServerUpdateResponseOneOf1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of status + if self.status: + _dict['status'] = self.status.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ServerUpdateResponseOneOf1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "status": BidStatusWithId.from_dict(obj["status"]) if obj.get("status") is not None else None, + "type": obj.get("type") + }) + return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py index 1356639fc7..643c93bbfc 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py @@ -5,7 +5,7 @@ No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - The version of the OpenAPI document: 0.1.5 + The version of the OpenAPI document: 0.1.6 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -85,3 +85,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "contract": obj.get("contract") }) return _obj + + From 4b277560edbcf5e4ec805681997627ecf0087781 Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 7 Mar 2024 20:31:17 -0500 Subject: [PATCH 37/58] add pydantic to pyproj --- express_relay/sdk/python/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/express_relay/sdk/python/pyproject.toml b/express_relay/sdk/python/pyproject.toml index 638b8811e1..7760fff7fa 100644 --- a/express_relay/sdk/python/pyproject.toml +++ b/express_relay/sdk/python/pyproject.toml @@ -15,6 +15,7 @@ httpx = "^0.23.3" websockets = "^11.0.3" asyncio = "^3.4.3" argparse = "^1.4.0" +pydantic = "^2.6.3" [tool.poetry.group.dev.dependencies] black = "^24.1.1" From 342196e5cf2444770a8b2f7c4741da5d9cc41e8a Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 7 Mar 2024 20:31:30 -0500 Subject: [PATCH 38/58] more changes --- .../express_relay_client.py | 189 ++++++++++-------- .../searcher/examples/simple_searcher.py | 51 +++-- 2 files changed, 136 insertions(+), 104 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 95c08a4a82..daf4684dbc 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -11,25 +11,44 @@ from eth_account.account import Account from openapi_client.models.client_message import ClientMessage from openapi_client.models.opportunity_bid import OpportunityBid -from openapi_client.models.opportunity_params import OpportunityParams from openapi_client.models.opportunity_params_with_metadata import ( OpportunityParamsWithMetadata, ) +from openapi_client.models.bid import Bid +from openapi_client.models.bid_status_with_id import BidStatusWithId from web3.auto import w3 - class BidInfo: + def __init__(self, bid: Bid): + self.bid = bid + + def to_dict(self): + return {"bid": self.bid.to_dict()} + +class OpportunityBidInfo: def __init__(self, opportunity_id: UUID, opportunity_bid: OpportunityBid): self.opportunity_id = opportunity_id self.opportunity_bid = opportunity_bid + def to_dict(self): + return { + "opportunity_id": str(self.opportunity_id), + "opportunity_bid": self.opportunity_bid.to_dict(), + } class ExpressRelayClientException(Exception): pass class ExpressRelayClient: - def __init__(self, server_url: str): + def __init__(self, server_url: str, opportunity_callback: (Callable[[OpportunityParamsWithMetadata], None] | None) = None, bid_status_callback: Callable[[BidStatusWithId], None] | None = None, **kwargs): + """ + Args: + server_url: The URL of the liquidation server. + opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. + bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatus. + kwargs: Keyword arguments to pass to the websocket connection. + """ parsed_url = urllib.parse.urlparse(server_url) if parsed_url.scheme == "https": ws_scheme = "wss" @@ -43,34 +62,24 @@ def __init__(self, server_url: str): self.ws_msg_counter = 0 self.ws = None self.ws_lock = asyncio.Lock() - self.ws_task = None + self.ws_loop = None self.ws_msg_futures = {} + + self.opportunity_callback = opportunity_callback + self.bid_status_callback = bid_status_callback + self.ws_options = kwargs - async def start_ws( - self, - opportunity_callback: ( - Callable[[OpportunityParamsWithMetadata], None] | None - ) = None, - **kwargs, - ) -> asyncio.Task: + async def start_ws(self): """ Initializes the websocket connection to the server, if not already connected. - - Args: - opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. - kwargs: Keyword arguments to pass to the websocket connection. - Returns: - The websocket task. """ async with self.ws_lock: if self.ws is None: - self.ws = await websockets.connect(self.ws_endpoint, **kwargs) - - if self.ws_task is None: - ws_call = self.ws_handler(opportunity_callback) - self.ws_task = asyncio.create_task(ws_call) + self.ws = await websockets.connect(self.ws_endpoint, **self.ws_options) - return self.ws_task + if self.ws_loop is None: + ws_call = self.ws_handler(self.opportunity_callback, self.bid_status_callback) + self.ws_loop = asyncio.create_task(ws_call) async def close_ws(self): """ @@ -79,6 +88,12 @@ async def close_ws(self): async with self.ws_lock: await self.ws.close() + def get_ws_loop(self) -> asyncio.Task: + """ + Returns the websocket handler loop. + """ + return self.ws_loop + async def get_opportunities( self, chain_id: str | None = None, timeout: int = 10 ) -> list[OpportunityParamsWithMetadata]: @@ -113,12 +128,14 @@ async def get_opportunities( return opportunities - async def send_ws_message(self, msg: dict): + async def send_ws_message(self, msg: dict) -> dict: """ Sends a message to the server via websocket. Args: msg: The message to send. + Returns: + The result of the response message from the server. """ await self.start_ws() @@ -132,10 +149,10 @@ async def send_ws_message(self, msg: dict): await self.ws.send(json.dumps(msg)) - # await the response for the subscription from the server + # await the response for the sent ws message from the server msg = await future - self.process_response_msg(msg) + return self.process_response_msg(msg) async def subscribe_chains(self, chain_ids: list[str]): """ @@ -167,6 +184,38 @@ async def unsubscribe_chains(self, chain_ids: list[str]): } await self.send_ws_message(json_unsubscribe) + async def submit_bid(self, bid_info: BidInfo) -> UUID: + """ + Submits a bid to the liquidation server. + + Args: + bid_info: An object representing the bid to submit. + Returns: + The ID of the submitted bid. + """ + json_submit_bid = { + "method": "post_bid", + "params": bid_info.to_dict(), + } + result = await self.send_ws_message(json_submit_bid) + return UUID(result.get("id")) + + async def submit_opportunity_bid(self, opportunity_bid_info: OpportunityBidInfo) -> UUID: + """ + Submits a bid on an opportunity to the liquidation server. + + Args: + opportunity_bid_info: An object representing the bid to submit on an opportunity. + Returns: + The ID of the submitted bid. + """ + json_submit_opportunity_bid = { + "method": "post_liquidation_bid", + "params": opportunity_bid_info.to_dict(), + } + result = await self.send_ws_message(json_submit_opportunity_bid) + return UUID(result.get("id")) + def process_response_msg(self, msg: dict): """ Processes a response message received from the server via websocket. @@ -176,83 +225,47 @@ def process_response_msg(self, msg: dict): """ if msg.get("status") and msg.get("status") != "success": raise ExpressRelayClientException( - f"Error in websocket with message id {msg.get('id')}: {msg.get('result')}" + f"Error in websocket response with message id {msg.get('id')}: {msg.get('result')}" ) + return msg.get("result") async def ws_handler( self, opportunity_callback: ( Callable[[OpportunityParamsWithMetadata], None] | None ) = None, + bid_status_callback: Callable[[BidStatusWithId], None] | None = None ): """ Continuously handles new ws messages as they are received from the server via websocket. Args: opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. + bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatus. """ if not self.ws: raise ExpressRelayClientException("Websocket not connected") - + async for msg in self.ws: msg = json.loads(msg) - if msg.get("id"): - future = self.ws_msg_futures.pop(msg["id"]) - future.set_result(msg) - if msg.get("type") == "new_opportunity": - opportunity = OpportunityParamsWithMetadata.from_dict( - msg["opportunity"] - ) + if msg.get("type"): + if msg.get("type") == "new_opportunity": + opportunity = OpportunityParamsWithMetadata.from_dict( + msg.get("opportunity") + ) - if opportunity_callback is not None: - asyncio.create_task(opportunity_callback(opportunity)) + if opportunity_callback is not None: + asyncio.create_task(opportunity_callback(opportunity)) - async def submit_opportunity( - self, opportunity: OpportunityParams, timeout: int = 10 - ) -> UUID: - """ - Submits an opportunity to the liquidation server. + elif msg.get("type") == "bid_status_update": + if bid_status_callback is not None: + bid_status = BidStatusWithId.from_dict(msg.get("status")) + asyncio.create_task(bid_status_callback(bid_status)) - Args: - opportunity: An object representing the opportunity to submit. - timeout: The timeout for the HTTP request in seconds. - Returns: - The ID of the submitted opportunity. - """ - async with httpx.AsyncClient() as client: - resp = await client.post( - urllib.parse.urlparse(self.server_url) - ._replace(path="/v1/liquidation/opportunities") - .geturl(), - json=opportunity.to_dict(), - timeout=timeout, - ) - resp.raise_for_status() - return UUID(resp.json()["opportunity_id"]) - - async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> UUID: - """ - Submits a bid to the liquidation server. - - Args: - bid_info: An object representing the bid to submit. - timeout: The timeout for the HTTP request in seconds. - Returns: - The ID of the submitted bid. - """ - async with httpx.AsyncClient() as client: - resp = await client.post( - urllib.parse.urlparse(self.server_url) - ._replace( - path=f"/v1/liquidation/opportunities/{str(bid_info.opportunity_id)}/bids" - ) - .geturl(), - json=bid_info.opportunity_bid.to_dict(), - timeout=timeout, - ) - resp.raise_for_status() - return UUID(resp.json()["id"]) + elif msg.get("id"): + future = self.ws_msg_futures.pop(msg["id"]) + future.set_result(msg) def sign_bid( @@ -260,17 +273,17 @@ def sign_bid( bid: int, valid_until: int, private_key: str, -) -> BidInfo: +) -> OpportunityBidInfo: """ - Constructs a signature for a liquidator's bid and returns the BidInfo object to be submitted to the liquidation server. + Constructs a signature for a liquidator's bid and returns the OpportunityBidInfo object to be submitted to the liquidation server. Args: liquidation_opportunity: An object representing the liquidation opportunity, of type OpportunityParamsWithMetadata. - bid: An integer representing the amount of the bid. - valid_until: An integer representing the block until which the bid is valid. + bid: An integer representing the amount of the bid (in wei). + valid_until: An integer representing the unix timestamp until which the bid is valid. private_key: A 0x-prefixed hex string representing the liquidator's private key. Returns: - A BidInfo object, representing the transaction to submit to the liquidation server. This object contains the liquidator's signature. + A OpportunityBidInfo object, representing the transaction to submit to the liquidation server. This object contains the liquidator's signature. """ repay_tokens = [ (token.contract, int(token.amount)) @@ -314,9 +327,9 @@ def sign_bid( } opportunity_bid = OpportunityBid.from_dict(opportunity_bid) - bid_info = BidInfo( + opportunity_bid_info = OpportunityBidInfo( opportunity_id=UUID(liquidation_opportunity.opportunity_id), opportunity_bid=opportunity_bid, ) - return bid_info + return opportunity_bid_info diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index a7bc8c9f9a..fa28f56451 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -3,10 +3,12 @@ import logging from eth_account.account import Account -from express_relay_client import BidInfo, ExpressRelayClient, sign_bid +from express_relay_client import OpportunityBidInfo, ExpressRelayClient, sign_bid from openapi_client.models.opportunity_params_with_metadata import ( OpportunityParamsWithMetadata, ) +from openapi_client.models.bid import Bid +from openapi_client.models.bid_status_with_id import BidStatusWithId logger = logging.getLogger(__name__) @@ -17,29 +19,29 @@ class SimpleSearcher: def __init__(self, server_url: str, private_key: str): - self.client = ExpressRelayClient(server_url) + self.client = ExpressRelayClient(server_url, self.opportunity_callback, self.bid_status_callback) self.private_key = private_key self.liquidator = Account.from_key(private_key).address def assess_liquidation_opportunity( self, opp: OpportunityParamsWithMetadata, - ) -> BidInfo | None: + ) -> OpportunityBidInfo | None: """ - Assesses whether a liquidation opportunity is worth liquidating; if so, returns a BidInfo object. Otherwise returns None. + Assesses whether a liquidation opportunity is worth liquidating; if so, returns an OpportunityBidInfo object. Otherwise returns None. This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the liquidation. There are many ways to evaluate this, but the most common way is to check that the value of the amount the searcher will receive from the liquidation exceeds the value of the amount repaid. Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation. - In this simple searcher, the function always (naively) returns a BidInfo object with a default bid and valid_until timestamp. + In this simple searcher, the function always (naively) returns an OpportunityBidInfo object with a default bid and valid_until timestamp. Args: opp: A OpportunityParamsWithMetadata object, representing a single liquidation opportunity. Returns: - If the opportunity is deemed worthwhile, this function can return a BidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. + If the opportunity is deemed worthwhile, this function can return an OpportunityBidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. """ - bid_info = sign_bid(opp, NAIVE_BID, VALID_UNTIL_MAX, self.private_key) + opportunity_bid_info = sign_bid(opp, NAIVE_BID, VALID_UNTIL_MAX, self.private_key) - return bid_info + return opportunity_bid_info async def opportunity_callback(self, opp: OpportunityParamsWithMetadata): """ @@ -48,17 +50,36 @@ async def opportunity_callback(self, opp: OpportunityParamsWithMetadata): Args: opp: A OpportunityParamsWithMetadata object, representing a single liquidation opportunity. """ - bid_info = self.assess_liquidation_opportunity(opp) - if bid_info: + opportunity_bid_info = self.assess_liquidation_opportunity(opp) + if opportunity_bid_info: try: - await self.client.submit_bid(bid_info) + await self.client.submit_opportunity_bid(opportunity_bid_info) logger.info( - f"Submitted bid amount {bid_info.opportunity_bid.amount} for opportunity {str(bid_info.opportunity_id)}" + f"Submitted bid amount {opportunity_bid_info.opportunity_bid.amount} for opportunity {str(opportunity_bid_info.opportunity_id)}" ) except Exception as e: logger.error( - f"Error submitting bid amount {bid_info.opportunity_bid.amount} for opportunity {str(bid_info.opportunity_id)}: {e}" + f"Error submitting bid amount {opportunity_bid_info.opportunity_bid.amount} for opportunity {str(opportunity_bid_info.opportunity_id)}: {e}" ) + + async def bid_status_callback(self, status: BidStatusWithId): + """ + Callback function to run when a bid status is updated. + + Args: + status: A BidStatus object, representing the status of a bid. + """ + bid_id = status.id + bid_status = status.bid_status.actual_instance + + if bid_status.status == "submitted": + logger.info(f"Bid {bid_id} has been submitted in hash {bid_status.result}") + elif bid_status.status == "lost": + logger.info(f"Bid {bid_id} was unsuccessful") + elif bid_status.status == "pending": + logger.info(f"Bid {bid_id} is pending") + else: + logger.error(f"Unrecognized status {bid_status.status} for bid {bid_id}") async def main(): @@ -99,11 +120,9 @@ async def main(): simple_searcher = SimpleSearcher(args.server_url, sk_liquidator) logger.info("Liquidator address: %s", simple_searcher.liquidator) - task = await simple_searcher.client.start_ws(simple_searcher.opportunity_callback) - await simple_searcher.client.subscribe_chains(args.chain_ids) - await task + await simple_searcher.client.get_ws_loop() if __name__ == "__main__": From 2888be4ff1e47c1aaa10b9a0e28cf12d683985bc Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 7 Mar 2024 20:51:23 -0500 Subject: [PATCH 39/58] precommit --- .../express_relay_client.py | 31 ++++++++++++++----- .../openapi_client/models/api_response.py | 2 -- .../openapi_client/models/api_resposne.py | 2 -- .../openapi_client/models/bid.py | 2 -- .../openapi_client/models/bid400_response.py | 2 -- .../openapi_client/models/bid_result.py | 2 -- .../openapi_client/models/bid_status.py | 2 -- .../models/bid_status_one_of.py | 2 -- .../models/bid_status_one_of1.py | 2 -- .../models/bid_status_one_of2.py | 2 -- .../models/bid_status_with_id.py | 2 -- .../models/bid_status_with_id_bid_status.py | 2 -- .../models/bid_status_with_id_status.py | 2 -- .../openapi_client/models/client_message.py | 2 -- .../models/client_message_one_of.py | 2 -- .../models/client_message_one_of1.py | 2 -- .../models/client_message_one_of2.py | 2 -- .../models/client_message_one_of2_params.py | 2 -- .../models/client_message_one_of3.py | 2 -- .../models/client_message_one_of3_params.py | 2 -- .../models/client_message_one_of_params.py | 2 -- .../openapi_client/models/client_request.py | 2 -- .../models/error_body_response.py | 2 -- .../openapi_client/models/opportunity_bid.py | 2 -- .../models/opportunity_params.py | 2 -- .../models/opportunity_params_one_of.py | 2 -- .../models/opportunity_params_v1.py | 2 -- .../opportunity_params_with_metadata.py | 2 -- .../models/server_result_message.py | 2 -- .../models/server_result_message_one_of.py | 2 -- .../models/server_result_message_one_of1.py | 2 -- .../models/server_result_response.py | 2 -- .../models/server_update_response.py | 2 -- .../models/server_update_response_one_of.py | 2 -- .../models/server_update_response_one_of1.py | 2 -- .../openapi_client/models/token_qty.py | 2 -- .../searcher/examples/simple_searcher.py | 15 +++++---- 37 files changed, 32 insertions(+), 84 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index daf4684dbc..9664dd2e0a 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -9,15 +9,16 @@ import websockets from eth_abi import encode from eth_account.account import Account +from openapi_client.models.bid import Bid +from openapi_client.models.bid_status_with_id import BidStatusWithId from openapi_client.models.client_message import ClientMessage from openapi_client.models.opportunity_bid import OpportunityBid from openapi_client.models.opportunity_params_with_metadata import ( OpportunityParamsWithMetadata, ) -from openapi_client.models.bid import Bid -from openapi_client.models.bid_status_with_id import BidStatusWithId from web3.auto import w3 + class BidInfo: def __init__(self, bid: Bid): self.bid = bid @@ -25,6 +26,7 @@ def __init__(self, bid: Bid): def to_dict(self): return {"bid": self.bid.to_dict()} + class OpportunityBidInfo: def __init__(self, opportunity_id: UUID, opportunity_bid: OpportunityBid): self.opportunity_id = opportunity_id @@ -36,12 +38,21 @@ def to_dict(self): "opportunity_bid": self.opportunity_bid.to_dict(), } + class ExpressRelayClientException(Exception): pass class ExpressRelayClient: - def __init__(self, server_url: str, opportunity_callback: (Callable[[OpportunityParamsWithMetadata], None] | None) = None, bid_status_callback: Callable[[BidStatusWithId], None] | None = None, **kwargs): + def __init__( + self, + server_url: str, + opportunity_callback: ( + Callable[[OpportunityParamsWithMetadata], None] | None + ) = None, + bid_status_callback: Callable[[BidStatusWithId], None] | None = None, + **kwargs, + ): """ Args: server_url: The URL of the liquidation server. @@ -64,7 +75,7 @@ def __init__(self, server_url: str, opportunity_callback: (Callable[[Opportunity self.ws_lock = asyncio.Lock() self.ws_loop = None self.ws_msg_futures = {} - + self.opportunity_callback = opportunity_callback self.bid_status_callback = bid_status_callback self.ws_options = kwargs @@ -78,7 +89,9 @@ async def start_ws(self): self.ws = await websockets.connect(self.ws_endpoint, **self.ws_options) if self.ws_loop is None: - ws_call = self.ws_handler(self.opportunity_callback, self.bid_status_callback) + ws_call = self.ws_handler( + self.opportunity_callback, self.bid_status_callback + ) self.ws_loop = asyncio.create_task(ws_call) async def close_ws(self): @@ -200,7 +213,9 @@ async def submit_bid(self, bid_info: BidInfo) -> UUID: result = await self.send_ws_message(json_submit_bid) return UUID(result.get("id")) - async def submit_opportunity_bid(self, opportunity_bid_info: OpportunityBidInfo) -> UUID: + async def submit_opportunity_bid( + self, opportunity_bid_info: OpportunityBidInfo + ) -> UUID: """ Submits a bid on an opportunity to the liquidation server. @@ -234,7 +249,7 @@ async def ws_handler( opportunity_callback: ( Callable[[OpportunityParamsWithMetadata], None] | None ) = None, - bid_status_callback: Callable[[BidStatusWithId], None] | None = None + bid_status_callback: Callable[[BidStatusWithId], None] | None = None, ): """ Continuously handles new ws messages as they are received from the server via websocket. @@ -245,7 +260,7 @@ async def ws_handler( """ if not self.ws: raise ExpressRelayClientException("Websocket not connected") - + async for msg in self.ws: msg = json.loads(msg) diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py index e60f023cad..ff0ebf9b20 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py @@ -119,5 +119,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py index 264d3d7823..cc9899c569 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py @@ -119,5 +119,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py index ca8ac69cb9..1e4c13eec3 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py @@ -91,5 +91,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "permission_key": obj.get("permission_key") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py index 4f105c4701..ea08de5280 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py index 7a8831d97e..f70c229ba5 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py @@ -85,5 +85,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py index 0e36c51c10..b5b2d1bb66 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py @@ -150,5 +150,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOne def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py index 6fc96fc6d5..1214222c99 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py @@ -90,5 +90,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py index abd0dd479c..35b5c9c96f 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py @@ -92,5 +92,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py index 205db3dbed..3ef1bda706 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py @@ -90,5 +90,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py index e7bbee11fa..21509a60de 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py @@ -89,5 +89,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py index 40bfad1898..b4a6878a85 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py @@ -150,5 +150,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOne def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py index 66fee6cdcd..0adf50a5eb 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py @@ -150,5 +150,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOne def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py index aa9a65e46c..1542c34201 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py @@ -165,5 +165,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMe def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py index 70c89a2b25..cdfab85efe 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py index 7afdd366a2..04af9ba2f1 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py index d62f1859b3..05a06a6a25 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOf2Params.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py index 1b76bacf93..ba234d83f4 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py @@ -87,5 +87,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "bid": Bid.from_dict(obj["bid"]) if obj.get("bid") is not None else None }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py index 7ae631db79..cfe816b98a 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOf3Params.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py index 325f0de00b..4518d4891c 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py @@ -89,5 +89,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "opportunity_id": obj.get("opportunity_id") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py index c4b59cf971..2c735e4f2d 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "chain_ids": obj.get("chain_ids") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py index 6ca1f7a88b..108f79f9e1 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py @@ -90,5 +90,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py index 7dc15e20e5..6a434b9a25 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py @@ -83,5 +83,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py index 5e4432b5e6..e807a40116 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py @@ -91,5 +91,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "valid_until": obj.get("valid_until") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py index f3aeaad06a..88818823fd 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py @@ -122,5 +122,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], OpportunityParamsOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py index c346501c27..976eb052ae 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py @@ -119,5 +119,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "version": obj.get("version") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py index 9fe3b11a38..14e678c020 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py @@ -110,5 +110,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "value": obj.get("value") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py index 524caa68fb..ef54c366bb 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py @@ -123,5 +123,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "opportunity_id": obj.get("opportunity_id") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py index 1654f23f2e..b82ef17cea 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py @@ -137,5 +137,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerResultMessageOneOf, Se def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py index 49523bff2a..f6494828c5 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py @@ -101,5 +101,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py index dc84c41f8e..1c3d064a2c 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py @@ -92,5 +92,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py index a761094f09..3861024277 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py @@ -91,5 +91,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py index ff8efa7136..bc962316b5 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py @@ -136,5 +136,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf, S def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py index 160146ea35..0f5c34a041 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "type": obj.get("type") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py index 731c607438..898c7acddc 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py @@ -96,5 +96,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "type": obj.get("type") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py index 643c93bbfc..a07b01858d 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py @@ -85,5 +85,3 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "contract": obj.get("contract") }) return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index fa28f56451..6665c037a0 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -3,12 +3,11 @@ import logging from eth_account.account import Account -from express_relay_client import OpportunityBidInfo, ExpressRelayClient, sign_bid +from express_relay_client import ExpressRelayClient, OpportunityBidInfo, sign_bid +from openapi_client.models.bid_status_with_id import BidStatusWithId from openapi_client.models.opportunity_params_with_metadata import ( OpportunityParamsWithMetadata, ) -from openapi_client.models.bid import Bid -from openapi_client.models.bid_status_with_id import BidStatusWithId logger = logging.getLogger(__name__) @@ -19,7 +18,9 @@ class SimpleSearcher: def __init__(self, server_url: str, private_key: str): - self.client = ExpressRelayClient(server_url, self.opportunity_callback, self.bid_status_callback) + self.client = ExpressRelayClient( + server_url, self.opportunity_callback, self.bid_status_callback + ) self.private_key = private_key self.liquidator = Account.from_key(private_key).address @@ -39,7 +40,9 @@ def assess_liquidation_opportunity( Returns: If the opportunity is deemed worthwhile, this function can return an OpportunityBidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. """ - opportunity_bid_info = sign_bid(opp, NAIVE_BID, VALID_UNTIL_MAX, self.private_key) + opportunity_bid_info = sign_bid( + opp, NAIVE_BID, VALID_UNTIL_MAX, self.private_key + ) return opportunity_bid_info @@ -61,7 +64,7 @@ async def opportunity_callback(self, opp: OpportunityParamsWithMetadata): logger.error( f"Error submitting bid amount {opportunity_bid_info.opportunity_bid.amount} for opportunity {str(opportunity_bid_info.opportunity_id)}: {e}" ) - + async def bid_status_callback(self, status: BidStatusWithId): """ Callback function to run when a bid status is updated. From 6b5555f5f53acd19546e55539c10130111e42cfc Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 7 Mar 2024 23:52:23 -0500 Subject: [PATCH 40/58] remove extraneous files, stick w actual_instance --- .../openapi_client/models/api_response.py | 121 -------------- .../models/bid_status_with_id_bid_status.py | 152 ------------------ .../models/bid_status_with_id_status.py | 152 ------------------ 3 files changed, 425 deletions(-) delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py deleted file mode 100644 index ff0ebf9b20..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py +++ /dev/null @@ -1,121 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.bid_result import BidResult -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -APIRESPONSE_ONE_OF_SCHEMAS = ["BidResult"] - -class APIResponse(BaseModel): - """ - APIResponse - """ - # data type: BidResult - oneof_schema_1_validator: Optional[BidResult] = None - actual_instance: Optional[Union[BidResult]] = None - one_of_schemas: List[str] = Field(default=Literal["BidResult"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = APIResponse.model_construct() - error_messages = [] - match = 0 - # validate data type: BidResult - if not isinstance(v, BidResult): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidResult`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into BidResult - try: - instance.actual_instance = BidResult.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py deleted file mode 100644 index b4a6878a85..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_bid_status.py +++ /dev/null @@ -1,152 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.bid_status_one_of import BidStatusOneOf -from openapi_client.models.bid_status_one_of1 import BidStatusOneOf1 -from openapi_client.models.bid_status_one_of2 import BidStatusOneOf2 -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -BIDSTATUSWITHIDBIDSTATUS_ONE_OF_SCHEMAS = ["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"] - -class BidStatusWithIdBidStatus(BaseModel): - """ - BidStatusWithIdBidStatus - """ - # data type: BidStatusOneOf - oneof_schema_1_validator: Optional[BidStatusOneOf] = None - # data type: BidStatusOneOf1 - oneof_schema_2_validator: Optional[BidStatusOneOf1] = None - # data type: BidStatusOneOf2 - oneof_schema_3_validator: Optional[BidStatusOneOf2] = None - actual_instance: Optional[Union[BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]] = None - one_of_schemas: List[str] = Field(default=Literal["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - discriminator_value_class_map: Dict[str, str] = { - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = BidStatusWithIdBidStatus.model_construct() - error_messages = [] - match = 0 - # validate data type: BidStatusOneOf - if not isinstance(v, BidStatusOneOf): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf`") - else: - match += 1 - # validate data type: BidStatusOneOf1 - if not isinstance(v, BidStatusOneOf1): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf1`") - else: - match += 1 - # validate data type: BidStatusOneOf2 - if not isinstance(v, BidStatusOneOf2): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf2`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in BidStatusWithIdBidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in BidStatusWithIdBidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into BidStatusOneOf - try: - instance.actual_instance = BidStatusOneOf.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into BidStatusOneOf1 - try: - instance.actual_instance = BidStatusOneOf1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into BidStatusOneOf2 - try: - instance.actual_instance = BidStatusOneOf2.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into BidStatusWithIdBidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into BidStatusWithIdBidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py deleted file mode 100644 index 0adf50a5eb..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id_status.py +++ /dev/null @@ -1,152 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.bid_status_one_of import BidStatusOneOf -from openapi_client.models.bid_status_one_of1 import BidStatusOneOf1 -from openapi_client.models.bid_status_one_of2 import BidStatusOneOf2 -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -BIDSTATUSWITHIDSTATUS_ONE_OF_SCHEMAS = ["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"] - -class BidStatusWithIdStatus(BaseModel): - """ - BidStatusWithIdStatus - """ - # data type: BidStatusOneOf - oneof_schema_1_validator: Optional[BidStatusOneOf] = None - # data type: BidStatusOneOf1 - oneof_schema_2_validator: Optional[BidStatusOneOf1] = None - # data type: BidStatusOneOf2 - oneof_schema_3_validator: Optional[BidStatusOneOf2] = None - actual_instance: Optional[Union[BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]] = None - one_of_schemas: List[str] = Field(default=Literal["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - discriminator_value_class_map: Dict[str, str] = { - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = BidStatusWithIdStatus.model_construct() - error_messages = [] - match = 0 - # validate data type: BidStatusOneOf - if not isinstance(v, BidStatusOneOf): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf`") - else: - match += 1 - # validate data type: BidStatusOneOf1 - if not isinstance(v, BidStatusOneOf1): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf1`") - else: - match += 1 - # validate data type: BidStatusOneOf2 - if not isinstance(v, BidStatusOneOf2): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf2`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in BidStatusWithIdStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in BidStatusWithIdStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into BidStatusOneOf - try: - instance.actual_instance = BidStatusOneOf.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into BidStatusOneOf1 - try: - instance.actual_instance = BidStatusOneOf1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into BidStatusOneOf2 - try: - instance.actual_instance = BidStatusOneOf2.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into BidStatusWithIdStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into BidStatusWithIdStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) From 968f874ddd056828f3ff6c748d65744330982978 Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 8 Mar 2024 01:11:36 -0500 Subject: [PATCH 41/58] added back http as nondefault --- .../express_relay_client.py | 99 +++++++++++--- .../openapi_client/models/api_resposne.py | 121 ------------------ 2 files changed, 82 insertions(+), 138 deletions(-) delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 9664dd2e0a..8c38736f30 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -13,6 +13,7 @@ from openapi_client.models.bid_status_with_id import BidStatusWithId from openapi_client.models.client_message import ClientMessage from openapi_client.models.opportunity_bid import OpportunityBid +from openapi_client.models.opportunity_params import OpportunityParams from openapi_client.models.opportunity_params_with_metadata import ( OpportunityParamsWithMetadata, ) @@ -75,10 +76,9 @@ def __init__( self.ws_lock = asyncio.Lock() self.ws_loop = None self.ws_msg_futures = {} - + self.ws_options = kwargs self.opportunity_callback = opportunity_callback self.bid_status_callback = bid_status_callback - self.ws_options = kwargs async def start_ws(self): """ @@ -197,39 +197,82 @@ async def unsubscribe_chains(self, chain_ids: list[str]): } await self.send_ws_message(json_unsubscribe) - async def submit_bid(self, bid_info: BidInfo) -> UUID: + async def submit_bid( + self, bid_info: BidInfo, subscribe: bool = True, **kwargs + ) -> UUID: """ Submits a bid to the liquidation server. Args: bid_info: An object representing the bid to submit. + subscribe: A boolean indicating whether to subscribe to the bid status updates. + kwargs: Keyword arguments to pass to the HTTP post request. Returns: The ID of the submitted bid. """ - json_submit_bid = { - "method": "post_bid", - "params": bid_info.to_dict(), - } - result = await self.send_ws_message(json_submit_bid) - return UUID(result.get("id")) + bid_info_dict = bid_info.to_dict() + if subscribe: + json_submit_bid = { + "method": "post_bid", + "params": bid_info_dict, + } + result = await self.send_ws_message(json_submit_bid) + bid_id = UUID(result.get("id")) + else: + async with httpx.AsyncClient() as client: + resp = await client.post( + urllib.parse.urlparse(self.server_url) + ._replace(path="/v1/bids") + .geturl(), + json=bid_info.bid.to_dict(), + **kwargs, + ) + + resp.raise_for_status() + bid_id = UUID(resp.json().get("id")) + + return bid_id async def submit_opportunity_bid( - self, opportunity_bid_info: OpportunityBidInfo + self, opportunity_bid_info: OpportunityBidInfo, subscribe: bool = True, **kwargs ) -> UUID: """ - Submits a bid on an opportunity to the liquidation server. + Submits a bid on an opportunity to the liquidation server via websocket. Args: opportunity_bid_info: An object representing the bid to submit on an opportunity. + subscribe: A boolean indicating whether to subscribe to the bid status updates. + kwargs: Keyword arguments to pass to the HTTP post request. Returns: The ID of the submitted bid. """ - json_submit_opportunity_bid = { - "method": "post_liquidation_bid", - "params": opportunity_bid_info.to_dict(), - } - result = await self.send_ws_message(json_submit_opportunity_bid) - return UUID(result.get("id")) + opportunity_bid_info_dict = opportunity_bid_info.to_dict() + if subscribe: + json_submit_opportunity_bid = { + "method": "post_liquidation_bid", + "params": opportunity_bid_info_dict(), + } + result = await self.send_ws_message(json_submit_opportunity_bid) + bid_id = UUID(result.get("id")) + else: + opportunity_id = opportunity_bid_info_dict["opportunity_id"] + opportunity_bid = opportunity_bid_info_dict["opportunity_bid"] + + async with httpx.AsyncClient() as client: + resp = await client.post( + urllib.parse.urlparse(self.server_url) + ._replace( + path=f"/v1/liquidation/opportunities/{opportunity_id}/bids" + ) + .geturl(), + json=opportunity_bid, + **kwargs, + ) + + resp.raise_for_status() + bid_id = UUID(resp.json().get("id")) + + return bid_id def process_response_msg(self, msg: dict): """ @@ -282,6 +325,28 @@ async def ws_handler( future = self.ws_msg_futures.pop(msg["id"]) future.set_result(msg) + async def submit_opportunity( + self, opportunity: OpportunityParams, timeout: int = 10 + ) -> UUID: + """ + Submits an opportunity to the liquidation server. + Args: + opportunity: An object representing the opportunity to submit. + timeout: The timeout for the HTTP request in seconds. + Returns: + The ID of the submitted opportunity. + """ + async with httpx.AsyncClient() as client: + resp = await client.post( + urllib.parse.urlparse(self.server_url) + ._replace(path="/v1/liquidation/opportunities") + .geturl(), + json=opportunity.to_dict(), + timeout=timeout, + ) + resp.raise_for_status() + return UUID(resp.json()["opportunity_id"]) + def sign_bid( liquidation_opportunity: OpportunityParamsWithMetadata, diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py deleted file mode 100644 index cc9899c569..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_resposne.py +++ /dev/null @@ -1,121 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.bid_result import BidResult -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -APIRESPOSNE_ONE_OF_SCHEMAS = ["BidResult"] - -class APIResposne(BaseModel): - """ - APIResposne - """ - # data type: BidResult - oneof_schema_1_validator: Optional[BidResult] = None - actual_instance: Optional[Union[BidResult]] = None - one_of_schemas: List[str] = Field(default=Literal["BidResult"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = APIResposne.model_construct() - error_messages = [] - match = 0 - # validate data type: BidResult - if not isinstance(v, BidResult): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidResult`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in APIResposne with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in APIResposne with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into BidResult - try: - instance.actual_instance = BidResult.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into APIResposne with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into APIResposne with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) From 8afe3e73a177ddef14af80ca3bc7c3191148cea0 Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 8 Mar 2024 01:14:34 -0500 Subject: [PATCH 42/58] correction --- .../sdk/python/express_relay_utils/express_relay_client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 8c38736f30..2b2f796326 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -250,7 +250,7 @@ async def submit_opportunity_bid( if subscribe: json_submit_opportunity_bid = { "method": "post_liquidation_bid", - "params": opportunity_bid_info_dict(), + "params": opportunity_bid_info.to_dict(), } result = await self.send_ws_message(json_submit_opportunity_bid) bid_id = UUID(result.get("id")) @@ -330,6 +330,7 @@ async def submit_opportunity( ) -> UUID: """ Submits an opportunity to the liquidation server. + Args: opportunity: An object representing the opportunity to submit. timeout: The timeout for the HTTP request in seconds. From 3e55b2f04afe1d7a3bb3971d2d2c87066d48e095 Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 8 Mar 2024 10:33:18 -0500 Subject: [PATCH 43/58] some cleanup and reorg --- .../express_relay_client.py | 134 +++++++++--------- .../searcher/examples/simple_searcher.py | 2 +- 2 files changed, 71 insertions(+), 65 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 2b2f796326..e6f2cf4036 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -101,45 +101,13 @@ async def close_ws(self): async with self.ws_lock: await self.ws.close() - def get_ws_loop(self) -> asyncio.Task: + async def get_ws_loop(self) -> asyncio.Task: """ Returns the websocket handler loop. """ - return self.ws_loop - - async def get_opportunities( - self, chain_id: str | None = None, timeout: int = 10 - ) -> list[OpportunityParamsWithMetadata]: - """ - Connects to the liquidation server and fetches liquidation opportunities. - - Args: - chain_id: The chain ID to fetch liquidation opportunities for. If None, fetches opportunities across all chains. - timeout: The timeout for the HTTP request in seconds. - Returns: - A list of liquidation opportunities. - """ - params = {} - if chain_id: - params["chain_id"] = chain_id - - async with httpx.AsyncClient() as client: - resp = await client.get( - urllib.parse.urlparse(self.server_url) - ._replace(path="/v1/liquidation/opportunities") - .geturl(), - params=params, - timeout=timeout, - ) - - resp.raise_for_status() - - opportunities = [ - OpportunityParamsWithMetadata.from_dict(opportunity) - for opportunity in resp.json() - ] + await self.start_ws() - return opportunities + return self.ws_loop async def send_ws_message(self, msg: dict) -> dict: """ @@ -167,6 +135,21 @@ async def send_ws_message(self, msg: dict) -> dict: return self.process_response_msg(msg) + def process_response_msg(self, msg: dict) -> dict: + """ + Processes a response message received from the server via websocket. + + Args: + msg: The message to process. + Returns: + The result field of the message. + """ + if msg.get("status") and msg.get("status") != "success": + raise ExpressRelayClientException( + f"Error in websocket response with message id {msg.get('id')}: {msg.get('result')}" + ) + return msg.get("result") + async def subscribe_chains(self, chain_ids: list[str]): """ Subscribes websocket to a list of chain IDs for new liquidation opportunities. @@ -198,20 +181,20 @@ async def unsubscribe_chains(self, chain_ids: list[str]): await self.send_ws_message(json_unsubscribe) async def submit_bid( - self, bid_info: BidInfo, subscribe: bool = True, **kwargs + self, bid_info: BidInfo, subscribe_to_updates: bool = True, **kwargs ) -> UUID: """ - Submits a bid to the liquidation server. + Submits a bid to the auction server. Args: bid_info: An object representing the bid to submit. - subscribe: A boolean indicating whether to subscribe to the bid status updates. + subscribe_to_updates: A boolean indicating whether to subscribe to the bid status updates. kwargs: Keyword arguments to pass to the HTTP post request. Returns: The ID of the submitted bid. """ bid_info_dict = bid_info.to_dict() - if subscribe: + if subscribe_to_updates: json_submit_bid = { "method": "post_bid", "params": bid_info_dict, @@ -234,20 +217,23 @@ async def submit_bid( return bid_id async def submit_opportunity_bid( - self, opportunity_bid_info: OpportunityBidInfo, subscribe: bool = True, **kwargs + self, + opportunity_bid_info: OpportunityBidInfo, + subscribe_to_updates: bool = True, + **kwargs, ) -> UUID: """ Submits a bid on an opportunity to the liquidation server via websocket. Args: opportunity_bid_info: An object representing the bid to submit on an opportunity. - subscribe: A boolean indicating whether to subscribe to the bid status updates. + subscribe_to_updates: A boolean indicating whether to subscribe to the bid status updates. kwargs: Keyword arguments to pass to the HTTP post request. Returns: The ID of the submitted bid. """ opportunity_bid_info_dict = opportunity_bid_info.to_dict() - if subscribe: + if subscribe_to_updates: json_submit_opportunity_bid = { "method": "post_liquidation_bid", "params": opportunity_bid_info.to_dict(), @@ -274,19 +260,6 @@ async def submit_opportunity_bid( return bid_id - def process_response_msg(self, msg: dict): - """ - Processes a response message received from the server via websocket. - - Args: - msg: The message to process. - """ - if msg.get("status") and msg.get("status") != "success": - raise ExpressRelayClientException( - f"Error in websocket response with message id {msg.get('id')}: {msg.get('result')}" - ) - return msg.get("result") - async def ws_handler( self, opportunity_callback: ( @@ -295,11 +268,11 @@ async def ws_handler( bid_status_callback: Callable[[BidStatusWithId], None] | None = None, ): """ - Continuously handles new ws messages as they are received from the server via websocket. + Continually handles new ws messages as they are received from the server via websocket. Args: opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. - bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatus. + bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatusWithId. """ if not self.ws: raise ExpressRelayClientException("Websocket not connected") @@ -309,11 +282,10 @@ async def ws_handler( if msg.get("type"): if msg.get("type") == "new_opportunity": - opportunity = OpportunityParamsWithMetadata.from_dict( - msg.get("opportunity") - ) - if opportunity_callback is not None: + opportunity = OpportunityParamsWithMetadata.from_dict( + msg.get("opportunity") + ) asyncio.create_task(opportunity_callback(opportunity)) elif msg.get("type") == "bid_status_update": @@ -325,15 +297,49 @@ async def ws_handler( future = self.ws_msg_futures.pop(msg["id"]) future.set_result(msg) + async def get_opportunities( + self, chain_id: str | None = None, timeout_secs: int = 10 + ) -> list[OpportunityParamsWithMetadata]: + """ + Connects to the liquidation server and fetches liquidation opportunities. + + Args: + chain_id: The chain ID to fetch liquidation opportunities for. If None, fetches opportunities across all chains. + timeout_secs: The timeout for the HTTP request in seconds. + Returns: + A list of liquidation opportunities. + """ + params = {} + if chain_id: + params["chain_id"] = chain_id + + async with httpx.AsyncClient() as client: + resp = await client.get( + urllib.parse.urlparse(self.server_url) + ._replace(path="/v1/liquidation/opportunities") + .geturl(), + params=params, + timeout=timeout_secs, + ) + + resp.raise_for_status() + + opportunities = [ + OpportunityParamsWithMetadata.from_dict(opportunity) + for opportunity in resp.json() + ] + + return opportunities + async def submit_opportunity( - self, opportunity: OpportunityParams, timeout: int = 10 + self, opportunity: OpportunityParams, timeout_secs: int = 10 ) -> UUID: """ Submits an opportunity to the liquidation server. Args: opportunity: An object representing the opportunity to submit. - timeout: The timeout for the HTTP request in seconds. + timeout_secs: The timeout for the HTTP request in seconds. Returns: The ID of the submitted opportunity. """ @@ -343,7 +349,7 @@ async def submit_opportunity( ._replace(path="/v1/liquidation/opportunities") .geturl(), json=opportunity.to_dict(), - timeout=timeout, + timeout=timeout_secs, ) resp.raise_for_status() return UUID(resp.json()["opportunity_id"]) diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index 6665c037a0..2e863ac4ce 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -70,7 +70,7 @@ async def bid_status_callback(self, status: BidStatusWithId): Callback function to run when a bid status is updated. Args: - status: A BidStatus object, representing the status of a bid. + status: A BidStatusWithId object, representing the status of a bid. """ bid_id = status.id bid_status = status.bid_status.actual_instance From fbc5e6b1ca2261ce9302cfb4c96adbbc78438d80 Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 8 Mar 2024 16:11:32 -0500 Subject: [PATCH 44/58] minor changes --- .../python/express_relay_utils/express_relay_client.py | 8 +++++--- .../searcher/examples/simple_searcher.py | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index e6f2cf4036..88693f357b 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -148,7 +148,7 @@ def process_response_msg(self, msg: dict) -> dict: raise ExpressRelayClientException( f"Error in websocket response with message id {msg.get('id')}: {msg.get('result')}" ) - return msg.get("result") + return msg["result"] async def subscribe_chains(self, chain_ids: list[str]): """ @@ -290,8 +290,10 @@ async def ws_handler( elif msg.get("type") == "bid_status_update": if bid_status_callback is not None: - bid_status = BidStatusWithId.from_dict(msg.get("status")) - asyncio.create_task(bid_status_callback(bid_status)) + bid_status_with_id = BidStatusWithId.from_dict( + msg.get("status") + ) + asyncio.create_task(bid_status_callback(bid_status_with_id)) elif msg.get("id"): future = self.ws_msg_futures.pop(msg["id"]) diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index 2e863ac4ce..9f979a02bb 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -125,7 +125,8 @@ async def main(): await simple_searcher.client.subscribe_chains(args.chain_ids) - await simple_searcher.client.get_ws_loop() + task = await simple_searcher.client.get_ws_loop() + await task if __name__ == "__main__": From 05f426e60cb52490c18ccdf42ec4949a10a5364a Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 8 Mar 2024 16:11:52 -0500 Subject: [PATCH 45/58] add back api response typing file --- .../openapi_client/models/api_response.py | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py new file mode 100644 index 0000000000..e60f023cad --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py @@ -0,0 +1,123 @@ +# coding: utf-8 + +""" + auction-server + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 0.1.6 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from openapi_client.models.bid_result import BidResult +from pydantic import StrictStr, Field +from typing import Union, List, Optional, Dict +from typing_extensions import Literal, Self + +APIRESPONSE_ONE_OF_SCHEMAS = ["BidResult"] + +class APIResponse(BaseModel): + """ + APIResponse + """ + # data type: BidResult + oneof_schema_1_validator: Optional[BidResult] = None + actual_instance: Optional[Union[BidResult]] = None + one_of_schemas: List[str] = Field(default=Literal["BidResult"]) + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = APIResponse.model_construct() + error_messages = [] + match = 0 + # validate data type: BidResult + if not isinstance(v, BidResult): + error_messages.append(f"Error! Input type `{type(v)}` is not `BidResult`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into BidResult + try: + instance.actual_instance = BidResult.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + From c85480ba57c774ce96e4547f6a194599b1bc4c26 Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 8 Mar 2024 16:14:23 -0500 Subject: [PATCH 46/58] minorer changes --- .../openapi_client/models/api_response.py | 2 -- .../searcher/examples/simple_searcher.py | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py index e60f023cad..ff0ebf9b20 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py @@ -119,5 +119,3 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index 9f979a02bb..4af07a92ad 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -65,15 +65,15 @@ async def opportunity_callback(self, opp: OpportunityParamsWithMetadata): f"Error submitting bid amount {opportunity_bid_info.opportunity_bid.amount} for opportunity {str(opportunity_bid_info.opportunity_id)}: {e}" ) - async def bid_status_callback(self, status: BidStatusWithId): + async def bid_status_callback(self, bid_status_with_id: BidStatusWithId): """ Callback function to run when a bid status is updated. Args: - status: A BidStatusWithId object, representing the status of a bid. + bid_status_with_id: A BidStatusWithId object, representing the status of a bid. """ - bid_id = status.id - bid_status = status.bid_status.actual_instance + bid_id = bid_status_with_id.id + bid_status = bid_status_with_id.bid_status.actual_instance if bid_status.status == "submitted": logger.info(f"Bid {bid_id} has been submitted in hash {bid_status.result}") From 3af2730ed0afd663113ee3afcfbad829b62e063b Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 8 Mar 2024 17:23:29 -0500 Subject: [PATCH 47/58] exclude openapi_client from end of file fixer --- .pre-commit-config.yaml | 1 + .../express_relay_utils/openapi_client/models/api_response.py | 2 ++ .../sdk/python/express_relay_utils/openapi_client/models/bid.py | 2 ++ .../openapi_client/models/bid400_response.py | 2 ++ .../express_relay_utils/openapi_client/models/bid_result.py | 2 ++ .../express_relay_utils/openapi_client/models/bid_status.py | 2 ++ .../openapi_client/models/bid_status_one_of.py | 2 ++ .../openapi_client/models/bid_status_one_of1.py | 2 ++ .../openapi_client/models/bid_status_one_of2.py | 2 ++ .../openapi_client/models/bid_status_with_id.py | 2 ++ .../express_relay_utils/openapi_client/models/client_message.py | 2 ++ .../openapi_client/models/client_message_one_of.py | 2 ++ .../openapi_client/models/client_message_one_of1.py | 2 ++ .../openapi_client/models/client_message_one_of2.py | 2 ++ .../openapi_client/models/client_message_one_of2_params.py | 2 ++ .../openapi_client/models/client_message_one_of3.py | 2 ++ .../openapi_client/models/client_message_one_of3_params.py | 2 ++ .../openapi_client/models/client_message_one_of_params.py | 2 ++ .../express_relay_utils/openapi_client/models/client_request.py | 2 ++ .../openapi_client/models/error_body_response.py | 2 ++ .../openapi_client/models/opportunity_bid.py | 2 ++ .../openapi_client/models/opportunity_params.py | 2 ++ .../openapi_client/models/opportunity_params_one_of.py | 2 ++ .../openapi_client/models/opportunity_params_v1.py | 2 ++ .../openapi_client/models/opportunity_params_with_metadata.py | 2 ++ .../openapi_client/models/server_result_message.py | 2 ++ .../openapi_client/models/server_result_message_one_of.py | 2 ++ .../openapi_client/models/server_result_message_one_of1.py | 2 ++ .../openapi_client/models/server_result_response.py | 2 ++ .../openapi_client/models/server_update_response.py | 2 ++ .../openapi_client/models/server_update_response_one_of.py | 2 ++ .../openapi_client/models/server_update_response_one_of1.py | 2 ++ .../express_relay_utils/openapi_client/models/token_qty.py | 2 ++ 33 files changed, 65 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 09919ffd2a..cf34a7d6ea 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,6 +4,7 @@ repos: hooks: - id: trailing-whitespace - id: end-of-file-fixer + exclude: openapi_client - id: check-added-large-files exclude: package-lock.json # Hook to format many type of files in the repo diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py index ff0ebf9b20..e60f023cad 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py @@ -119,3 +119,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py index 1e4c13eec3..ca8ac69cb9 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py @@ -91,3 +91,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "permission_key": obj.get("permission_key") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py index ea08de5280..4f105c4701 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py index f70c229ba5..7a8831d97e 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py @@ -85,3 +85,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py index b5b2d1bb66..0e36c51c10 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py @@ -150,3 +150,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOne def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py index 1214222c99..6fc96fc6d5 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py @@ -90,3 +90,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py index 35b5c9c96f..abd0dd479c 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py @@ -92,3 +92,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py index 3ef1bda706..205db3dbed 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py @@ -90,3 +90,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py index 21509a60de..e7bbee11fa 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py @@ -89,3 +89,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py index 1542c34201..aa9a65e46c 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py @@ -165,3 +165,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMe def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py index cdfab85efe..70c89a2b25 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py index 04af9ba2f1..7afdd366a2 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py index 05a06a6a25..d62f1859b3 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOf2Params.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py index ba234d83f4..1b76bacf93 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py @@ -87,3 +87,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "bid": Bid.from_dict(obj["bid"]) if obj.get("bid") is not None else None }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py index cfe816b98a..7ae631db79 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "params": ClientMessageOneOf3Params.from_dict(obj["params"]) if obj.get("params") is not None else None }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py index 4518d4891c..325f0de00b 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py @@ -89,3 +89,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "opportunity_id": obj.get("opportunity_id") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py index 2c735e4f2d..c4b59cf971 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "chain_ids": obj.get("chain_ids") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py index 108f79f9e1..6ca1f7a88b 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py @@ -90,3 +90,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py index 6a434b9a25..7dc15e20e5 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py @@ -83,3 +83,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "error": obj.get("error") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py index e807a40116..5e4432b5e6 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py @@ -91,3 +91,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "valid_until": obj.get("valid_until") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py index 88818823fd..f3aeaad06a 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py @@ -122,3 +122,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], OpportunityParamsOneOf]]: def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py index 976eb052ae..c346501c27 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py @@ -119,3 +119,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "version": obj.get("version") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py index 14e678c020..9fe3b11a38 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py @@ -110,3 +110,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "value": obj.get("value") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py index ef54c366bb..524caa68fb 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py @@ -123,3 +123,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "opportunity_id": obj.get("opportunity_id") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py index b82ef17cea..1654f23f2e 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py @@ -137,3 +137,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerResultMessageOneOf, Se def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py index f6494828c5..49523bff2a 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py @@ -101,3 +101,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py index 1c3d064a2c..dc84c41f8e 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py @@ -92,3 +92,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "status": obj.get("status") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py index 3861024277..a761094f09 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py @@ -91,3 +91,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "id": obj.get("id") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py index bc962316b5..ff8efa7136 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py @@ -136,3 +136,5 @@ def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf, S def to_str(self) -> str: """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py index 0f5c34a041..160146ea35 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "type": obj.get("type") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py index 898c7acddc..731c607438 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py @@ -96,3 +96,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "type": obj.get("type") }) return _obj + + diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py index a07b01858d..643c93bbfc 100644 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py +++ b/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py @@ -85,3 +85,5 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "contract": obj.get("contract") }) return _obj + + From 946ff3aafaf423e0f3bc646659ad374e31fc802d Mon Sep 17 00:00:00 2001 From: ani Date: Tue, 12 Mar 2024 23:05:28 -0400 Subject: [PATCH 48/58] build internal models via pydantic --- .pre-commit-config.yaml | 8 +- .../express_relay_client.py | 152 +- .../express_relay_types.py | 400 ++++ .../openapi_client/models/api_response.py | 123 - .../openapi_client/models/bid.py | 95 - .../openapi_client/models/bid400_response.py | 87 - .../openapi_client/models/bid_result.py | 89 - .../openapi_client/models/bid_status.py | 154 -- .../models/bid_status_one_of.py | 94 - .../models/bid_status_one_of1.py | 96 - .../models/bid_status_one_of2.py | 94 - .../models/bid_status_with_id.py | 93 - .../openapi_client/models/client_message.py | 169 -- .../models/client_message_one_of.py | 100 - .../models/client_message_one_of1.py | 100 - .../models/client_message_one_of2.py | 100 - .../models/client_message_one_of2_params.py | 91 - .../models/client_message_one_of3.py | 100 - .../models/client_message_one_of3_params.py | 93 - .../models/client_message_one_of_params.py | 87 - .../openapi_client/models/client_request.py | 94 - .../models/error_body_response.py | 87 - .../openapi_client/models/opportunity_bid.py | 95 - .../models/opportunity_params.py | 126 - .../models/opportunity_params_one_of.py | 123 - .../models/opportunity_params_v1.py | 114 - .../opportunity_params_with_metadata.py | 127 - .../models/server_result_message.py | 141 -- .../models/server_result_message_one_of.py | 105 - .../models/server_result_message_one_of1.py | 96 - .../models/server_result_response.py | 95 - .../models/server_update_response.py | 140 -- .../models/server_update_response_one_of.py | 100 - .../models/server_update_response_one_of1.py | 100 - .../openapi_client/models/token_qty.py | 89 - .../searcher/examples/simple_searcher.py | 15 +- express_relay/sdk/python/mypy.ini | 2 + express_relay/sdk/python/poetry.lock | 2075 +++++++++++++++++ express_relay/sdk/python/pyproject.toml | 7 +- 39 files changed, 2551 insertions(+), 3505 deletions(-) create mode 100644 express_relay/sdk/python/express_relay_utils/express_relay_types.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py delete mode 100644 express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py create mode 100644 express_relay/sdk/python/mypy.ini create mode 100644 express_relay/sdk/python/poetry.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cf34a7d6ea..897ed5cbb8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -115,11 +115,11 @@ repos: # For express relay python files - id: black name: black - entry: poetry -C express_relay/sdk/python/express_relay_utils run black express_relay/sdk/python/express_relay_utils --exclude openapi_client + entry: poetry -C express_relay/sdk/python/express_relay_utils run black express_relay/sdk/python/express_relay_utils pass_filenames: false language: system - - id: ruff - name: ruff - entry: poetry -C express_relay/sdk/python/express_relay_utils run ruff express_relay/sdk/python/express_relay_utils --exclude openapi_client --fix + - id: pyflakes + name: pyflakes + entry: poetry -C express_relay/sdk/python/express_relay_utils run pyflakes express_relay/sdk/python/express_relay_utils pass_filenames: false language: system diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 88693f357b..ecce0c8b4e 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -1,43 +1,20 @@ import asyncio +from asyncio import Task import json import urllib.parse -from typing import Callable -from uuid import UUID +from typing import Callable, Any +from collections.abc import Coroutine import httpx import web3 import websockets +from websockets.client import WebSocketClientProtocol from eth_abi import encode from eth_account.account import Account -from openapi_client.models.bid import Bid -from openapi_client.models.bid_status_with_id import BidStatusWithId -from openapi_client.models.client_message import ClientMessage -from openapi_client.models.opportunity_bid import OpportunityBid -from openapi_client.models.opportunity_params import OpportunityParams -from openapi_client.models.opportunity_params_with_metadata import ( - OpportunityParamsWithMetadata, -) -from web3.auto import w3 - - -class BidInfo: - def __init__(self, bid: Bid): - self.bid = bid - - def to_dict(self): - return {"bid": self.bid.to_dict()} +from web3.auto import w3 -class OpportunityBidInfo: - def __init__(self, opportunity_id: UUID, opportunity_bid: OpportunityBid): - self.opportunity_id = opportunity_id - self.opportunity_bid = opportunity_bid - - def to_dict(self): - return { - "opportunity_id": str(self.opportunity_id), - "opportunity_bid": self.opportunity_bid.to_dict(), - } +from express_relay_types import * class ExpressRelayClientException(Exception): @@ -72,10 +49,10 @@ def __init__( self.server_url = server_url self.ws_endpoint = parsed_url._replace(scheme=ws_scheme, path="/v1/ws").geturl() self.ws_msg_counter = 0 - self.ws = None + self.ws: WebSocketClientProtocol self.ws_lock = asyncio.Lock() - self.ws_loop = None - self.ws_msg_futures = {} + self.ws_loop: Task[Any] + self.ws_msg_futures: dict[str, asyncio.Future] = {} self.ws_options = kwargs self.opportunity_callback = opportunity_callback self.bid_status_callback = bid_status_callback @@ -85,10 +62,10 @@ async def start_ws(self): Initializes the websocket connection to the server, if not already connected. """ async with self.ws_lock: - if self.ws is None: + if not hasattr(self, "ws"): self.ws = await websockets.connect(self.ws_endpoint, **self.ws_options) - if self.ws_loop is None: + if not hasattr(self, "ws_loop"): ws_call = self.ws_handler( self.opportunity_callback, self.bid_status_callback ) @@ -109,26 +86,28 @@ async def get_ws_loop(self) -> asyncio.Task: return self.ws_loop - async def send_ws_message(self, msg: dict) -> dict: + async def send_ws_message(self, method: str, params: dict) -> dict: """ Sends a message to the server via websocket. Args: - msg: The message to send. + method: The type of message to send. + params: The parameters to send in the message. Returns: The result of the response message from the server. """ await self.start_ws() # validate the format of msg - msg = ClientMessage.from_dict(msg).to_dict() - msg["id"] = str(self.ws_msg_counter) + params.update({"method": method}) + msg_validated = ClientMessage.model_validate({"params": params}).to_dict() + msg_validated["id"] = str(self.ws_msg_counter) self.ws_msg_counter += 1 future = asyncio.get_event_loop().create_future() - self.ws_msg_futures[msg["id"]] = future + self.ws_msg_futures[msg_validated["id"]] = future - await self.ws.send(json.dumps(msg)) + await self.ws.send(json.dumps(msg_validated)) # await the response for the sent ws message from the server msg = await future @@ -157,13 +136,10 @@ async def subscribe_chains(self, chain_ids: list[str]): Args: chain_ids: A list of chain IDs to subscribe to. """ - json_subscribe = { - "method": "subscribe", - "params": { - "chain_ids": chain_ids, - }, + params = { + "chain_ids": chain_ids, } - await self.send_ws_message(json_subscribe) + await self.send_ws_message("subscribe", params) async def unsubscribe_chains(self, chain_ids: list[str]): """ @@ -172,16 +148,13 @@ async def unsubscribe_chains(self, chain_ids: list[str]): Args: chain_ids: A list of chain IDs to unsubscribe from. """ - json_unsubscribe = { - "method": "unsubscribe", - "params": { - "chain_ids": chain_ids, - }, + params = { + "chain_ids": chain_ids, } - await self.send_ws_message(json_unsubscribe) + await self.send_ws_message("unsubscribe", params) async def submit_bid( - self, bid_info: BidInfo, subscribe_to_updates: bool = True, **kwargs + self, bid: Bid, subscribe_to_updates: bool = True, **kwargs ) -> UUID: """ Submits a bid to the auction server. @@ -193,13 +166,8 @@ async def submit_bid( Returns: The ID of the submitted bid. """ - bid_info_dict = bid_info.to_dict() if subscribe_to_updates: - json_submit_bid = { - "method": "post_bid", - "params": bid_info_dict, - } - result = await self.send_ws_message(json_submit_bid) + result = await self.send_ws_message("post_bid", {"bid": bid}) bid_id = UUID(result.get("id")) else: async with httpx.AsyncClient() as client: @@ -207,7 +175,7 @@ async def submit_bid( urllib.parse.urlparse(self.server_url) ._replace(path="/v1/bids") .geturl(), - json=bid_info.bid.to_dict(), + json=bid.to_dict(), **kwargs, ) @@ -232,15 +200,17 @@ async def submit_opportunity_bid( Returns: The ID of the submitted bid. """ - opportunity_bid_info_dict = opportunity_bid_info.to_dict() if subscribe_to_updates: - json_submit_opportunity_bid = { - "method": "post_liquidation_bid", - "params": opportunity_bid_info.to_dict(), - } - result = await self.send_ws_message(json_submit_opportunity_bid) + result = await self.send_ws_message( + "post_liquidation_bid", + { + "opportunity_id": opportunity_bid_info.opportunity_id, + "opportunity_bid": opportunity_bid_info.opportunity_bid, + }, + ) bid_id = UUID(result.get("id")) else: + opportunity_bid_info_dict = opportunity_bid_info.to_dict() opportunity_id = opportunity_bid_info_dict["opportunity_id"] opportunity_bid = opportunity_bid_info_dict["opportunity_bid"] @@ -263,9 +233,11 @@ async def submit_opportunity_bid( async def ws_handler( self, opportunity_callback: ( - Callable[[OpportunityParamsWithMetadata], None] | None + Callable[[OpportunityParamsWithMetadata], Coroutine[Any, Any, Any]] | None + ) = None, + bid_status_callback: ( + Callable[[BidStatusWithId], Coroutine[Any, Any, Any]] | None ) = None, - bid_status_callback: Callable[[BidStatusWithId], None] | None = None, ): """ Continually handles new ws messages as they are received from the server via websocket. @@ -278,26 +250,26 @@ async def ws_handler( raise ExpressRelayClientException("Websocket not connected") async for msg in self.ws: - msg = json.loads(msg) + msg_json = json.loads(msg) - if msg.get("type"): - if msg.get("type") == "new_opportunity": + if msg_json.get("type"): + if msg_json.get("type") == "new_opportunity": if opportunity_callback is not None: opportunity = OpportunityParamsWithMetadata.from_dict( - msg.get("opportunity") + msg_json.get("opportunity") ) asyncio.create_task(opportunity_callback(opportunity)) - elif msg.get("type") == "bid_status_update": + elif msg_json.get("type") == "bid_status_update": if bid_status_callback is not None: bid_status_with_id = BidStatusWithId.from_dict( - msg.get("status") + msg_json.get("status") ) asyncio.create_task(bid_status_callback(bid_status_with_id)) - elif msg.get("id"): - future = self.ws_msg_futures.pop(msg["id"]) - future.set_result(msg) + elif msg_json.get("id"): + future = self.ws_msg_futures.pop(msg_json["id"]) + future.set_result(msg_json) async def get_opportunities( self, chain_id: str | None = None, timeout_secs: int = 10 @@ -359,7 +331,7 @@ async def submit_opportunity( def sign_bid( liquidation_opportunity: OpportunityParamsWithMetadata, - bid: int, + bid_amount: int, valid_until: int, private_key: str, ) -> OpportunityBidInfo: @@ -368,21 +340,21 @@ def sign_bid( Args: liquidation_opportunity: An object representing the liquidation opportunity, of type OpportunityParamsWithMetadata. - bid: An integer representing the amount of the bid (in wei). + bid_amount: An integer representing the amount of the bid (in wei). valid_until: An integer representing the unix timestamp until which the bid is valid. private_key: A 0x-prefixed hex string representing the liquidator's private key. Returns: A OpportunityBidInfo object, representing the transaction to submit to the liquidation server. This object contains the liquidator's signature. """ repay_tokens = [ - (token.contract, int(token.amount)) + (token.contract.address, int(token.amount)) for token in liquidation_opportunity.repay_tokens ] receipt_tokens = [ - (token.contract, int(token.amount)) + (token.contract.address, int(token.amount)) for token in liquidation_opportunity.receipt_tokens ] - calldata = bytes.fromhex(liquidation_opportunity.calldata.replace("0x", "")) + calldata = bytes.fromhex(liquidation_opportunity.calldata.string.replace("0x", "")) digest = encode( [ @@ -397,27 +369,27 @@ def sign_bid( [ repay_tokens, receipt_tokens, - liquidation_opportunity.contract, + liquidation_opportunity.contract.address, calldata, int(liquidation_opportunity.value), - bid, + bid_amount, valid_until, ], ) msg_data = web3.Web3.solidity_keccak(["bytes"], [digest]) signature = w3.eth.account.signHash(msg_data, private_key=private_key) - opportunity_bid = { + opportunity_bid_dict = { "permission_key": liquidation_opportunity.permission_key, - "amount": str(bid), + "amount": str(bid_amount), "valid_until": str(valid_until), - "liquidator": Account.from_key(private_key).address, - "signature": bytes(signature.signature).hex(), + "liquidator": Address(address=Account.from_key(private_key).address), + "signature": signature, } - opportunity_bid = OpportunityBid.from_dict(opportunity_bid) + opportunity_bid = OpportunityBid.model_validate(opportunity_bid_dict) opportunity_bid_info = OpportunityBidInfo( - opportunity_id=UUID(liquidation_opportunity.opportunity_id), + opportunity_id=liquidation_opportunity.opportunity_id, opportunity_bid=opportunity_bid, ) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_types.py b/express_relay/sdk/python/express_relay_utils/express_relay_types.py new file mode 100644 index 0000000000..d4d2c6d4ba --- /dev/null +++ b/express_relay/sdk/python/express_relay_utils/express_relay_types.py @@ -0,0 +1,400 @@ +from enum import Enum +from pydantic import BaseModel, field_validator, model_validator +from uuid import UUID +import web3 +from typing import Union, Dict, Any +from pydantic import Field +from typing_extensions import Literal + +from eth_account.datastructures import SignedMessage + + +def assert_hex_string(s: str): + """ + Validates that a string is a valid hex string. + + Args: + s: The string to validate as a hex string. Can be '0x'-prefixed. + """ + if s.startswith("0x"): + s = s[2:] + + s_set = set(s) + hex_set = set("0123456789abcdefABCDEF") + + assert s_set.issubset(hex_set), "v must be a valid hex string" + + +class HexString(BaseModel): + """ + Args: + string: The string to validate as a hex string. + """ + + string: str + + @field_validator("string") + @classmethod + def check_hex(cls, v: str): + assert_hex_string(v) + + return v + + +class Address(BaseModel): + """ + Args: + address: The string to validate as an Ethereum address. + """ + + address: str + + @field_validator("address") + @classmethod + def check_address(cls, v: str): + assert web3.Web3.is_address(v), "s must be a valid Ethereum address" + return v + + +class Bytes32(BaseModel): + """ + Args: + string: The hex string to validate as a 32-byte hash. + """ + + string: str + + @field_validator("string") + @classmethod + def check_bytes32(cls, v: str): + assert_hex_string(v) + assert len(v.replace("0x", "")) == 64, "permission key must be 32 bytes long" + return v + + +class Bid(BaseModel): + """ + Args: + amount: The amount of the bid in wei. + calldata: The calldata for the contract call. + chain_id: The chain ID to bid on. + contract: The contract address to call. + permission_key: The permission key to bid on. + """ + + amount: int + calldata: HexString + chain_id: str + contract: Address + permission_key: HexString + + def to_dict(self): + return { + "amount": str(self.amount), + "calldata": self.calldata.string, + "chain_id": self.chain_id, + "contract": self.contract.address, + "permission_key": self.permission_key.string, + } + + +class Status(Enum): + SUBMITTED = "submitted" + LOST = "lost" + PENDING = "pending" + + +class BidStatus(BaseModel): + """ + Args: + status: The status tag, either 'submitted', 'lost', or 'pending'. + result: The result of the bid: a transaction hash if the status is 'submitted', else None. + """ + + status: Status + result: Bytes32 | None = Field(default=None) + + @model_validator(mode="after") + def check_result(self): + if self.status == Status("submitted"): + assert self.result is not None, "result must be a valid 32-byte hash" + else: + assert self.result is None, "result must be None" + return self + + +class BidStatusWithId(BaseModel): + """ + Args: + bid_status: The status of the bid. + id: The ID of the bid. + """ + + bid_status: BidStatus + id: UUID + + @classmethod + def from_dict(cls, obj: Dict[str, Any]): + bid_status_dict = obj["bid_status"] + status = bid_status_dict["status"] + result = bid_status_dict.get("result") + if result: + result = Bytes32(string=result) + obj["bid_status"] = BidStatus(status=status, result=result) + obj["id"] = UUID(obj["id"]) + + _obj = cls.model_validate({"bid_status": obj["bid_status"], "id": obj["id"]}) + + return _obj + + +class OpportunityBid(BaseModel): + """ + Args: + amount: The amount of the bid in wei. + liquidator: The address of the liquidator. + permission_key: The permission key to bid on. + signature: The signature of the bid. + valid_until: The unix timestamp after which the bid becomes invalid. + """ + + amount: int + liquidator: Address + permission_key: HexString + signature: SignedMessage + valid_until: int + + model_config = { + "arbitrary_types_allowed": True, + } + + def to_dict(self): + return { + "amount": str(self.amount), + "liquidator": self.liquidator.address, + "permission_key": self.permission_key.string, + "signature": bytes(self.signature.signature).hex(), + "valid_until": str(self.valid_until), + } + + +class OpportunityBidInfo(BaseModel): + """ + Args: + opportunity_id: The ID of the liquidation opportunity. + opportunity_bid: The bid to submit on the liquidation opportunity. + """ + + opportunity_id: UUID + opportunity_bid: OpportunityBid + + def to_dict(self): + return { + "opportunity_id": str(self.opportunity_id), + "opportunity_bid": self.opportunity_bid.to_dict(), + } + + +class TokenAmount(BaseModel): + """ + Args: + contract: The address of the token contract. + amount: The amount of the token. + """ + + contract: Address + amount: int + + +class OpportunityParamsV1(BaseModel): + """ + Args: + calldata: The calldata for the contract call. + chain_id: The chain ID to bid on. + contract: The contract address to call. + permission_key: The permission key to bid on. + receipt_tokens: The tokens to receive in the liquidation. + repay_tokens: The tokens to repay in the liquidation. + value: The value to send with the contract call. + version: The version of the opportunity. + """ + + calldata: HexString + chain_id: str + contract: Address + permission_key: HexString + receipt_tokens: list[TokenAmount] + repay_tokens: list[TokenAmount] + value: int + version: Literal["v1"] + + def to_dict(self): + return { + "calldata": self.calldata.string, + "chain_id": self.chain_id, + "contract": self.contract.address, + "permission_key": self.permission_key.string, + "receipt_tokens": [ + (token.contract.address, str(token.amount)) + for token in self.receipt_tokens + ], + "repay_tokens": [ + (token.contract.address, str(token.amount)) + for token in self.repay_tokens + ], + "value": str(self.value), + "version": self.version, + } + + +class OpportunityParams(BaseModel): + """ + Args: + params: The parameters of the opportunity. + """ + + params: Union[OpportunityParamsV1] = Field(..., discriminator="version") + + def to_dict(self): + return self.params.to_dict() + + +class OpportunityParamsWithMetadata(BaseModel): + """ + Args: + calldata: The calldata for the contract call. + chain_id: The chain ID to bid on. + contract: The contract address to call. + permission_key: The permission key to bid on. + receipt_tokens: The tokens to receive in the liquidation. + repay_tokens: The tokens to repay in the liquidation. + value: The value to send with the contract call. + version: The version of the opportunity. + creation_time: The creation time of the opportunity. + opportunity_id: The ID of the opportunity. + """ + + calldata: HexString + chain_id: str + contract: Address + permission_key: HexString + receipt_tokens: list[TokenAmount] + repay_tokens: list[TokenAmount] + value: int + version: str + creation_time: int + opportunity_id: UUID + + @classmethod + def from_dict(cls, obj: Dict[str, Any]): + obj["calldata"] = HexString(string=obj["calldata"]) + obj["contract"] = Address(address=obj["contract"]) + obj["permission_key"] = HexString(string=obj["permission_key"]) + obj["receipt_tokens"] = [ + TokenAmount( + contract=Address(address=token["contract"]), amount=token["amount"] + ) + for token in obj["receipt_tokens"] + ] + obj["repay_tokens"] = [ + TokenAmount( + contract=Address(address=token["contract"]), amount=token["amount"] + ) + for token in obj["repay_tokens"] + ] + obj["opportunity_id"] = UUID(obj["opportunity_id"]) + + _obj = cls.model_validate( + { + "calldata": obj["calldata"], + "chain_id": obj["chain_id"], + "contract": obj["contract"], + "permission_key": obj["permission_key"], + "receipt_tokens": obj["receipt_tokens"], + "repay_tokens": obj["repay_tokens"], + "value": obj["value"], + "version": obj["version"], + "creation_time": obj["creation_time"], + "opportunity_id": obj["opportunity_id"], + } + ) + + return _obj + + +class SubscribeMessage(BaseModel): + """ + Args: + method: A string literal "subscribe". + chain_ids: The chain IDs to subscribe to. + """ + + method: Literal["subscribe"] + chain_ids: list[str] + + def to_dict(self): + return {"method": str(self.method), "params": {"chain_ids": self.chain_ids}} + + +class UnsubscribeMessage(BaseModel): + """ + Args: + method: A string literal "unsubscribe". + chain_ids: The chain IDs to subscribe to. + """ + + method: Literal["unsubscribe"] + chain_ids: list[str] + + def to_dict(self): + return {"method": str(self.method), "params": {"chain_ids": self.chain_ids}} + + +class PostBidMessage(BaseModel): + """ + Args: + method: A string literal "post_bid". + bid: The bid to post. + """ + + method: Literal["post_bid"] + bid: Bid + + def to_dict(self): + return {"method": str(self.method), "params": {"bid": self.bid.to_dict()}} + + +class PostLiquidationBidMessage(BaseModel): + """ + Args: + method: A string literal "post_liquidation_bid". + opportunity_id: The ID of the opportunity. + opportunity_bid: The bid to post on the opportunity. + """ + + method: Literal["post_liquidation_bid"] + opportunity_id: UUID + opportunity_bid: OpportunityBid + + def to_dict(self): + return { + "method": str(self.method), + "params": { + "opportunity_id": str(self.opportunity_id), + "opportunity_bid": self.opportunity_bid.to_dict(), + }, + } + + +class ClientMessage(BaseModel): + """ + Args: + params: The parameters of the message. + """ + + params: Union[ + SubscribeMessage, UnsubscribeMessage, PostBidMessage, PostLiquidationBidMessage + ] = Field(..., discriminator="method") + + def to_dict(self): + return self.params.to_dict() diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py deleted file mode 100644 index e60f023cad..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/api_response.py +++ /dev/null @@ -1,123 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.bid_result import BidResult -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -APIRESPONSE_ONE_OF_SCHEMAS = ["BidResult"] - -class APIResponse(BaseModel): - """ - APIResponse - """ - # data type: BidResult - oneof_schema_1_validator: Optional[BidResult] = None - actual_instance: Optional[Union[BidResult]] = None - one_of_schemas: List[str] = Field(default=Literal["BidResult"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = APIResponse.model_construct() - error_messages = [] - match = 0 - # validate data type: BidResult - if not isinstance(v, BidResult): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidResult`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into BidResult - try: - instance.actual_instance = BidResult.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into APIResponse with oneOf schemas: BidResult. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], BidResult]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py deleted file mode 100644 index ca8ac69cb9..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid.py +++ /dev/null @@ -1,95 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, Field, StrictStr -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class Bid(BaseModel): - """ - Bid - """ # noqa: E501 - amount: StrictStr = Field(description="Amount of bid in wei.") - calldata: StrictStr = Field(description="Calldata for the contract call.") - chain_id: StrictStr = Field(description="The chain id to bid on.") - contract: StrictStr = Field(description="The contract address to call.") - permission_key: StrictStr = Field(description="The permission key to bid on.") - __properties: ClassVar[List[str]] = ["amount", "calldata", "chain_id", "contract", "permission_key"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of Bid from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of Bid from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "amount": obj.get("amount"), - "calldata": obj.get("calldata"), - "chain_id": obj.get("chain_id"), - "contract": obj.get("contract"), - "permission_key": obj.get("permission_key") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py deleted file mode 100644 index 4f105c4701..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid400_response.py +++ /dev/null @@ -1,87 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class Bid400Response(BaseModel): - """ - Bid400Response - """ # noqa: E501 - error: StrictStr - __properties: ClassVar[List[str]] = ["error"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of Bid400Response from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of Bid400Response from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "error": obj.get("error") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py deleted file mode 100644 index 7a8831d97e..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_result.py +++ /dev/null @@ -1,89 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, Field, StrictStr -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class BidResult(BaseModel): - """ - BidResult - """ # noqa: E501 - id: StrictStr = Field(description="The unique id created to identify the bid. This id can be used to query the status of the bid.") - status: StrictStr - __properties: ClassVar[List[str]] = ["id", "status"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of BidResult from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of BidResult from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "id": obj.get("id"), - "status": obj.get("status") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py deleted file mode 100644 index 0e36c51c10..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status.py +++ /dev/null @@ -1,154 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.bid_status_one_of import BidStatusOneOf -from openapi_client.models.bid_status_one_of1 import BidStatusOneOf1 -from openapi_client.models.bid_status_one_of2 import BidStatusOneOf2 -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -BIDSTATUS_ONE_OF_SCHEMAS = ["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"] - -class BidStatus(BaseModel): - """ - BidStatus - """ - # data type: BidStatusOneOf - oneof_schema_1_validator: Optional[BidStatusOneOf] = None - # data type: BidStatusOneOf1 - oneof_schema_2_validator: Optional[BidStatusOneOf1] = None - # data type: BidStatusOneOf2 - oneof_schema_3_validator: Optional[BidStatusOneOf2] = None - actual_instance: Optional[Union[BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]] = None - one_of_schemas: List[str] = Field(default=Literal["BidStatusOneOf", "BidStatusOneOf1", "BidStatusOneOf2"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - discriminator_value_class_map: Dict[str, str] = { - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = BidStatus.model_construct() - error_messages = [] - match = 0 - # validate data type: BidStatusOneOf - if not isinstance(v, BidStatusOneOf): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf`") - else: - match += 1 - # validate data type: BidStatusOneOf1 - if not isinstance(v, BidStatusOneOf1): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf1`") - else: - match += 1 - # validate data type: BidStatusOneOf2 - if not isinstance(v, BidStatusOneOf2): - error_messages.append(f"Error! Input type `{type(v)}` is not `BidStatusOneOf2`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in BidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in BidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into BidStatusOneOf - try: - instance.actual_instance = BidStatusOneOf.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into BidStatusOneOf1 - try: - instance.actual_instance = BidStatusOneOf1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into BidStatusOneOf2 - try: - instance.actual_instance = BidStatusOneOf2.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into BidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into BidStatus with oneOf schemas: BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], BidStatusOneOf, BidStatusOneOf1, BidStatusOneOf2]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py deleted file mode 100644 index 6fc96fc6d5..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of.py +++ /dev/null @@ -1,94 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class BidStatusOneOf(BaseModel): - """ - BidStatusOneOf - """ # noqa: E501 - status: StrictStr - __properties: ClassVar[List[str]] = ["status"] - - @field_validator('status') - def status_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['pending']): - raise ValueError("must be one of enum values ('pending')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of BidStatusOneOf from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of BidStatusOneOf from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "status": obj.get("status") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py deleted file mode 100644 index abd0dd479c..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of1.py +++ /dev/null @@ -1,96 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, Field, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class BidStatusOneOf1(BaseModel): - """ - BidStatusOneOf1 - """ # noqa: E501 - result: StrictStr = Field(description="The bid won the auction and was submitted to the chain in a transaction with the given hash") - status: StrictStr - __properties: ClassVar[List[str]] = ["result", "status"] - - @field_validator('status') - def status_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['submitted']): - raise ValueError("must be one of enum values ('submitted')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of BidStatusOneOf1 from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of BidStatusOneOf1 from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "result": obj.get("result"), - "status": obj.get("status") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py deleted file mode 100644 index 205db3dbed..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_one_of2.py +++ /dev/null @@ -1,94 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class BidStatusOneOf2(BaseModel): - """ - BidStatusOneOf2 - """ # noqa: E501 - status: StrictStr - __properties: ClassVar[List[str]] = ["status"] - - @field_validator('status') - def status_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['lost']): - raise ValueError("must be one of enum values ('lost')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of BidStatusOneOf2 from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of BidStatusOneOf2 from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "status": obj.get("status") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py deleted file mode 100644 index e7bbee11fa..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/bid_status_with_id.py +++ /dev/null @@ -1,93 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr -from typing import Any, ClassVar, Dict, List -from openapi_client.models.bid_status import BidStatus -from typing import Optional, Set -from typing_extensions import Self - -class BidStatusWithId(BaseModel): - """ - BidStatusWithId - """ # noqa: E501 - bid_status: BidStatus - id: StrictStr - __properties: ClassVar[List[str]] = ["bid_status", "id"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of BidStatusWithId from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of bid_status - if self.bid_status: - _dict['bid_status'] = self.bid_status.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of BidStatusWithId from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "bid_status": BidStatus.from_dict(obj["bid_status"]) if obj.get("bid_status") is not None else None, - "id": obj.get("id") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py deleted file mode 100644 index aa9a65e46c..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message.py +++ /dev/null @@ -1,169 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.client_message_one_of import ClientMessageOneOf -from openapi_client.models.client_message_one_of1 import ClientMessageOneOf1 -from openapi_client.models.client_message_one_of2 import ClientMessageOneOf2 -from openapi_client.models.client_message_one_of3 import ClientMessageOneOf3 -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -CLIENTMESSAGE_ONE_OF_SCHEMAS = ["ClientMessageOneOf", "ClientMessageOneOf1", "ClientMessageOneOf2", "ClientMessageOneOf3"] - -class ClientMessage(BaseModel): - """ - ClientMessage - """ - # data type: ClientMessageOneOf - oneof_schema_1_validator: Optional[ClientMessageOneOf] = None - # data type: ClientMessageOneOf1 - oneof_schema_2_validator: Optional[ClientMessageOneOf1] = None - # data type: ClientMessageOneOf2 - oneof_schema_3_validator: Optional[ClientMessageOneOf2] = None - # data type: ClientMessageOneOf3 - oneof_schema_4_validator: Optional[ClientMessageOneOf3] = None - actual_instance: Optional[Union[ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3]] = None - one_of_schemas: List[str] = Field(default=Literal["ClientMessageOneOf", "ClientMessageOneOf1", "ClientMessageOneOf2", "ClientMessageOneOf3"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - discriminator_value_class_map: Dict[str, str] = { - 'ClientRequest': 'ClientRequest' - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = ClientMessage.model_construct() - error_messages = [] - match = 0 - # validate data type: ClientMessageOneOf - if not isinstance(v, ClientMessageOneOf): - error_messages.append(f"Error! Input type `{type(v)}` is not `ClientMessageOneOf`") - else: - match += 1 - # validate data type: ClientMessageOneOf1 - if not isinstance(v, ClientMessageOneOf1): - error_messages.append(f"Error! Input type `{type(v)}` is not `ClientMessageOneOf1`") - else: - match += 1 - # validate data type: ClientMessageOneOf2 - if not isinstance(v, ClientMessageOneOf2): - error_messages.append(f"Error! Input type `{type(v)}` is not `ClientMessageOneOf2`") - else: - match += 1 - # validate data type: ClientMessageOneOf3 - if not isinstance(v, ClientMessageOneOf3): - error_messages.append(f"Error! Input type `{type(v)}` is not `ClientMessageOneOf3`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into ClientMessageOneOf - try: - instance.actual_instance = ClientMessageOneOf.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into ClientMessageOneOf1 - try: - instance.actual_instance = ClientMessageOneOf1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into ClientMessageOneOf2 - try: - instance.actual_instance = ClientMessageOneOf2.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into ClientMessageOneOf3 - try: - instance.actual_instance = ClientMessageOneOf3.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into ClientMessage with oneOf schemas: ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], ClientMessageOneOf, ClientMessageOneOf1, ClientMessageOneOf2, ClientMessageOneOf3]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py deleted file mode 100644 index 70c89a2b25..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of.py +++ /dev/null @@ -1,100 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from openapi_client.models.client_message_one_of_params import ClientMessageOneOfParams -from typing import Optional, Set -from typing_extensions import Self - -class ClientMessageOneOf(BaseModel): - """ - ClientMessageOneOf - """ # noqa: E501 - method: StrictStr - params: ClientMessageOneOfParams - __properties: ClassVar[List[str]] = ["method", "params"] - - @field_validator('method') - def method_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['subscribe']): - raise ValueError("must be one of enum values ('subscribe')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ClientMessageOneOf from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of params - if self.params: - _dict['params'] = self.params.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ClientMessageOneOf from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "method": obj.get("method"), - "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py deleted file mode 100644 index 7afdd366a2..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of1.py +++ /dev/null @@ -1,100 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from openapi_client.models.client_message_one_of_params import ClientMessageOneOfParams -from typing import Optional, Set -from typing_extensions import Self - -class ClientMessageOneOf1(BaseModel): - """ - ClientMessageOneOf1 - """ # noqa: E501 - method: StrictStr - params: ClientMessageOneOfParams - __properties: ClassVar[List[str]] = ["method", "params"] - - @field_validator('method') - def method_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['unsubscribe']): - raise ValueError("must be one of enum values ('unsubscribe')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ClientMessageOneOf1 from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of params - if self.params: - _dict['params'] = self.params.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ClientMessageOneOf1 from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "method": obj.get("method"), - "params": ClientMessageOneOfParams.from_dict(obj["params"]) if obj.get("params") is not None else None - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py deleted file mode 100644 index d62f1859b3..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2.py +++ /dev/null @@ -1,100 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from openapi_client.models.client_message_one_of2_params import ClientMessageOneOf2Params -from typing import Optional, Set -from typing_extensions import Self - -class ClientMessageOneOf2(BaseModel): - """ - ClientMessageOneOf2 - """ # noqa: E501 - method: StrictStr - params: ClientMessageOneOf2Params - __properties: ClassVar[List[str]] = ["method", "params"] - - @field_validator('method') - def method_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['post_bid']): - raise ValueError("must be one of enum values ('post_bid')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ClientMessageOneOf2 from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of params - if self.params: - _dict['params'] = self.params.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ClientMessageOneOf2 from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "method": obj.get("method"), - "params": ClientMessageOneOf2Params.from_dict(obj["params"]) if obj.get("params") is not None else None - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py deleted file mode 100644 index 1b76bacf93..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of2_params.py +++ /dev/null @@ -1,91 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel -from typing import Any, ClassVar, Dict, List -from openapi_client.models.bid import Bid -from typing import Optional, Set -from typing_extensions import Self - -class ClientMessageOneOf2Params(BaseModel): - """ - ClientMessageOneOf2Params - """ # noqa: E501 - bid: Bid - __properties: ClassVar[List[str]] = ["bid"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ClientMessageOneOf2Params from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of bid - if self.bid: - _dict['bid'] = self.bid.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ClientMessageOneOf2Params from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "bid": Bid.from_dict(obj["bid"]) if obj.get("bid") is not None else None - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py deleted file mode 100644 index 7ae631db79..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3.py +++ /dev/null @@ -1,100 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from openapi_client.models.client_message_one_of3_params import ClientMessageOneOf3Params -from typing import Optional, Set -from typing_extensions import Self - -class ClientMessageOneOf3(BaseModel): - """ - ClientMessageOneOf3 - """ # noqa: E501 - method: StrictStr - params: ClientMessageOneOf3Params - __properties: ClassVar[List[str]] = ["method", "params"] - - @field_validator('method') - def method_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['post_liquidation_bid']): - raise ValueError("must be one of enum values ('post_liquidation_bid')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ClientMessageOneOf3 from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of params - if self.params: - _dict['params'] = self.params.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ClientMessageOneOf3 from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "method": obj.get("method"), - "params": ClientMessageOneOf3Params.from_dict(obj["params"]) if obj.get("params") is not None else None - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py deleted file mode 100644 index 325f0de00b..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of3_params.py +++ /dev/null @@ -1,93 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr -from typing import Any, ClassVar, Dict, List -from openapi_client.models.opportunity_bid import OpportunityBid -from typing import Optional, Set -from typing_extensions import Self - -class ClientMessageOneOf3Params(BaseModel): - """ - ClientMessageOneOf3Params - """ # noqa: E501 - opportunity_bid: OpportunityBid - opportunity_id: StrictStr - __properties: ClassVar[List[str]] = ["opportunity_bid", "opportunity_id"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ClientMessageOneOf3Params from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of opportunity_bid - if self.opportunity_bid: - _dict['opportunity_bid'] = self.opportunity_bid.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ClientMessageOneOf3Params from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "opportunity_bid": OpportunityBid.from_dict(obj["opportunity_bid"]) if obj.get("opportunity_bid") is not None else None, - "opportunity_id": obj.get("opportunity_id") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py deleted file mode 100644 index c4b59cf971..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_message_one_of_params.py +++ /dev/null @@ -1,87 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class ClientMessageOneOfParams(BaseModel): - """ - ClientMessageOneOfParams - """ # noqa: E501 - chain_ids: List[StrictStr] - __properties: ClassVar[List[str]] = ["chain_ids"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ClientMessageOneOfParams from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ClientMessageOneOfParams from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "chain_ids": obj.get("chain_ids") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py deleted file mode 100644 index 6ca1f7a88b..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/client_request.py +++ /dev/null @@ -1,94 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import StrictStr -from typing import Any, ClassVar, Dict, List -from openapi_client.models.client_message import ClientMessage -from openapi_client.models.client_message_one_of3_params import ClientMessageOneOf3Params -from typing import Optional, Set -from typing_extensions import Self - -class ClientRequest(ClientMessage): - """ - ClientRequest - """ # noqa: E501 - id: StrictStr - __properties: ClassVar[List[str]] = ["method", "params", "id"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ClientRequest from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of params - if self.params: - _dict['params'] = self.params.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ClientRequest from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "method": obj.get("method"), - "params": ClientMessageOneOf3Params.from_dict(obj["params"]) if obj.get("params") is not None else None, - "id": obj.get("id") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py deleted file mode 100644 index 7dc15e20e5..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/error_body_response.py +++ /dev/null @@ -1,87 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class ErrorBodyResponse(BaseModel): - """ - ErrorBodyResponse - """ # noqa: E501 - error: StrictStr - __properties: ClassVar[List[str]] = ["error"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ErrorBodyResponse from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ErrorBodyResponse from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "error": obj.get("error") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py deleted file mode 100644 index 5e4432b5e6..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_bid.py +++ /dev/null @@ -1,95 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, Field, StrictStr -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class OpportunityBid(BaseModel): - """ - OpportunityBid - """ # noqa: E501 - amount: StrictStr = Field(description="The bid amount in wei.") - liquidator: StrictStr = Field(description="Liquidator address") - permission_key: StrictStr = Field(description="The opportunity permission key") - signature: StrictStr - valid_until: StrictStr = Field(description="How long the bid will be valid for.") - __properties: ClassVar[List[str]] = ["amount", "liquidator", "permission_key", "signature", "valid_until"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of OpportunityBid from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of OpportunityBid from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "amount": obj.get("amount"), - "liquidator": obj.get("liquidator"), - "permission_key": obj.get("permission_key"), - "signature": obj.get("signature"), - "valid_until": obj.get("valid_until") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py deleted file mode 100644 index f3aeaad06a..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params.py +++ /dev/null @@ -1,126 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.opportunity_params_one_of import OpportunityParamsOneOf -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -OPPORTUNITYPARAMS_ONE_OF_SCHEMAS = ["OpportunityParamsOneOf"] - -class OpportunityParams(BaseModel): - """ - OpportunityParams - """ - # data type: OpportunityParamsOneOf - oneof_schema_1_validator: Optional[OpportunityParamsOneOf] = None - actual_instance: Optional[Union[OpportunityParamsOneOf]] = None - one_of_schemas: List[str] = Field(default=Literal["OpportunityParamsOneOf"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - discriminator_value_class_map: Dict[str, str] = { - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = OpportunityParams.model_construct() - error_messages = [] - match = 0 - # validate data type: OpportunityParamsOneOf - if not isinstance(v, OpportunityParamsOneOf): - error_messages.append(f"Error! Input type `{type(v)}` is not `OpportunityParamsOneOf`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in OpportunityParams with oneOf schemas: OpportunityParamsOneOf. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in OpportunityParams with oneOf schemas: OpportunityParamsOneOf. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into OpportunityParamsOneOf - try: - instance.actual_instance = OpportunityParamsOneOf.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into OpportunityParams with oneOf schemas: OpportunityParamsOneOf. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into OpportunityParams with oneOf schemas: OpportunityParamsOneOf. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], OpportunityParamsOneOf]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py deleted file mode 100644 index c346501c27..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_one_of.py +++ /dev/null @@ -1,123 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, Field, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from openapi_client.models.token_qty import TokenQty -from typing import Optional, Set -from typing_extensions import Self - -class OpportunityParamsOneOf(BaseModel): - """ - OpportunityParamsOneOf - """ # noqa: E501 - calldata: StrictStr = Field(description="Calldata for the contract call.") - chain_id: StrictStr = Field(description="The chain id where the liquidation will be executed.") - contract: StrictStr = Field(description="The contract address to call for execution of the liquidation.") - permission_key: StrictStr = Field(description="The permission key required for succesful execution of the liquidation.") - receipt_tokens: List[TokenQty] - repay_tokens: List[TokenQty] - value: StrictStr = Field(description="The value to send with the contract call.") - version: StrictStr - __properties: ClassVar[List[str]] = ["calldata", "chain_id", "contract", "permission_key", "receipt_tokens", "repay_tokens", "value", "version"] - - @field_validator('version') - def version_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['v1']): - raise ValueError("must be one of enum values ('v1')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of OpportunityParamsOneOf from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of each item in receipt_tokens (list) - _items = [] - if self.receipt_tokens: - for _item in self.receipt_tokens: - if _item: - _items.append(_item.to_dict()) - _dict['receipt_tokens'] = _items - # override the default output from pydantic by calling `to_dict()` of each item in repay_tokens (list) - _items = [] - if self.repay_tokens: - for _item in self.repay_tokens: - if _item: - _items.append(_item.to_dict()) - _dict['repay_tokens'] = _items - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of OpportunityParamsOneOf from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "calldata": obj.get("calldata"), - "chain_id": obj.get("chain_id"), - "contract": obj.get("contract"), - "permission_key": obj.get("permission_key"), - "receipt_tokens": [TokenQty.from_dict(_item) for _item in obj["receipt_tokens"]] if obj.get("receipt_tokens") is not None else None, - "repay_tokens": [TokenQty.from_dict(_item) for _item in obj["repay_tokens"]] if obj.get("repay_tokens") is not None else None, - "value": obj.get("value"), - "version": obj.get("version") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py deleted file mode 100644 index 9fe3b11a38..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_v1.py +++ /dev/null @@ -1,114 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, Field, StrictStr -from typing import Any, ClassVar, Dict, List -from openapi_client.models.token_qty import TokenQty -from typing import Optional, Set -from typing_extensions import Self - -class OpportunityParamsV1(BaseModel): - """ - Opportunity parameters needed for on-chain execution If a searcher signs the opportunity and have approved enough tokens to liquidation adapter, by calling this contract with the given calldata and structures, they will receive the tokens specified in the receipt_tokens field, and will send the tokens specified in the repay_tokens field. - """ # noqa: E501 - calldata: StrictStr = Field(description="Calldata for the contract call.") - chain_id: StrictStr = Field(description="The chain id where the liquidation will be executed.") - contract: StrictStr = Field(description="The contract address to call for execution of the liquidation.") - permission_key: StrictStr = Field(description="The permission key required for succesful execution of the liquidation.") - receipt_tokens: List[TokenQty] - repay_tokens: List[TokenQty] - value: StrictStr = Field(description="The value to send with the contract call.") - __properties: ClassVar[List[str]] = ["calldata", "chain_id", "contract", "permission_key", "receipt_tokens", "repay_tokens", "value"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of OpportunityParamsV1 from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of each item in receipt_tokens (list) - _items = [] - if self.receipt_tokens: - for _item in self.receipt_tokens: - if _item: - _items.append(_item.to_dict()) - _dict['receipt_tokens'] = _items - # override the default output from pydantic by calling `to_dict()` of each item in repay_tokens (list) - _items = [] - if self.repay_tokens: - for _item in self.repay_tokens: - if _item: - _items.append(_item.to_dict()) - _dict['repay_tokens'] = _items - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of OpportunityParamsV1 from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "calldata": obj.get("calldata"), - "chain_id": obj.get("chain_id"), - "contract": obj.get("contract"), - "permission_key": obj.get("permission_key"), - "receipt_tokens": [TokenQty.from_dict(_item) for _item in obj["receipt_tokens"]] if obj.get("receipt_tokens") is not None else None, - "repay_tokens": [TokenQty.from_dict(_item) for _item in obj["repay_tokens"]] if obj.get("repay_tokens") is not None else None, - "value": obj.get("value") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py deleted file mode 100644 index 524caa68fb..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/opportunity_params_with_metadata.py +++ /dev/null @@ -1,127 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, Field, StrictInt, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from openapi_client.models.token_qty import TokenQty -from typing import Optional, Set -from typing_extensions import Self - -class OpportunityParamsWithMetadata(BaseModel): - """ - Similar to OpportunityParams, but with the opportunity id included. - """ # noqa: E501 - calldata: StrictStr = Field(description="Calldata for the contract call.") - chain_id: StrictStr = Field(description="The chain id where the liquidation will be executed.") - contract: StrictStr = Field(description="The contract address to call for execution of the liquidation.") - permission_key: StrictStr = Field(description="The permission key required for succesful execution of the liquidation.") - receipt_tokens: List[TokenQty] - repay_tokens: List[TokenQty] - value: StrictStr = Field(description="The value to send with the contract call.") - version: StrictStr - creation_time: StrictInt = Field(description="Creation time of the opportunity") - opportunity_id: StrictStr = Field(description="The opportunity unique id") - __properties: ClassVar[List[str]] = ["calldata", "chain_id", "contract", "permission_key", "receipt_tokens", "repay_tokens", "value", "version", "creation_time", "opportunity_id"] - - @field_validator('version') - def version_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['v1']): - raise ValueError("must be one of enum values ('v1')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of OpportunityParamsWithMetadata from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of each item in receipt_tokens (list) - _items = [] - if self.receipt_tokens: - for _item in self.receipt_tokens: - if _item: - _items.append(_item.to_dict()) - _dict['receipt_tokens'] = _items - # override the default output from pydantic by calling `to_dict()` of each item in repay_tokens (list) - _items = [] - if self.repay_tokens: - for _item in self.repay_tokens: - if _item: - _items.append(_item.to_dict()) - _dict['repay_tokens'] = _items - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of OpportunityParamsWithMetadata from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "calldata": obj.get("calldata"), - "chain_id": obj.get("chain_id"), - "contract": obj.get("contract"), - "permission_key": obj.get("permission_key"), - "receipt_tokens": [TokenQty.from_dict(_item) for _item in obj["receipt_tokens"]] if obj.get("receipt_tokens") is not None else None, - "repay_tokens": [TokenQty.from_dict(_item) for _item in obj["repay_tokens"]] if obj.get("repay_tokens") is not None else None, - "value": obj.get("value"), - "version": obj.get("version"), - "creation_time": obj.get("creation_time"), - "opportunity_id": obj.get("opportunity_id") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py deleted file mode 100644 index 1654f23f2e..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message.py +++ /dev/null @@ -1,141 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.server_result_message_one_of import ServerResultMessageOneOf -from openapi_client.models.server_result_message_one_of1 import ServerResultMessageOneOf1 -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -SERVERRESULTMESSAGE_ONE_OF_SCHEMAS = ["ServerResultMessageOneOf", "ServerResultMessageOneOf1"] - -class ServerResultMessage(BaseModel): - """ - ServerResultMessage - """ - # data type: ServerResultMessageOneOf - oneof_schema_1_validator: Optional[ServerResultMessageOneOf] = None - # data type: ServerResultMessageOneOf1 - oneof_schema_2_validator: Optional[ServerResultMessageOneOf1] = None - actual_instance: Optional[Union[ServerResultMessageOneOf, ServerResultMessageOneOf1]] = None - one_of_schemas: List[str] = Field(default=Literal["ServerResultMessageOneOf", "ServerResultMessageOneOf1"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - discriminator_value_class_map: Dict[str, str] = { - 'ServerResultResponse': 'ServerResultResponse' - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = ServerResultMessage.model_construct() - error_messages = [] - match = 0 - # validate data type: ServerResultMessageOneOf - if not isinstance(v, ServerResultMessageOneOf): - error_messages.append(f"Error! Input type `{type(v)}` is not `ServerResultMessageOneOf`") - else: - match += 1 - # validate data type: ServerResultMessageOneOf1 - if not isinstance(v, ServerResultMessageOneOf1): - error_messages.append(f"Error! Input type `{type(v)}` is not `ServerResultMessageOneOf1`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in ServerResultMessage with oneOf schemas: ServerResultMessageOneOf, ServerResultMessageOneOf1. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in ServerResultMessage with oneOf schemas: ServerResultMessageOneOf, ServerResultMessageOneOf1. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into ServerResultMessageOneOf - try: - instance.actual_instance = ServerResultMessageOneOf.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into ServerResultMessageOneOf1 - try: - instance.actual_instance = ServerResultMessageOneOf1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into ServerResultMessage with oneOf schemas: ServerResultMessageOneOf, ServerResultMessageOneOf1. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into ServerResultMessage with oneOf schemas: ServerResultMessageOneOf, ServerResultMessageOneOf1. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], ServerResultMessageOneOf, ServerResultMessageOneOf1]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py deleted file mode 100644 index 49523bff2a..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of.py +++ /dev/null @@ -1,105 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List, Optional -from openapi_client.models.api_response import APIResponse -from typing import Optional, Set -from typing_extensions import Self - -class ServerResultMessageOneOf(BaseModel): - """ - ServerResultMessageOneOf - """ # noqa: E501 - result: Optional[APIResponse] - status: StrictStr - __properties: ClassVar[List[str]] = ["result", "status"] - - @field_validator('status') - def status_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['success']): - raise ValueError("must be one of enum values ('success')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ServerResultMessageOneOf from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of result - if self.result: - _dict['result'] = self.result.to_dict() - # set to None if result (nullable) is None - # and model_fields_set contains the field - if self.result is None and "result" in self.model_fields_set: - _dict['result'] = None - - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ServerResultMessageOneOf from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "result": APIResponse.from_dict(obj["result"]) if obj.get("result") is not None else None, - "status": obj.get("status") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py deleted file mode 100644 index dc84c41f8e..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_message_one_of1.py +++ /dev/null @@ -1,96 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class ServerResultMessageOneOf1(BaseModel): - """ - ServerResultMessageOneOf1 - """ # noqa: E501 - result: StrictStr - status: StrictStr - __properties: ClassVar[List[str]] = ["result", "status"] - - @field_validator('status') - def status_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['error']): - raise ValueError("must be one of enum values ('error')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ServerResultMessageOneOf1 from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ServerResultMessageOneOf1 from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "result": obj.get("result"), - "status": obj.get("status") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py deleted file mode 100644 index a761094f09..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_result_response.py +++ /dev/null @@ -1,95 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import StrictStr -from typing import Any, ClassVar, Dict, List, Optional -from openapi_client.models.server_result_message import ServerResultMessage -from typing import Optional, Set -from typing_extensions import Self - -class ServerResultResponse(ServerResultMessage): - """ - This enum is used to send the result for a specific client request with the same id id is only None when the client message is invalid - """ # noqa: E501 - id: Optional[StrictStr] = None - __properties: ClassVar[List[str]] = ["result", "status", "id"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ServerResultResponse from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # set to None if id (nullable) is None - # and model_fields_set contains the field - if self.id is None and "id" in self.model_fields_set: - _dict['id'] = None - - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ServerResultResponse from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "result": obj.get("result"), - "status": obj.get("status"), - "id": obj.get("id") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py deleted file mode 100644 index ff8efa7136..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response.py +++ /dev/null @@ -1,140 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import json -import pprint -from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from openapi_client.models.server_update_response_one_of import ServerUpdateResponseOneOf -from openapi_client.models.server_update_response_one_of1 import ServerUpdateResponseOneOf1 -from pydantic import StrictStr, Field -from typing import Union, List, Optional, Dict -from typing_extensions import Literal, Self - -SERVERUPDATERESPONSE_ONE_OF_SCHEMAS = ["ServerUpdateResponseOneOf", "ServerUpdateResponseOneOf1"] - -class ServerUpdateResponse(BaseModel): - """ - This enum is used to send an update to the client for any subscriptions made - """ - # data type: ServerUpdateResponseOneOf - oneof_schema_1_validator: Optional[ServerUpdateResponseOneOf] = None - # data type: ServerUpdateResponseOneOf1 - oneof_schema_2_validator: Optional[ServerUpdateResponseOneOf1] = None - actual_instance: Optional[Union[ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1]] = None - one_of_schemas: List[str] = Field(default=Literal["ServerUpdateResponseOneOf", "ServerUpdateResponseOneOf1"]) - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - - discriminator_value_class_map: Dict[str, str] = { - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = ServerUpdateResponse.model_construct() - error_messages = [] - match = 0 - # validate data type: ServerUpdateResponseOneOf - if not isinstance(v, ServerUpdateResponseOneOf): - error_messages.append(f"Error! Input type `{type(v)}` is not `ServerUpdateResponseOneOf`") - else: - match += 1 - # validate data type: ServerUpdateResponseOneOf1 - if not isinstance(v, ServerUpdateResponseOneOf1): - error_messages.append(f"Error! Input type `{type(v)}` is not `ServerUpdateResponseOneOf1`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) - - @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into ServerUpdateResponseOneOf - try: - instance.actual_instance = ServerUpdateResponseOneOf.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into ServerUpdateResponseOneOf1 - try: - instance.actual_instance = ServerUpdateResponseOneOf1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into ServerUpdateResponse with oneOf schemas: ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], ServerUpdateResponseOneOf, ServerUpdateResponseOneOf1]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py deleted file mode 100644 index 160146ea35..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of.py +++ /dev/null @@ -1,100 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from openapi_client.models.opportunity_params_with_metadata import OpportunityParamsWithMetadata -from typing import Optional, Set -from typing_extensions import Self - -class ServerUpdateResponseOneOf(BaseModel): - """ - ServerUpdateResponseOneOf - """ # noqa: E501 - opportunity: OpportunityParamsWithMetadata - type: StrictStr - __properties: ClassVar[List[str]] = ["opportunity", "type"] - - @field_validator('type') - def type_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['new_opportunity']): - raise ValueError("must be one of enum values ('new_opportunity')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ServerUpdateResponseOneOf from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of opportunity - if self.opportunity: - _dict['opportunity'] = self.opportunity.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ServerUpdateResponseOneOf from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "opportunity": OpportunityParamsWithMetadata.from_dict(obj["opportunity"]) if obj.get("opportunity") is not None else None, - "type": obj.get("type") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py deleted file mode 100644 index 731c607438..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/server_update_response_one_of1.py +++ /dev/null @@ -1,100 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, StrictStr, field_validator -from typing import Any, ClassVar, Dict, List -from openapi_client.models.bid_status_with_id import BidStatusWithId -from typing import Optional, Set -from typing_extensions import Self - -class ServerUpdateResponseOneOf1(BaseModel): - """ - ServerUpdateResponseOneOf1 - """ # noqa: E501 - status: BidStatusWithId - type: StrictStr - __properties: ClassVar[List[str]] = ["status", "type"] - - @field_validator('type') - def type_validate_enum(cls, value): - """Validates the enum""" - if value not in set(['bid_status_update']): - raise ValueError("must be one of enum values ('bid_status_update')") - return value - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of ServerUpdateResponseOneOf1 from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - # override the default output from pydantic by calling `to_dict()` of status - if self.status: - _dict['status'] = self.status.to_dict() - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of ServerUpdateResponseOneOf1 from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "status": BidStatusWithId.from_dict(obj["status"]) if obj.get("status") is not None else None, - "type": obj.get("type") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py b/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py deleted file mode 100644 index 643c93bbfc..0000000000 --- a/express_relay/sdk/python/express_relay_utils/openapi_client/models/token_qty.py +++ /dev/null @@ -1,89 +0,0 @@ -# coding: utf-8 - -""" - auction-server - - No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - - The version of the OpenAPI document: 0.1.6 - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - -from __future__ import annotations -import pprint -import re # noqa: F401 -import json - -from pydantic import BaseModel, Field, StrictStr -from typing import Any, ClassVar, Dict, List -from typing import Optional, Set -from typing_extensions import Self - -class TokenQty(BaseModel): - """ - TokenQty - """ # noqa: E501 - amount: StrictStr = Field(description="Token amount") - contract: StrictStr = Field(description="Token contract address") - __properties: ClassVar[List[str]] = ["amount", "contract"] - - model_config = { - "populate_by_name": True, - "validate_assignment": True, - "protected_namespaces": (), - } - - - def to_str(self) -> str: - """Returns the string representation of the model using alias""" - return pprint.pformat(self.model_dump(by_alias=True)) - - def to_json(self) -> str: - """Returns the JSON representation of the model using alias""" - # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_str: str) -> Optional[Self]: - """Create an instance of TokenQty from a JSON string""" - return cls.from_dict(json.loads(json_str)) - - def to_dict(self) -> Dict[str, Any]: - """Return the dictionary representation of the model using alias. - - This has the following differences from calling pydantic's - `self.model_dump(by_alias=True)`: - - * `None` is only added to the output dict for nullable fields that - were set at model initialization. Other fields with value `None` - are ignored. - """ - excluded_fields: Set[str] = set([ - ]) - - _dict = self.model_dump( - by_alias=True, - exclude=excluded_fields, - exclude_none=True, - ) - return _dict - - @classmethod - def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: - """Create an instance of TokenQty from a dict""" - if obj is None: - return None - - if not isinstance(obj, dict): - return cls.model_validate(obj) - - _obj = cls.model_validate({ - "amount": obj.get("amount"), - "contract": obj.get("contract") - }) - return _obj - - diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index 4af07a92ad..5e87284fe3 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -3,11 +3,8 @@ import logging from eth_account.account import Account -from express_relay_client import ExpressRelayClient, OpportunityBidInfo, sign_bid -from openapi_client.models.bid_status_with_id import BidStatusWithId -from openapi_client.models.opportunity_params_with_metadata import ( - OpportunityParamsWithMetadata, -) +from express_relay_client import ExpressRelayClient, sign_bid +from express_relay_types import * logger = logging.getLogger(__name__) @@ -73,13 +70,13 @@ async def bid_status_callback(self, bid_status_with_id: BidStatusWithId): bid_status_with_id: A BidStatusWithId object, representing the status of a bid. """ bid_id = bid_status_with_id.id - bid_status = bid_status_with_id.bid_status.actual_instance + bid_status = bid_status_with_id.bid_status - if bid_status.status == "submitted": + if bid_status.status == Status("submitted"): logger.info(f"Bid {bid_id} has been submitted in hash {bid_status.result}") - elif bid_status.status == "lost": + elif bid_status.status == Status("lost"): logger.info(f"Bid {bid_id} was unsuccessful") - elif bid_status.status == "pending": + elif bid_status.status == Status("pending"): logger.info(f"Bid {bid_id} is pending") else: logger.error(f"Unrecognized status {bid_status.status} for bid {bid_id}") diff --git a/express_relay/sdk/python/mypy.ini b/express_relay/sdk/python/mypy.ini new file mode 100644 index 0000000000..895701cc05 --- /dev/null +++ b/express_relay/sdk/python/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +plugins = pydantic.mypy \ No newline at end of file diff --git a/express_relay/sdk/python/poetry.lock b/express_relay/sdk/python/poetry.lock new file mode 100644 index 0000000000..d60b4758fb --- /dev/null +++ b/express_relay/sdk/python/poetry.lock @@ -0,0 +1,2075 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "aiohttp" +version = "3.9.3" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, + {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, + {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, + {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, + {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, + {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, + {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, + {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, + {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, + {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, + {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, + {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "argparse" +version = "1.4.0" +description = "Python command-line parsing library" +optional = false +python-versions = "*" +files = [ + {file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"}, + {file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"}, +] + +[[package]] +name = "asyncio" +version = "3.4.3" +description = "reference implementation of PEP 3156" +optional = false +python-versions = "*" +files = [ + {file = "asyncio-3.4.3-cp33-none-win32.whl", hash = "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de"}, + {file = "asyncio-3.4.3-cp33-none-win_amd64.whl", hash = "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c"}, + {file = "asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d"}, + {file = "asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "bitarray" +version = "2.9.2" +description = "efficient arrays of booleans -- C extension" +optional = false +python-versions = "*" +files = [ + {file = "bitarray-2.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:917905de565d9576eb20f53c797c15ba88b9f4f19728acabec8d01eee1d3756a"}, + {file = "bitarray-2.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b35bfcb08b7693ab4bf9059111a6e9f14e07d57ac93cd967c420db58ab9b71e1"}, + {file = "bitarray-2.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ea1923d2e7880f9e1959e035da661767b5a2e16a45dfd57d6aa831e8b65ee1bf"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0b63a565e8a311cc8348ff1262d5784df0f79d64031d546411afd5dd7ef67d"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf0620da2b81946d28c0b16f3e3704d38e9837d85ee4f0652816e2609aaa4fed"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79a9b8b05f2876c7195a2b698c47528e86a73c61ea203394ff8e7a4434bda5c8"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:345c76b349ff145549652436235c5532e5bfe9db690db6f0a6ad301c62b9ef21"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e2936f090bf3f4d1771f44f9077ebccdbc0415d2b598d51a969afcb519df505"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f9346e98fc2abcef90b942973087e2462af6d3e3710e82938078d3493f7fef52"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e6ec283d4741befb86e8c3ea2e9ac1d17416c956d392107e45263e736954b1f7"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:962892646599529917ef26266091e4cb3077c88b93c3833a909d68dcc971c4e3"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e8da5355d7d75a52df5b84750989e34e39919ec7e59fafc4c104cc1607ab2d31"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:603e7d640e54ad764d2b4da6b61e126259af84f253a20f512dd10689566e5478"}, + {file = "bitarray-2.9.2-cp310-cp310-win32.whl", hash = "sha256:f00079f8e69d75c2a417de7961a77612bb77ef46c09bc74607d86de4740771ef"}, + {file = "bitarray-2.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:1bb33673e7f7190a65f0a940c1ef63266abdb391f4a3e544a47542d40a81f536"}, + {file = "bitarray-2.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fe71fd4b76380c2772f96f1e53a524da7063645d647a4fcd3b651bdd80ca0f2e"}, + {file = "bitarray-2.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d527172919cdea1e13994a66d9708a80c3d33dedcf2f0548e4925e600fef3a3a"}, + {file = "bitarray-2.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:052c5073bdcaa9dd10628d99d37a2f33ec09364b86dd1f6281e2d9f8d3db3060"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e064caa55a6ed493aca1eda06f8b3f689778bc780a75e6ad7724642ba5dc62f7"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:508069a04f658210fdeee85a7a0ca84db4bcc110cbb1d21f692caa13210f24a7"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4da73ebd537d75fa7bccfc2228fcaedea0803f21dd9d0bf0d3b67fef3c4af294"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb378eaa65cd43098f11ff5d27e48ee3b956d2c00d2d6b5bfc2a09fe183be47"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d14c790b91f6cbcd9b718f88ed737c78939980c69ac8c7f03dd7e60040c12951"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eea9318293bc0ea6447e9ebfba600a62f3428bea7e9c6d42170ae4f481dbab3"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b76ffec27c7450b8a334f967366a9ebadaea66ee43f5b530c12861b1a991f503"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:76b76a07d4ee611405045c6950a1e24c4362b6b44808d4ad6eea75e0dbc59af4"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c7d16beeaaab15b075990cd26963d6b5b22e8c5becd131781514a00b8bdd04bd"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60df43e868a615c7e15117a1e1c2e5e11f48f6457280eba6ddf8fbefbec7da99"}, + {file = "bitarray-2.9.2-cp311-cp311-win32.whl", hash = "sha256:e788608ed7767b7b3bbde6d49058bccdf94df0de9ca75d13aa99020cc7e68095"}, + {file = "bitarray-2.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:a23397da092ef0a8cfe729571da64c2fc30ac18243caa82ac7c4f965087506ff"}, + {file = "bitarray-2.9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:90e3a281ffe3897991091b7c46fca38c2675bfd4399ffe79dfeded6c52715436"}, + {file = "bitarray-2.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bed637b674db5e6c8a97a4a321e3e4d73e72d50b5c6b29950008a93069cc64cd"}, + {file = "bitarray-2.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e49066d251dbbe4e6e3a5c3937d85b589e40e2669ad0eef41a00f82ec17d844b"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4344e96642e2211fb3a50558feff682c31563a4c64529a931769d40832ca79"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aeb60962ec4813c539a59fbd4f383509c7222b62c3fb1faa76b54943a613e33a"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed0f7982f10581bb16553719e5e8f933e003f5b22f7d25a68bdb30fac630a6ff"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c71d1cabdeee0cdda4669168618f0e46b7dace207b29da7b63aaa1adc2b54081"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0ef2d0a6f1502d38d911d25609b44c6cc27bee0a4363dd295df78b075041b60"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6f71d92f533770fb027388b35b6e11988ab89242b883f48a6fe7202d238c61f8"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ba0734aa300757c924f3faf8148e1b8c247176a0ac8e16aefdf9c1eb19e868f7"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:d91406f413ccbf4af6ab5ae7bc78f772a95609f9ddd14123db36ef8c37116d95"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:87abb7f80c0a042f3fe8e5264da1a2756267450bb602110d5327b8eaff7682e7"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b558ce85579b51a2e38703877d1e93b7728a7af664dd45a34e833534f0b755d"}, + {file = "bitarray-2.9.2-cp312-cp312-win32.whl", hash = "sha256:dac2399ee2889fbdd3472bfc2ede74c34cceb1ccf29a339964281a16eb1d3188"}, + {file = "bitarray-2.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:48a30d718d1a6dfc22a49547450107abe8f4afdf2abdcbe76eb9ed88edc49498"}, + {file = "bitarray-2.9.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2c6be1b651fad8f3adb7a5aa12c65b612cd9b89530969af941844ae680f7d981"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5b399ae6ab975257ec359f03b48fc00b1c1cd109471e41903548469b8feae5c"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b3543c8a1cb286ad105f11c25d8d0f712f41c5c55f90be39f0e5a1376c7d0b0"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:03adaacb79e2fb8f483ab3a67665eec53bb3fd0cd5dbd7358741aef124688db3"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ae5b0657380d2581e13e46864d147a52c1e2bbac9f59b59c576e42fa7d10cf0"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c1f4bf6ea8eb9d7f30808c2e9894237a96650adfecbf5f3643862dc5982f89e"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a8873089be2aa15494c0f81af1209f6e1237d762c5065bc4766c1b84321e1b50"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:677e67f50e2559efc677a4366707070933ad5418b8347a603a49a070890b19bc"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:a620d8ce4ea2f1c73c6b6b1399e14cb68c6915e2be3fad5808c2998ed55b4acf"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:64115ccabbdbe279c24c367b629c6b1d3da9ed36c7420129e27c338a3971bfee"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5d6fb422772e75385b76ad1c52f45a68bd4efafd8be8d0061c11877be74c4d43"}, + {file = "bitarray-2.9.2-cp36-cp36m-win32.whl", hash = "sha256:852e202875dd6dfd6139ce7ec4e98dac2b17d8d25934dc99900831e81c3adaef"}, + {file = "bitarray-2.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:7dfefdcb0dc6a3ba9936063cec65a74595571b375beabe18742b3d91d087eefd"}, + {file = "bitarray-2.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b306c4cf66912511422060f7f5e1149c8bdb404f8e00e600561b0749fdd45659"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a09c4f81635408e3387348f415521d4b94198c562c23330f560596a6aaa26eaf"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5361413fd2ecfdf44dc8f065177dc6aba97fa80a91b815586cb388763acf7f8d"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8a9475d415ef1eaae7942df6f780fa4dcd48fce32825eda591a17abba869299"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b87baa7bfff9a5878fcc1bffe49ecde6e647a72a64b39a69cd8a2992a43a34"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb6b86cfdfc503e92cb71c68766a24565359136961642504a7cc9faf936d9c88"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cd56b8ae87ebc71bcacbd73615098e8a8de952ecbb5785b6b4e2b07da8a06e1f"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3fa909cfd675004aed8b4cc9df352415933656e0155a6209d878b7cb615c787e"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b069ca9bf728e0c5c5b60e00a89df9af34cc170c695c3bfa3b372d8f40288efb"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:6067f2f07a7121749858c7daa93c8774325c91590b3e81a299621e347740c2ae"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:321841cdad1dd0f58fe62e80e9c9c7531f8ebf8be93f047401e930dc47425b1e"}, + {file = "bitarray-2.9.2-cp37-cp37m-win32.whl", hash = "sha256:54e16e32e60973bb83c315de9975bc1bcfc9bd50bb13001c31da159bc49b0ca1"}, + {file = "bitarray-2.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f4dcadb7b8034aa3491ee8f5a69b3d9ba9d7d1e55c3cc1fc45be313e708277f8"}, + {file = "bitarray-2.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c8919fdbd3bb596b104388b56ae4b266eb28da1f2f7dff2e1f9334a21840fe96"}, + {file = "bitarray-2.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eb7a9d8a2e400a1026de341ad48e21670a6261a75b06df162c5c39b0d0e7c8f4"}, + {file = "bitarray-2.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6ec84668dd7b937874a2b2c293cd14ba84f37be0d196dead852e0ada9815d807"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2de9a31c34e543ae089fd2a5ced01292f725190e379921384f695e2d7184bd3"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9521f49ae121a17c0a41e5112249e6fa7f6a571245b1118de81fb86e7c1bc1ce"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6cc6545d6d76542aee3d18c1c9485fb7b9812b8df4ebe52c4535ec42081b48f"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:856bbe1616425f71c0df5ef2e8755e878d9504d5a531acba58ab4273c52c117a"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4bba8042ea6ab331ade91bc435d81ad72fddb098e49108610b0ce7780c14e68"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a035da89c959d98afc813e3c62f052690d67cfd55a36592f25d734b70de7d4b0"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d70b1579da7fb71be5a841a1f965d19aca0ef27f629cfc07d06b09aafd0a333"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:405b83bed28efaae6d86b6ab287c75712ead0adbfab2a1075a1b7ab47dad4d62"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7eb8be687c50da0b397d5e0ab7ca200b5ebb639e79a9f5e285851d1944c94be9"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eceb551dfeaf19c609003a69a0cf8264b0efd7abc3791a11dfabf4788daf0d19"}, + {file = "bitarray-2.9.2-cp38-cp38-win32.whl", hash = "sha256:bb198c6ed1edbcdaf3d1fa3c9c9d1cdb7e179a5134ef5ee660b53cdec43b34e7"}, + {file = "bitarray-2.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:648d2f2685590b0103c67a937c2fb9e09bcc8dfb166f0c7c77bd341902a6f5b3"}, + {file = "bitarray-2.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ea816dc8f8e65841a8bbdd30e921edffeeb6f76efe6a1eb0da147b60d539d1cf"}, + {file = "bitarray-2.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4d0e32530f941c41eddfc77600ec89b65184cb909c549336463a738fab3ed285"}, + {file = "bitarray-2.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4a22266fb416a3b6c258bf7f83c9fe531ba0b755a56986a81ad69dc0f3bcc070"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc6d3e80dd8239850f2604833ff3168b28909c8a9357abfed95632cccd17e3e7"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f135e804986b12bf14f2cd1eb86674c47dea86c4c5f0fa13c88978876b97ebe6"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87580c7f7d14f7ec401eda7adac1e2a25e95153e9c339872c8ae61b3208819a1"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64b433e26993127732ac7b66a7821b2537c3044355798de7c5fcb0af34b8296f"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e497c535f2a9b68c69d36631bf2dba243e05eb343b00b9c7bbdc8c601c6802d"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e40b3cb9fa1edb4e0175d7c06345c49c7925fe93e39ef55ecb0bc40c906b0c09"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f2f8692f95c9e377eb19ca519d30d1f884b02feb7e115f798de47570a359e43f"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f0b84fc50b6dbeced4fa390688c07c10a73222810fb0e08392bd1a1b8259de36"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d656ad38c942e38a470ddbce26b5020e08e1a7ea86b8fd413bb9024b5189993a"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ab0f1dbfe5070db98771a56aa14797595acd45a1af9eadfb193851a270e7996"}, + {file = "bitarray-2.9.2-cp39-cp39-win32.whl", hash = "sha256:0a99b23ac845a9ea3157782c97465e6ae026fe0c7c4c1ed1d88f759fd6ea52d9"}, + {file = "bitarray-2.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:9bbcfc7c279e8d74b076e514e669b683f77b4a2a328585b3f16d4c5259c91222"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:43847799461d8ba71deb4d97b47250c2c2fb66d82cd3cb8b4caf52bb97c03034"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f44381b0a4bdf64416082f4f0e7140377ae962c0ced6f983c6d7bbfc034040"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a484061616fb4b158b80789bd3cb511f399d2116525a8b29b6334c68abc2310f"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ff9e38356cc803e06134cf8ae9758e836ccd1b793135ef3db53c7c5d71e93bc"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b44105792fbdcfbda3e26ee88786790fda409da4c71f6c2b73888108cf8f062f"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7e913098de169c7fc890638ce5e171387363eb812579e637c44261460ac00aa2"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6fe315355cdfe3ed22ef355b8bdc81a805ca4d0949d921576560e5b227a1112"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f708e91fdbe443f3bec2df394ed42328fb9b0446dff5cb4199023ac6499e09fd"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b7b09489b71f9f1f64c0fa0977e250ec24500767dab7383ba9912495849cadf"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:128cc3488176145b9b137fdcf54c1c201809bbb8dd30b260ee40afe915843b43"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:21f21e7f56206be346bdbda2a6bdb2165a5e6a11821f88fd4911c5a6bbbdc7e2"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f4dd3af86dd8a617eb6464622fb64ca86e61ce99b59b5c35d8cd33f9c30603d"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6465de861aff7a2559f226b37982007417eab8c3557543879987f58b453519bd"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbaf2bb71d6027152d603f1d5f31e0dfd5e50173d06f877bec484e5396d4594b"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2f32948c86e0d230a296686db28191b67ed229756f84728847daa0c7ab7406e3"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be94e5a685e60f9d24532af8fe5c268002e9016fa80272a94727f435de3d1003"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5cc9381fd54f3c23ae1039f977bfd6d041a5c3c1518104f616643c3a5a73b15"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd926e8ae4d1ed1ac4a8f37212a62886292f692bc1739fde98013bf210c2d175"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:461a3dafb9d5fda0bb3385dc507d78b1984b49da3fe4c6d56c869a54373b7008"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:393cb27fd859af5fd9c16eb26b1c59b17b390ff66b3ae5d0dd258270191baf13"}, + {file = "bitarray-2.9.2.tar.gz", hash = "sha256:a8f286a51a32323715d77755ed959f94bef13972e9a2fe71b609e40e6d27957e"}, +] + +[[package]] +name = "black" +version = "24.2.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, + {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, + {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, + {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, + {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, + {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, + {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, + {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, + {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, + {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, + {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, + {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, + {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, + {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, + {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, + {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, + {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, + {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, + {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, + {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, + {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, + {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "cytoolz" +version = "0.12.3" +description = "Cython implementation of Toolz: High performance functional utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cytoolz-0.12.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bbe58e26c84b163beba0fbeacf6b065feabc8f75c6d3fe305550d33f24a2d346"}, + {file = "cytoolz-0.12.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c51b66ada9bfdb88cf711bf350fcc46f82b83a4683cf2413e633c31a64df6201"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e70d9c615e5c9dc10d279d1e32e846085fe1fd6f08d623ddd059a92861f4e3dd"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a83f4532707963ae1a5108e51fdfe1278cc8724e3301fee48b9e73e1316de64f"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d028044524ee2e815f36210a793c414551b689d4f4eda28f8bbb0883ad78bf5f"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c2875bcd1397d0627a09a4f9172fa513185ad302c63758efc15b8eb33cc2a98"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:131ff4820e5d64a25d7ad3c3556f2d8aa65c66b3f021b03f8a8e98e4180dd808"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:04afa90d9d9d18394c40d9bed48c51433d08b57c042e0e50c8c0f9799735dcbd"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:dc1ca9c610425f9854323669a671fc163300b873731584e258975adf50931164"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bfa3f8e01bc423a933f2e1c510cbb0632c6787865b5242857cc955cae220d1bf"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:f702e295dddef5f8af4a456db93f114539b8dc2a7a9bc4de7c7e41d169aa6ec3"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0fbad1fb9bb47e827d00e01992a099b0ba79facf5e5aa453be066033232ac4b5"}, + {file = "cytoolz-0.12.3-cp310-cp310-win32.whl", hash = "sha256:8587c3c3dbe78af90c5025288766ac10dc2240c1e76eb0a93a4e244c265ccefd"}, + {file = "cytoolz-0.12.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e45803d9e75ef90a2f859ef8f7f77614730f4a8ce1b9244375734567299d239"}, + {file = "cytoolz-0.12.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ac4f2fb38bbc67ff1875b7d2f0f162a247f43bd28eb7c9d15e6175a982e558d"}, + {file = "cytoolz-0.12.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0cf1e1e96dd86829a0539baf514a9c8473a58fbb415f92401a68e8e52a34ecd5"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08a438701c6141dd34eaf92e9e9a1f66e23a22f7840ef8a371eba274477de85d"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6b6f11b0d7ed91be53166aeef2a23a799e636625675bb30818f47f41ad31821"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7fde09384d23048a7b4ac889063761e44b89a0b64015393e2d1d21d5c1f534a"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d3bfe45173cc8e6c76206be3a916d8bfd2214fb2965563e288088012f1dabfc"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27513a5d5b6624372d63313574381d3217a66e7a2626b056c695179623a5cb1a"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d294e5e81ff094fe920fd545052ff30838ea49f9e91227a55ecd9f3ca19774a0"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:727b01a2004ddb513496507a695e19b5c0cfebcdfcc68349d3efd92a1c297bf4"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:fe1e1779a39dbe83f13886d2b4b02f8c4b10755e3c8d9a89b630395f49f4f406"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:de74ef266e2679c3bf8b5fc20cee4fc0271ba13ae0d9097b1491c7a9bcadb389"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e04d22049233394e0b08193aca9737200b4a2afa28659d957327aa780ddddf2"}, + {file = "cytoolz-0.12.3-cp311-cp311-win32.whl", hash = "sha256:20d36430d8ac809186736fda735ee7d595b6242bdb35f69b598ef809ebfa5605"}, + {file = "cytoolz-0.12.3-cp311-cp311-win_amd64.whl", hash = "sha256:780c06110f383344d537f48d9010d79fa4f75070d214fc47f389357dd4f010b6"}, + {file = "cytoolz-0.12.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:86923d823bd19ce35805953b018d436f6b862edd6a7c8b747a13d52b39ed5716"}, + {file = "cytoolz-0.12.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3e61acfd029bfb81c2c596249b508dfd2b4f72e31b7b53b62e5fb0507dd7293"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd728f4e6051af6af234651df49319da1d813f47894d4c3c8ab7455e01703a37"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe8c6267caa7ec67bcc37e360f0d8a26bc3bdce510b15b97f2f2e0143bdd3673"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99462abd8323c52204a2a0ce62454ce8fa0f4e94b9af397945c12830de73f27e"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da125221b1fa25c690fcd030a54344cecec80074df018d906fc6a99f46c1e3a6"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c18e351956f70db9e2d04ff02f28e9a41839250d3f936a4c8a1eabd1c3094d2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:921e6d2440ac758c4945c587b1d1d9b781b72737ac0c0ca5d5e02ca1db8bded2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1651a9bd591a8326329ce1d6336f3129161a36d7061a4d5ea9e5377e033364cf"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8893223b87c2782bd59f9c4bd5c7bf733edd8728b523c93efb91d7468b486528"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:e4d2961644153c5ae186db964aa9f6109da81b12df0f1d3494b4e5cf2c332ee2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:71b6eb97f6695f7ba8ce69c49b707a351c5f46fd97f5aeb5f6f2fb0d6e72b887"}, + {file = "cytoolz-0.12.3-cp312-cp312-win32.whl", hash = "sha256:cee3de65584e915053412cd178729ff510ad5f8f585c21c5890e91028283518f"}, + {file = "cytoolz-0.12.3-cp312-cp312-win_amd64.whl", hash = "sha256:9eef0d23035fa4dcfa21e570961e86c375153a7ee605cdd11a8b088c24f707f6"}, + {file = "cytoolz-0.12.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9a38332cfad2a91e89405b7c18b3f00e2edc951c225accbc217597d3e4e9fde"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f501ae1353071fa5d6677437bbeb1aeb5622067dce0977cedc2c5ec5843b202"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56f899758146a52e2f8cfb3fb6f4ca19c1e5814178c3d584de35f9e4d7166d91"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:800f0526adf9e53d3c6acda748f4def1f048adaa780752f154da5cf22aa488a2"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0976a3fcb81d065473173e9005848218ce03ddb2ec7d40dd6a8d2dba7f1c3ae"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c835eab01466cb67d0ce6290601ebef2d82d8d0d0a285ed0d6e46989e4a7a71a"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4fba0616fcd487e34b8beec1ad9911d192c62e758baa12fcb44448b9b6feae22"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6f6e8207d732651e0204779e1ba5a4925c93081834570411f959b80681f8d333"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:8119bf5961091cfe644784d0bae214e273b3b3a479f93ee3baab97bbd995ccfe"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7ad1331cb68afeec58469c31d944a2100cee14eac221553f0d5218ace1a0b25d"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:92c53d508fb8a4463acc85b322fa24734efdc66933a5c8661bdc862103a3373d"}, + {file = "cytoolz-0.12.3-cp37-cp37m-win32.whl", hash = "sha256:2c6dd75dae3d84fa8988861ab8b1189d2488cb8a9b8653828f9cd6126b5e7abd"}, + {file = "cytoolz-0.12.3-cp37-cp37m-win_amd64.whl", hash = "sha256:caf07a97b5220e6334dd32c8b6d8b2bd255ca694eca5dfe914bb5b880ee66cdb"}, + {file = "cytoolz-0.12.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ed0cfb9326747759e2ad81cb6e45f20086a273b67ac3a4c00b19efcbab007c60"}, + {file = "cytoolz-0.12.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:96a5a0292575c3697121f97cc605baf2fd125120c7dcdf39edd1a135798482ca"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b76f2f50a789c44d6fd7f773ec43d2a8686781cd52236da03f7f7d7998989bee"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2905fdccacc64b4beba37f95cab9d792289c80f4d70830b70de2fc66c007ec01"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ebe23028eac51251f22ba01dba6587d30aa9c320372ca0c14eeab67118ec3f"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96c715404a3825e37fe3966fe84c5f8a1f036e7640b2a02dbed96cac0c933451"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bac0adffc1b6b6a4c5f1fd1dd2161afb720bcc771a91016dc6bdba59af0a5d3"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:37441bf4a2a4e2e0fe9c3b0ea5e72db352f5cca03903977ffc42f6f6c5467be9"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f04037302049cb30033f7fa4e1d0e44afe35ed6bfcf9b380fc11f2a27d3ed697"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f37b60e66378e7a116931d7220f5352186abfcc950d64856038aa2c01944929c"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ec9be3e4b6f86ea8b294d34c990c99d2ba6c526ef1e8f46f1d52c263d4f32cd7"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e9199c9e3fbf380a92b8042c677eb9e7ed4bccb126de5e9c0d26f5888d96788"}, + {file = "cytoolz-0.12.3-cp38-cp38-win32.whl", hash = "sha256:18cd61e078bd6bffe088e40f1ed02001387c29174750abce79499d26fa57f5eb"}, + {file = "cytoolz-0.12.3-cp38-cp38-win_amd64.whl", hash = "sha256:765b8381d4003ceb1a07896a854eee2c31ebc950a4ae17d1e7a17c2a8feb2a68"}, + {file = "cytoolz-0.12.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b4a52dd2a36b0a91f7aa50ca6c8509057acc481a24255f6cb07b15d339a34e0f"}, + {file = "cytoolz-0.12.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:581f1ce479769fe7eeb9ae6d87eadb230df8c7c5fff32138162cdd99d7fb8fc3"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46f505d4c6eb79585c8ad0b9dc140ef30a138c880e4e3b40230d642690e36366"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59276021619b432a5c21c01cda8320b9cc7dbc40351ffc478b440bfccd5bbdd3"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e44f4c25e1e7cf6149b499c74945a14649c8866d36371a2c2d2164e4649e7755"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c64f8e60c1dd69e4d5e615481f2d57937746f4a6be2d0f86e9e7e3b9e2243b5e"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33c63186f3bf9d7ef1347bc0537bb9a0b4111a0d7d6e619623cabc18fef0dc3b"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fdddb9d988405f24035234f1e8d1653ab2e48cc2404226d21b49a129aefd1d25"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6986632d8a969ea1e720990c818dace1a24c11015fd7c59b9fea0b65ef71f726"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0ba1cbc4d9cd7571c917f88f4a069568e5121646eb5d82b2393b2cf84712cf2a"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7d267ffc9a36c0a9a58c7e0adc9fa82620f22e4a72533e15dd1361f57fc9accf"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95e878868a172a41fbf6c505a4b967309e6870e22adc7b1c3b19653d062711fa"}, + {file = "cytoolz-0.12.3-cp39-cp39-win32.whl", hash = "sha256:8e21932d6d260996f7109f2a40b2586070cb0a0cf1d65781e156326d5ebcc329"}, + {file = "cytoolz-0.12.3-cp39-cp39-win_amd64.whl", hash = "sha256:0d8edfbc694af6c9bda4db56643fb8ed3d14e47bec358c2f1417de9a12d6d1fb"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:55f9bd1ae6c2a27eda5abe2a0b65a83029d2385c5a1da7b8ef47af5905d7e905"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2d271393c378282727f1231d40391ae93b93ddc0997448acc21dd0cb6a1e56d"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee98968d6a66ee83a8ceabf31182189ab5d8598998c8ce69b6d5843daeb2db60"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01cfb8518828c1189200c02a5010ea404407fb18fd5589e29c126e84bbeadd36"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:456395d7aec01db32bf9e6db191d667347c78d8d48e77234521fa1078f60dabb"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cd88028bb897fba99ddd84f253ca6bef73ecb7bdf3f3cf25bc493f8f97d3c7c5"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59b19223e7f7bd7a73ec3aa6fdfb73b579ff09c2bc0b7d26857eec2d01a58c76"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a79d72b08048a0980a59457c239555f111ac0c8bdc140c91a025f124104dbb4"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dd70141b32b717696a72b8876e86bc9c6f8eff995c1808e299db3541213ff82"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a1445c91009eb775d479e88954c51d0b4cf9a1e8ce3c503c2672d17252882647"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ca6a9a9300d5bda417d9090107c6d2b007683efc59d63cc09aca0e7930a08a85"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be6feb903d2a08a4ba2e70e950e862fd3be9be9a588b7c38cee4728150a52918"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b6f43f086e5a965d33d62a145ae121b4ccb6e0789ac0acc895ce084fec8c65"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:534fa66db8564d9b13872d81d54b6b09ae592c585eb826aac235bd6f1830f8ad"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:fea649f979def23150680de1bd1d09682da3b54932800a0f90f29fc2a6c98ba8"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a447247ed312dd64e3a8d9483841ecc5338ee26d6e6fbd29cd373ed030db0240"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba3f843aa89f35467b38c398ae5b980a824fdbdb94065adc6ec7c47a0a22f4c7"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:582c22f97a380211fb36a7b65b1beeb84ea11d82015fa84b054be78580390082"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47feb089506fc66e1593cd9ade3945693a9d089a445fbe9a11385cab200b9f22"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ba9002d2f043943744a9dc8e50a47362bcb6e6f360dc0a1abcb19642584d87bb"}, + {file = "cytoolz-0.12.3.tar.gz", hash = "sha256:4503dc59f4ced53a54643272c61dc305d1dbbfbd7d6bdf296948de9f34c3a282"}, +] + +[package.dependencies] +toolz = ">=0.8.0" + +[package.extras] +cython = ["cython"] + +[[package]] +name = "eth-abi" +version = "4.2.1" +description = "eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding" +optional = false +python-versions = ">=3.7.2, <4" +files = [ + {file = "eth_abi-4.2.1-py3-none-any.whl", hash = "sha256:abd83410a5326145bf178675c276de0ed154f6dc695dcad1beafaa44d97f44ae"}, + {file = "eth_abi-4.2.1.tar.gz", hash = "sha256:60d88788d53725794cdb07c0f0bb0df2a31a6e1ad19644313fe6117ac24eeeb0"}, +] + +[package.dependencies] +eth-typing = ">=3.0.0" +eth-utils = ">=2.0.0" +parsimonious = ">=0.9.0,<0.10.0" + +[package.extras] +dev = ["black (>=23)", "build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "flake8 (==6.0.0)", "flake8-bugbear (==23.3.23)", "hypothesis (>=4.18.2,<5.0.0)", "ipython", "isort (>=5.10.1)", "mypy (==0.971)", "pydocstyle (>=6.0.0)", "pytest (>=7.0.0)", "pytest-pythonpath (>=0.7.1)", "pytest-watch (>=4.1.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +doc = ["sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +lint = ["black (>=23)", "flake8 (==6.0.0)", "flake8-bugbear (==23.3.23)", "isort (>=5.10.1)", "mypy (==0.971)", "pydocstyle (>=6.0.0)"] +test = ["eth-hash[pycryptodome]", "hypothesis (>=4.18.2,<5.0.0)", "pytest (>=7.0.0)", "pytest-pythonpath (>=0.7.1)", "pytest-xdist (>=2.4.0)"] +tools = ["hypothesis (>=4.18.2,<5.0.0)"] + +[[package]] +name = "eth-account" +version = "0.10.0" +description = "eth-account: Sign Ethereum transactions and messages with local private keys" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "eth-account-0.10.0.tar.gz", hash = "sha256:474a2fccf7286230cf66502565f03b536921d7e1fdfceba198e42160e5ac4bc1"}, + {file = "eth_account-0.10.0-py3-none-any.whl", hash = "sha256:b7a83f506a8edf57926569e5f04471ce3f1700e572d3421b4ad0dad7a26c0978"}, +] + +[package.dependencies] +bitarray = ">=2.4.0" +eth-abi = ">=4.0.0-b.2" +eth-keyfile = ">=0.6.0" +eth-keys = ">=0.4.0" +eth-rlp = ">=0.3.0" +eth-utils = ">=2.0.0" +hexbytes = ">=0.1.0,<0.4.0" +rlp = ">=1.0.0" + +[package.extras] +dev = ["black (>=23)", "build (>=0.9.0)", "bumpversion (>=0.5.3)", "coverage", "flake8 (==6.0.0)", "flake8-bugbear (==23.3.23)", "hypothesis (>=4.18.0,<5)", "ipython", "isort (>=5.10.1)", "mypy (==0.971)", "pydocstyle (>=6.0.0)", "pytest (>=7.0.0)", "pytest-watch (>=4.1.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +doc = ["sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +lint = ["black (>=23)", "flake8 (==6.0.0)", "flake8-bugbear (==23.3.23)", "isort (>=5.10.1)", "mypy (==0.971)", "pydocstyle (>=6.0.0)"] +test = ["coverage", "hypothesis (>=4.18.0,<5)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "eth-hash" +version = "0.7.0" +description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (erroneously) called sha3" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "eth-hash-0.7.0.tar.gz", hash = "sha256:bacdc705bfd85dadd055ecd35fd1b4f846b671add101427e089a4ca2e8db310a"}, + {file = "eth_hash-0.7.0-py3-none-any.whl", hash = "sha256:b8d5a230a2b251f4a291e3164a23a14057c4a6de4b0aa4a16fa4dc9161b57e2f"}, +] + +[package.dependencies] +pycryptodome = {version = ">=3.6.6,<4", optional = true, markers = "extra == \"pycryptodome\""} + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +pycryptodome = ["pycryptodome (>=3.6.6,<4)"] +pysha3 = ["pysha3 (>=1.0.0,<2.0.0)", "safe-pysha3 (>=1.0.0)"] +test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "eth-keyfile" +version = "0.8.0" +description = "eth-keyfile: A library for handling the encrypted keyfiles used to store ethereum private keys" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "eth-keyfile-0.8.0.tar.gz", hash = "sha256:02e3c2e564c7403b92db3fef8ecae3d21123b15787daecd5b643a57369c530f9"}, + {file = "eth_keyfile-0.8.0-py3-none-any.whl", hash = "sha256:9e09f5bc97c8309876c06bdea7a94f0051c25ba3109b5df37afb815418322efe"}, +] + +[package.dependencies] +eth-keys = ">=0.4.0" +eth-utils = ">=2" +pycryptodome = ">=3.6.6,<4" + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["towncrier (>=21,<22)"] +test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "eth-keys" +version = "0.5.0" +description = "eth-keys: Common API for Ethereum key operations" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "eth-keys-0.5.0.tar.gz", hash = "sha256:a0abccb83f3d84322591a2c047a1e3aa52ea86b185fa3e82ce311d120ca2791e"}, + {file = "eth_keys-0.5.0-py3-none-any.whl", hash = "sha256:b2bed3ff3bcede68cc0cd4458c7147baaeaac1211a1efdb6ca019f9d3d989f2b"}, +] + +[package.dependencies] +eth-typing = ">=3" +eth-utils = ">=2" + +[package.extras] +coincurve = ["coincurve (>=12.0.0)"] +dev = ["asn1tools (>=0.146.2)", "build (>=0.9.0)", "bumpversion (>=0.5.3)", "coincurve (>=12.0.0)", "eth-hash[pysha3]", "factory-boy (>=3.0.1)", "hypothesis (>=5.10.3,<6)", "ipython", "pre-commit (>=3.4.0)", "pyasn1 (>=0.4.5)", "pytest (>=7.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["towncrier (>=21,<22)"] +test = ["asn1tools (>=0.146.2)", "eth-hash[pysha3]", "factory-boy (>=3.0.1)", "hypothesis (>=5.10.3,<6)", "pyasn1 (>=0.4.5)", "pytest (>=7.0.0)"] + +[[package]] +name = "eth-rlp" +version = "1.0.1" +description = "eth-rlp: RLP definitions for common Ethereum objects in Python" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "eth-rlp-1.0.1.tar.gz", hash = "sha256:d61dbda892ee1220f28fb3663c08f6383c305db9f1f5624dc585c9cd05115027"}, + {file = "eth_rlp-1.0.1-py3-none-any.whl", hash = "sha256:dd76515d71654277377d48876b88e839d61553aaf56952e580bb7cebef2b1517"}, +] + +[package.dependencies] +eth-utils = ">=2.0.0" +hexbytes = ">=0.1.0,<1" +rlp = ">=0.6.0" +typing-extensions = {version = ">=4.0.1", markers = "python_version <= \"3.11\""} + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +test = ["eth-hash[pycryptodome]", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "eth-typing" +version = "4.0.0" +description = "eth-typing: Common type annotations for ethereum python packages" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "eth-typing-4.0.0.tar.gz", hash = "sha256:9af0b6beafbc5c2e18daf19da5f5a68315023172c4e79d149e12ad10a3d3f731"}, + {file = "eth_typing-4.0.0-py3-none-any.whl", hash = "sha256:7e556bea322b6e8c0a231547b736c258e10ce9eed5ddc254f51031b12af66a16"}, +] + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "eth-utils" +version = "4.0.0" +description = "eth-utils: Common utility functions for python code that interacts with Ethereum" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "eth-utils-4.0.0.tar.gz", hash = "sha256:58f9c57900e0f430be728a5e976dc6ed51f493a61e8a4ff1f73c043832cd4f2f"}, + {file = "eth_utils-4.0.0-py3-none-any.whl", hash = "sha256:38d0a5a4b5bb8f2e583f040ede678c47d9eae57a058a11895271a947853947a0"}, +] + +[package.dependencies] +cytoolz = {version = ">=0.10.1", markers = "implementation_name == \"cpython\""} +eth-hash = ">=0.3.1" +eth-typing = ">=3.0.0" +toolz = {version = ">0.8.2", markers = "implementation_name == \"pypy\""} + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "hypothesis (>=4.43.0)", "ipython", "mypy (==1.5.1)", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +test = ["hypothesis (>=4.43.0)", "mypy (==1.5.1)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "hexbytes" +version = "0.3.1" +description = "hexbytes: Python `bytes` subclass that decodes hex, with a readable console output" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "hexbytes-0.3.1-py3-none-any.whl", hash = "sha256:383595ad75026cf00abd570f44b368c6cdac0c6becfae5c39ff88829877f8a59"}, + {file = "hexbytes-0.3.1.tar.gz", hash = "sha256:a3fe35c6831ee8fafd048c4c086b986075fc14fd46258fa24ecb8d65745f9a9d"}, +] + +[package.extras] +dev = ["black (>=22)", "bumpversion (>=0.5.3)", "eth-utils (>=1.0.1,<3)", "flake8 (==6.0.0)", "flake8-bugbear (==23.3.23)", "hypothesis (>=3.44.24,<=6.31.6)", "ipython", "isort (>=5.10.1)", "mypy (==0.971)", "pydocstyle (>=5.0.0)", "pytest (>=7.0.0)", "pytest-watch (>=4.1.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +doc = ["sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +lint = ["black (>=22)", "flake8 (==6.0.0)", "flake8-bugbear (==23.3.23)", "isort (>=5.10.1)", "mypy (==0.971)", "pydocstyle (>=5.0.0)"] +test = ["eth-utils (>=1.0.1,<3)", "hypothesis (>=3.44.24,<=6.31.6)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "httpcore" +version = "0.16.3" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = "==1.*" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "httpx" +version = "0.23.3" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, + {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, +] + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.17.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "jsonschema" +version = "4.21.1" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, + {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "lru-dict" +version = "1.2.0" +description = "An Dict like LRU container." +optional = false +python-versions = "*" +files = [ + {file = "lru-dict-1.2.0.tar.gz", hash = "sha256:13c56782f19d68ddf4d8db0170041192859616514c706b126d0df2ec72a11bd7"}, + {file = "lru_dict-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:de906e5486b5c053d15b7731583c25e3c9147c288ac8152a6d1f9bccdec72641"}, + {file = "lru_dict-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604d07c7604b20b3130405d137cae61579578b0e8377daae4125098feebcb970"}, + {file = "lru_dict-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:203b3e78d03d88f491fa134f85a42919020686b6e6f2d09759b2f5517260c651"}, + {file = "lru_dict-1.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:020b93870f8c7195774cbd94f033b96c14f51c57537969965c3af300331724fe"}, + {file = "lru_dict-1.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1184d91cfebd5d1e659d47f17a60185bbf621635ca56dcdc46c6a1745d25df5c"}, + {file = "lru_dict-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fc42882b554a86e564e0b662da47b8a4b32fa966920bd165e27bb8079a323bc1"}, + {file = "lru_dict-1.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:18ee88ada65bd2ffd483023be0fa1c0a6a051ef666d1cd89e921dcce134149f2"}, + {file = "lru_dict-1.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:756230c22257597b7557eaef7f90484c489e9ba78e5bb6ab5a5bcfb6b03cb075"}, + {file = "lru_dict-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4da599af36618881748b5db457d937955bb2b4800db891647d46767d636c408"}, + {file = "lru_dict-1.2.0-cp310-cp310-win32.whl", hash = "sha256:35a142a7d1a4fd5d5799cc4f8ab2fff50a598d8cee1d1c611f50722b3e27874f"}, + {file = "lru_dict-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:6da5b8099766c4da3bf1ed6e7d7f5eff1681aff6b5987d1258a13bd2ed54f0c9"}, + {file = "lru_dict-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b20b7c9beb481e92e07368ebfaa363ed7ef61e65ffe6e0edbdbaceb33e134124"}, + {file = "lru_dict-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22147367b296be31cc858bf167c448af02435cac44806b228c9be8117f1bfce4"}, + {file = "lru_dict-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34a3091abeb95e707f381a8b5b7dc8e4ee016316c659c49b726857b0d6d1bd7a"}, + {file = "lru_dict-1.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:877801a20f05c467126b55338a4e9fa30e2a141eb7b0b740794571b7d619ee11"}, + {file = "lru_dict-1.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d3336e901acec897bcd318c42c2b93d5f1d038e67688f497045fc6bad2c0be7"}, + {file = "lru_dict-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8dafc481d2defb381f19b22cc51837e8a42631e98e34b9e0892245cc96593deb"}, + {file = "lru_dict-1.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:87bbad3f5c3de8897b8c1263a9af73bbb6469fb90e7b57225dad89b8ef62cd8d"}, + {file = "lru_dict-1.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:25f9e0bc2fe8f41c2711ccefd2871f8a5f50a39e6293b68c3dec576112937aad"}, + {file = "lru_dict-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ae301c282a499dc1968dd633cfef8771dd84228ae9d40002a3ea990e4ff0c469"}, + {file = "lru_dict-1.2.0-cp311-cp311-win32.whl", hash = "sha256:c9617583173a29048e11397f165501edc5ae223504a404b2532a212a71ecc9ed"}, + {file = "lru_dict-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6b7a031e47421d4b7aa626b8c91c180a9f037f89e5d0a71c4bb7afcf4036c774"}, + {file = "lru_dict-1.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ea2ac3f7a7a2f32f194c84d82a034e66780057fd908b421becd2f173504d040e"}, + {file = "lru_dict-1.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd46c94966f631a81ffe33eee928db58e9fbee15baba5923d284aeadc0e0fa76"}, + {file = "lru_dict-1.2.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:086ce993414f0b28530ded7e004c77dc57c5748fa6da488602aa6e7f79e6210e"}, + {file = "lru_dict-1.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df25a426446197488a6702954dcc1de511deee20c9db730499a2aa83fddf0df1"}, + {file = "lru_dict-1.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c53b12b89bd7a6c79f0536ff0d0a84fdf4ab5f6252d94b24b9b753bd9ada2ddf"}, + {file = "lru_dict-1.2.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f9484016e6765bd295708cccc9def49f708ce07ac003808f69efa386633affb9"}, + {file = "lru_dict-1.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d0f7ec902a0097ac39f1922c89be9eaccf00eb87751e28915320b4f72912d057"}, + {file = "lru_dict-1.2.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:981ef3edc82da38d39eb60eae225b88a538d47b90cce2e5808846fd2cf64384b"}, + {file = "lru_dict-1.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e25b2e90a032dc248213af7f3f3e975e1934b204f3b16aeeaeaff27a3b65e128"}, + {file = "lru_dict-1.2.0-cp36-cp36m-win32.whl", hash = "sha256:59f3df78e94e07959f17764e7fa7ca6b54e9296953d2626a112eab08e1beb2db"}, + {file = "lru_dict-1.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:de24b47159e07833aeab517d9cb1c3c5c2d6445cc378b1c2f1d8d15fb4841d63"}, + {file = "lru_dict-1.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d0dd4cd58220351233002f910e35cc01d30337696b55c6578f71318b137770f9"}, + {file = "lru_dict-1.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a87bdc291718bbdf9ea4be12ae7af26cbf0706fa62c2ac332748e3116c5510a7"}, + {file = "lru_dict-1.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05fb8744f91f58479cbe07ed80ada6696ec7df21ea1740891d4107a8dd99a970"}, + {file = "lru_dict-1.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00f6e8a3fc91481b40395316a14c94daa0f0a5de62e7e01a7d589f8d29224052"}, + {file = "lru_dict-1.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b172fce0a0ffc0fa6d282c14256d5a68b5db1e64719c2915e69084c4b6bf555"}, + {file = "lru_dict-1.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e707d93bae8f0a14e6df1ae8b0f076532b35f00e691995f33132d806a88e5c18"}, + {file = "lru_dict-1.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b9ec7a4a0d6b8297102aa56758434fb1fca276a82ed7362e37817407185c3abb"}, + {file = "lru_dict-1.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:f404dcc8172da1f28da9b1f0087009578e608a4899b96d244925c4f463201f2a"}, + {file = "lru_dict-1.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1171ad3bff32aa8086778be4a3bdff595cc2692e78685bcce9cb06b96b22dcc2"}, + {file = "lru_dict-1.2.0-cp37-cp37m-win32.whl", hash = "sha256:0c316dfa3897fabaa1fe08aae89352a3b109e5f88b25529bc01e98ac029bf878"}, + {file = "lru_dict-1.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5919dd04446bc1ee8d6ecda2187deeebfff5903538ae71083e069bc678599446"}, + {file = "lru_dict-1.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbf36c5a220a85187cacc1fcb7dd87070e04b5fc28df7a43f6842f7c8224a388"}, + {file = "lru_dict-1.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:712e71b64da181e1c0a2eaa76cd860265980cd15cb0e0498602b8aa35d5db9f8"}, + {file = "lru_dict-1.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f54908bf91280a9b8fa6a8c8f3c2f65850ce6acae2852bbe292391628ebca42f"}, + {file = "lru_dict-1.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3838e33710935da2ade1dd404a8b936d571e29268a70ff4ca5ba758abb3850df"}, + {file = "lru_dict-1.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5d5a5f976b39af73324f2b793862859902ccb9542621856d51a5993064f25e4"}, + {file = "lru_dict-1.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8bda3a9afd241ee0181661decaae25e5336ce513ac268ab57da737eacaa7871f"}, + {file = "lru_dict-1.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:bd2cd1b998ea4c8c1dad829fc4fa88aeed4dee555b5e03c132fc618e6123f168"}, + {file = "lru_dict-1.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b55753ee23028ba8644fd22e50de7b8f85fa60b562a0fafaad788701d6131ff8"}, + {file = "lru_dict-1.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7e51fa6a203fa91d415f3b2900e5748ec8e06ad75777c98cc3aeb3983ca416d7"}, + {file = "lru_dict-1.2.0-cp38-cp38-win32.whl", hash = "sha256:cd6806313606559e6c7adfa0dbeb30fc5ab625f00958c3d93f84831e7a32b71e"}, + {file = "lru_dict-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d90a70c53b0566084447c3ef9374cc5a9be886e867b36f89495f211baabd322"}, + {file = "lru_dict-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3ea7571b6bf2090a85ff037e6593bbafe1a8598d5c3b4560eb56187bcccb4dc"}, + {file = "lru_dict-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:287c2115a59c1c9ed0d5d8ae7671e594b1206c36ea9df2fca6b17b86c468ff99"}, + {file = "lru_dict-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5ccfd2291c93746a286c87c3f895165b697399969d24c54804ec3ec559d4e43"}, + {file = "lru_dict-1.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b710f0f4d7ec4f9fa89dfde7002f80bcd77de8024017e70706b0911ea086e2ef"}, + {file = "lru_dict-1.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5345bf50e127bd2767e9fd42393635bbc0146eac01f6baf6ef12c332d1a6a329"}, + {file = "lru_dict-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:291d13f85224551913a78fe695cde04cbca9dcb1d84c540167c443eb913603c9"}, + {file = "lru_dict-1.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d5bb41bc74b321789803d45b124fc2145c1b3353b4ad43296d9d1d242574969b"}, + {file = "lru_dict-1.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0facf49b053bf4926d92d8d5a46fe07eecd2af0441add0182c7432d53d6da667"}, + {file = "lru_dict-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:987b73a06bcf5a95d7dc296241c6b1f9bc6cda42586948c9dabf386dc2bef1cd"}, + {file = "lru_dict-1.2.0-cp39-cp39-win32.whl", hash = "sha256:231d7608f029dda42f9610e5723614a35b1fff035a8060cf7d2be19f1711ace8"}, + {file = "lru_dict-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:71da89e134747e20ed5b8ad5b4ee93fc5b31022c2b71e8176e73c5a44699061b"}, + {file = "lru_dict-1.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:21b3090928c7b6cec509e755cc3ab742154b33660a9b433923bd12c37c448e3e"}, + {file = "lru_dict-1.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaecd7085212d0aa4cd855f38b9d61803d6509731138bf798a9594745953245b"}, + {file = "lru_dict-1.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ead83ac59a29d6439ddff46e205ce32f8b7f71a6bd8062347f77e232825e3d0a"}, + {file = "lru_dict-1.2.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:312b6b2a30188586fe71358f0f33e4bac882d33f5e5019b26f084363f42f986f"}, + {file = "lru_dict-1.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:b30122e098c80e36d0117810d46459a46313421ce3298709170b687dc1240b02"}, + {file = "lru_dict-1.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f010cfad3ab10676e44dc72a813c968cd586f37b466d27cde73d1f7f1ba158c2"}, + {file = "lru_dict-1.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20f5f411f7751ad9a2c02e80287cedf69ae032edd321fe696e310d32dd30a1f8"}, + {file = "lru_dict-1.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afdadd73304c9befaed02eb42f5f09fdc16288de0a08b32b8080f0f0f6350aa6"}, + {file = "lru_dict-1.2.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7ab0c10c4fa99dc9e26b04e6b62ac32d2bcaea3aad9b81ec8ce9a7aa32b7b1b"}, + {file = "lru_dict-1.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:edad398d5d402c43d2adada390dd83c74e46e020945ff4df801166047013617e"}, + {file = "lru_dict-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:91d577a11b84387013815b1ad0bb6e604558d646003b44c92b3ddf886ad0f879"}, + {file = "lru_dict-1.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb12f19cdf9c4f2d9aa259562e19b188ff34afab28dd9509ff32a3f1c2c29326"}, + {file = "lru_dict-1.2.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e4c85aa8844bdca3c8abac3b7f78da1531c74e9f8b3e4890c6e6d86a5a3f6c0"}, + {file = "lru_dict-1.2.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c6acbd097b15bead4de8e83e8a1030bb4d8257723669097eac643a301a952f0"}, + {file = "lru_dict-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b6613daa851745dd22b860651de930275be9d3e9373283a2164992abacb75b62"}, +] + +[package.extras] +test = ["pytest"] + +[[package]] +name = "multidict" +version = "6.0.5" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, +] + +[[package]] +name = "mypy" +version = "1.9.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, + {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, + {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, + {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, + {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, + {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, + {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, + {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, + {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, + {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, + {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, + {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, + {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, + {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, + {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, + {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, + {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, + {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "parsimonious" +version = "0.9.0" +description = "(Soon to be) the fastest pure-Python PEG parser I could muster" +optional = false +python-versions = "*" +files = [ + {file = "parsimonious-0.9.0.tar.gz", hash = "sha256:b2ad1ae63a2f65bd78f5e0a8ac510a98f3607a43f1db2a8d46636a5d9e4a30c1"}, +] + +[package.dependencies] +regex = ">=2022.3.15" + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[package]] +name = "pycryptodome" +version = "3.20.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, + {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, +] + +[[package]] +name = "pydantic" +version = "2.6.3" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, + {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.16.3" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.16.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, + {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, + {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, + {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, + {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, + {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, + {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, + {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, + {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, + {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, + {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, + {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, + {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, + {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pyunormalize" +version = "15.1.0" +description = "Unicode normalization forms (NFC, NFKC, NFD, NFKD). A library independent from the Python core Unicode database." +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyunormalize-15.1.0.tar.gz", hash = "sha256:cf4a87451a0f1cb76911aa97f432f4579e1f564a2f0c84ce488c73a73901b6c1"}, +] + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "referencing" +version = "0.33.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.33.0-py3-none-any.whl", hash = "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5"}, + {file = "referencing-0.33.0.tar.gz", hash = "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "regex" +version = "2023.12.25" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +optional = false +python-versions = "*" +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "rlp" +version = "4.0.0" +description = "rlp: A package for Recursive Length Prefix encoding and decoding" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "rlp-4.0.0-py3-none-any.whl", hash = "sha256:1747fd933e054e6d25abfe591be92e19a4193a56c93981c05bd0f84dfe279f14"}, + {file = "rlp-4.0.0.tar.gz", hash = "sha256:61a5541f86e4684ab145cb849a5929d2ced8222930a570b3941cf4af16b72a78"}, +] + +[package.dependencies] +eth-utils = ">=2" + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "hypothesis (==5.19.0)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +rust-backend = ["rusty-rlp (>=0.2.1,<0.3)"] +test = ["hypothesis (==5.19.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "rpds-py" +version = "0.18.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, + {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, + {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, + {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, + {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, + {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, + {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, + {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, + {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, + {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, + {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, + {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, +] + +[[package]] +name = "ruff" +version = "0.3.1" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6b82e3937d0d76554cd5796bc3342a7d40de44494d29ff490022d7a52c501744"}, + {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ae7954c8f692b70e6a206087ae3988acc9295d84c550f8d90b66c62424c16771"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b730f56ccf91225da0f06cfe421e83b8cc27b2a79393db9c3df02ed7e2bbc01"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c78bfa85637668f47bd82aa2ae17de2b34221ac23fea30926f6409f9e37fc927"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6abaad602d6e6daaec444cbf4d9364df0a783e49604c21499f75bb92237d4af"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0c21b6914c3c9a25a59497cbb1e5b6c2d8d9beecc9b8e03ee986e24eee072e"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434c3fc72e6311c85cd143c4c448b0e60e025a9ac1781e63ba222579a8c29200"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78a7025e6312cbba496341da5062e7cdd47d95f45c1b903e635cdeb1ba5ec2b9"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52b02bb46f1a79b0c1fa93f6495bc7e77e4ef76e6c28995b4974a20ed09c0833"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11b5699c42f7d0b771c633d620f2cb22e727fb226273aba775a91784a9ed856c"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:54e5dca3e411772b51194b3102b5f23b36961e8ede463776b289b78180df71a0"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:951efb610c5844e668bbec4f71cf704f8645cf3106e13f283413969527ebfded"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:09c7333b25e983aabcf6e38445252cff0b4745420fc3bda45b8fce791cc7e9ce"}, + {file = "ruff-0.3.1-py3-none-win32.whl", hash = "sha256:d937f9b99ebf346e0606c3faf43c1e297a62ad221d87ef682b5bdebe199e01f6"}, + {file = "ruff-0.3.1-py3-none-win_amd64.whl", hash = "sha256:c0318a512edc9f4e010bbaab588b5294e78c5cdc9b02c3d8ab2d77c7ae1903e3"}, + {file = "ruff-0.3.1-py3-none-win_arm64.whl", hash = "sha256:d3b60e44240f7e903e6dbae3139a65032ea4c6f2ad99b6265534ff1b83c20afa"}, + {file = "ruff-0.3.1.tar.gz", hash = "sha256:d30db97141fc2134299e6e983a6727922c9e03c031ae4883a6d69461de722ae7"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "toolz" +version = "0.12.1" +description = "List processing tools and functional utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "toolz-0.12.1-py3-none-any.whl", hash = "sha256:d22731364c07d72eea0a0ad45bafb2c2937ab6fd38a3507bf55eae8744aa7d85"}, + {file = "toolz-0.12.1.tar.gz", hash = "sha256:ecca342664893f177a13dac0e6b41cbd8ac25a358e5f215316d43e2100224f4d"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "web3" +version = "6.15.1" +description = "web3.py" +optional = false +python-versions = ">=3.7.2" +files = [ + {file = "web3-6.15.1-py3-none-any.whl", hash = "sha256:4e4a8313aa4556ecde61c852a62405b853b667498b07da6ff05c29fe8c79096b"}, + {file = "web3-6.15.1.tar.gz", hash = "sha256:f9e7eefc1b3c3d194868a4ef9583b625c18ea3f31a48ebe143183db74898f381"}, +] + +[package.dependencies] +aiohttp = ">=3.7.4.post0" +eth-abi = ">=4.0.0" +eth-account = ">=0.8.0" +eth-hash = {version = ">=0.5.1", extras = ["pycryptodome"]} +eth-typing = ">=3.0.0" +eth-utils = ">=2.1.0" +hexbytes = ">=0.1.0,<0.4.0" +jsonschema = ">=4.0.0" +lru-dict = ">=1.1.6,<1.3.0" +protobuf = ">=4.21.6" +pyunormalize = ">=15.0.0" +pywin32 = {version = ">=223", markers = "platform_system == \"Windows\""} +requests = ">=2.16.0" +typing-extensions = ">=4.0.1" +websockets = ">=10.0.0" + +[package.extras] +dev = ["black (>=22.1.0)", "build (>=0.9.0)", "bumpversion", "eth-tester[py-evm] (==v0.9.1-b.2)", "flake8 (==3.8.3)", "flaky (>=3.7.0)", "hypothesis (>=3.31.2)", "importlib-metadata (<5.0)", "ipfshttpclient (==0.8.0a2)", "isort (>=5.11.0)", "mypy (==1.4.1)", "py-geth (>=3.14.0)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.18.1,<0.23)", "pytest-mock (>=1.10)", "pytest-watch (>=4.2)", "pytest-xdist (>=1.29)", "setuptools (>=38.6.0)", "sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=3.18.0)", "tqdm (>4.32)", "twine (>=1.13)", "types-protobuf (==3.19.13)", "types-requests (>=2.26.1)", "types-setuptools (>=57.4.4)", "when-changed (>=0.3.0)"] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +ipfs = ["ipfshttpclient (==0.8.0a2)"] +linter = ["black (>=22.1.0)", "flake8 (==3.8.3)", "isort (>=5.11.0)", "mypy (==1.4.1)", "types-protobuf (==3.19.13)", "types-requests (>=2.26.1)", "types-setuptools (>=57.4.4)"] +tester = ["eth-tester[py-evm] (==v0.9.1-b.2)", "py-geth (>=3.14.0)"] + +[[package]] +name = "websockets" +version = "11.0.3" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526"}, + {file = "websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69"}, + {file = "websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd"}, + {file = "websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c"}, + {file = "websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8"}, + {file = "websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af"}, + {file = "websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f"}, + {file = "websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788"}, + {file = "websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74"}, + {file = "websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311"}, + {file = "websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128"}, + {file = "websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602"}, + {file = "websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6"}, + {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, +] + +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "d8096d6e5bc2ee1301aabd700fbc6a0bd52a089adfe0f1d680a64a3086bff74a" diff --git a/express_relay/sdk/python/pyproject.toml b/express_relay/sdk/python/pyproject.toml index 7760fff7fa..5f4a9febb3 100644 --- a/express_relay/sdk/python/pyproject.toml +++ b/express_relay/sdk/python/pyproject.toml @@ -19,11 +19,8 @@ pydantic = "^2.6.3" [tool.poetry.group.dev.dependencies] black = "^24.1.1" -ruff = "^0.3.0" - -[tool.ruff.lint] -select = ["F", "E", "W", "I001"] -ignore = ["E501"] +pyflakes = "^3.2.0" +mypy = "^1.9.0" [build-system] requires = ["poetry-core>=1.0.0"] From 0f9081455eaee446e5c1d49a4a170d7d387996d6 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 13 Mar 2024 11:58:33 -0400 Subject: [PATCH 49/58] chgs --- .pre-commit-config.yaml | 1 - express_relay/sdk/python/README.md | 20 --- .../express_relay_client.py | 81 +++++------ .../express_relay_types.py | 130 +++++++++--------- .../searcher/examples/simple_searcher.py | 36 +++-- 5 files changed, 120 insertions(+), 148 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 897ed5cbb8..5fd99d2107 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,6 @@ repos: hooks: - id: trailing-whitespace - id: end-of-file-fixer - exclude: openapi_client - id: check-added-large-files exclude: package-lock.json # Hook to format many type of files in the repo diff --git a/express_relay/sdk/python/README.md b/express_relay/sdk/python/README.md index 0e12eb094d..48e81434b4 100644 --- a/express_relay/sdk/python/README.md +++ b/express_relay/sdk/python/README.md @@ -8,32 +8,12 @@ The SDK includes searcher-side utilities and protocol-side utilities. The search ## Installation -### pip - -``` -$ pip install express-relay-utils -``` - ### poetry ``` $ poetry add express-relay-utils ``` -### openapi-generator - -You can use the `openapi-generator` command line tool to auto generate the types you will need for the client. To install, run - -``` -$ brew install openapi-generator -``` - -To generate the types from the Express Relay server, navigate to `express_relay_utils/` and run - -``` -$ openapi-generator generate -i https://per-staging.dourolabs.app/docs/openapi.json -g python --additional-properties=generateSourceCodeOnly=true --global-property models,modelTests=false,modelDocs=false -``` - ## Quickstart To run the simple searcher script, navigate to `express_relay_utils/` and run diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index ecce0c8b4e..67ccd9178c 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -4,6 +4,7 @@ import urllib.parse from typing import Callable, Any from collections.abc import Coroutine +from uuid import UUID import httpx import web3 @@ -14,7 +15,7 @@ from web3.auto import w3 -from express_relay_types import * +from express_relay_utils.express_relay_types import * class ExpressRelayClientException(Exception): @@ -26,15 +27,17 @@ def __init__( self, server_url: str, opportunity_callback: ( - Callable[[OpportunityParamsWithMetadata], None] | None + Callable[[OpportunityParamsWithMetadata], Coroutine[Any, Any, Any]] | None + ) = None, + bid_status_callback: ( + Callable[[BidStatusWithId], Coroutine[Any, Any, Any]] | None ) = None, - bid_status_callback: Callable[[BidStatusWithId], None] | None = None, **kwargs, ): """ Args: - server_url: The URL of the liquidation server. - opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. + server_url: The URL of the auction server. + opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatus. kwargs: Keyword arguments to pass to the websocket connection. """ @@ -44,7 +47,7 @@ def __init__( elif parsed_url.scheme == "http": ws_scheme = "ws" else: - raise ValueError("Invalid liquidation server URL") + raise ValueError("Invalid server URL") self.server_url = server_url self.ws_endpoint = parsed_url._replace(scheme=ws_scheme, path="/v1/ws").geturl() @@ -131,7 +134,7 @@ def process_response_msg(self, msg: dict) -> dict: async def subscribe_chains(self, chain_ids: list[str]): """ - Subscribes websocket to a list of chain IDs for new liquidation opportunities. + Subscribes websocket to a list of chain IDs for new opportunities. Args: chain_ids: A list of chain IDs to subscribe to. @@ -143,7 +146,7 @@ async def subscribe_chains(self, chain_ids: list[str]): async def unsubscribe_chains(self, chain_ids: list[str]): """ - Unsubscribes websocket from a list of chain IDs for new liquidation opportunities. + Unsubscribes websocket from a list of chain IDs for new opportunities. Args: chain_ids: A list of chain IDs to unsubscribe from. @@ -191,7 +194,7 @@ async def submit_opportunity_bid( **kwargs, ) -> UUID: """ - Submits a bid on an opportunity to the liquidation server via websocket. + Submits a bid on an opportunity to the server via websocket. Args: opportunity_bid_info: An object representing the bid to submit on an opportunity. @@ -202,7 +205,7 @@ async def submit_opportunity_bid( """ if subscribe_to_updates: result = await self.send_ws_message( - "post_liquidation_bid", + "post_opportunity_bid", { "opportunity_id": opportunity_bid_info.opportunity_id, "opportunity_bid": opportunity_bid_info.opportunity_bid, @@ -217,9 +220,7 @@ async def submit_opportunity_bid( async with httpx.AsyncClient() as client: resp = await client.post( urllib.parse.urlparse(self.server_url) - ._replace( - path=f"/v1/liquidation/opportunities/{opportunity_id}/bids" - ) + ._replace(path=f"/v1/opportunities/{opportunity_id}/bids") .geturl(), json=opportunity_bid, **kwargs, @@ -243,7 +244,7 @@ async def ws_handler( Continually handles new ws messages as they are received from the server via websocket. Args: - opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. + opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatusWithId. """ if not self.ws: @@ -275,13 +276,13 @@ async def get_opportunities( self, chain_id: str | None = None, timeout_secs: int = 10 ) -> list[OpportunityParamsWithMetadata]: """ - Connects to the liquidation server and fetches liquidation opportunities. + Connects to the server and fetches opportunities. Args: - chain_id: The chain ID to fetch liquidation opportunities for. If None, fetches opportunities across all chains. + chain_id: The chain ID to fetch opportunities for. If None, fetches opportunities across all chains. timeout_secs: The timeout for the HTTP request in seconds. Returns: - A list of liquidation opportunities. + A list of opportunities. """ params = {} if chain_id: @@ -290,7 +291,7 @@ async def get_opportunities( async with httpx.AsyncClient() as client: resp = await client.get( urllib.parse.urlparse(self.server_url) - ._replace(path="/v1/liquidation/opportunities") + ._replace(path="/v1/opportunities") .geturl(), params=params, timeout=timeout_secs, @@ -309,7 +310,7 @@ async def submit_opportunity( self, opportunity: OpportunityParams, timeout_secs: int = 10 ) -> UUID: """ - Submits an opportunity to the liquidation server. + Submits an opportunity to the server. Args: opportunity: An object representing the opportunity to submit. @@ -320,7 +321,7 @@ async def submit_opportunity( async with httpx.AsyncClient() as client: resp = await client.post( urllib.parse.urlparse(self.server_url) - ._replace(path="/v1/liquidation/opportunities") + ._replace(path="/v1/opportunities") .geturl(), json=opportunity.to_dict(), timeout=timeout_secs, @@ -330,31 +331,31 @@ async def submit_opportunity( def sign_bid( - liquidation_opportunity: OpportunityParamsWithMetadata, + opportunity: OpportunityParamsWithMetadata, bid_amount: int, valid_until: int, private_key: str, ) -> OpportunityBidInfo: """ - Constructs a signature for a liquidator's bid and returns the OpportunityBidInfo object to be submitted to the liquidation server. + Constructs a signature for a searcher's bid and returns the OpportunityBidInfo object to be submitted to the server. Args: - liquidation_opportunity: An object representing the liquidation opportunity, of type OpportunityParamsWithMetadata. + opportunity: An object representing the opportunity, of type OpportunityParamsWithMetadata. bid_amount: An integer representing the amount of the bid (in wei). valid_until: An integer representing the unix timestamp until which the bid is valid. - private_key: A 0x-prefixed hex string representing the liquidator's private key. + private_key: A 0x-prefixed hex string representing the searcher's private key. Returns: - A OpportunityBidInfo object, representing the transaction to submit to the liquidation server. This object contains the liquidator's signature. + A OpportunityBidInfo object, representing the transaction to submit to the server. This object contains the searcher's signature. """ - repay_tokens = [ - (token.contract.address, int(token.amount)) - for token in liquidation_opportunity.repay_tokens + sell_tokens = [ + (token.token.address, int(token.amount)) for token in opportunity.sell_tokens ] - receipt_tokens = [ - (token.contract.address, int(token.amount)) - for token in liquidation_opportunity.receipt_tokens + buy_tokens = [ + (token.token.address, int(token.amount)) for token in opportunity.buy_tokens ] - calldata = bytes.fromhex(liquidation_opportunity.calldata.string.replace("0x", "")) + target_calldata = bytes.fromhex( + opportunity.target_calldata.string.replace("0x", "") + ) digest = encode( [ @@ -367,11 +368,11 @@ def sign_bid( "uint256", ], [ - repay_tokens, - receipt_tokens, - liquidation_opportunity.contract.address, - calldata, - int(liquidation_opportunity.value), + sell_tokens, + buy_tokens, + opportunity.target_contract.address, + target_calldata, + int(opportunity.target_call_value), bid_amount, valid_until, ], @@ -380,16 +381,16 @@ def sign_bid( signature = w3.eth.account.signHash(msg_data, private_key=private_key) opportunity_bid_dict = { - "permission_key": liquidation_opportunity.permission_key, + "permission_key": opportunity.permission_key, "amount": str(bid_amount), "valid_until": str(valid_until), - "liquidator": Address(address=Account.from_key(private_key).address), + "executor": Address(address=Account.from_key(private_key).address), "signature": signature, } opportunity_bid = OpportunityBid.model_validate(opportunity_bid_dict) opportunity_bid_info = OpportunityBidInfo( - opportunity_id=liquidation_opportunity.opportunity_id, + opportunity_id=opportunity.opportunity_id, opportunity_bid=opportunity_bid, ) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_types.py b/express_relay/sdk/python/express_relay_utils/express_relay_types.py index d4d2c6d4ba..7c4dd9580c 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_types.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_types.py @@ -22,7 +22,7 @@ def assert_hex_string(s: str): s_set = set(s) hex_set = set("0123456789abcdefABCDEF") - assert s_set.issubset(hex_set), "v must be a valid hex string" + assert s_set.issubset(hex_set), "string must be a valid hex string" class HexString(BaseModel): @@ -52,7 +52,7 @@ class Address(BaseModel): @field_validator("address") @classmethod def check_address(cls, v: str): - assert web3.Web3.is_address(v), "s must be a valid Ethereum address" + assert web3.Web3.is_address(v), "string must be a valid Ethereum address" return v @@ -68,7 +68,7 @@ class Bytes32(BaseModel): @classmethod def check_bytes32(cls, v: str): assert_hex_string(v) - assert len(v.replace("0x", "")) == 64, "permission key must be 32 bytes long" + assert len(v.replace("0x", "")) == 64, "string must be 32 bytes long" return v @@ -76,24 +76,24 @@ class Bid(BaseModel): """ Args: amount: The amount of the bid in wei. - calldata: The calldata for the contract call. + target_calldata: The calldata for the contract call. chain_id: The chain ID to bid on. - contract: The contract address to call. + target_contract: The contract address to call. permission_key: The permission key to bid on. """ amount: int - calldata: HexString + target_calldata: HexString chain_id: str - contract: Address + target_contract: Address permission_key: HexString def to_dict(self): return { "amount": str(self.amount), - "calldata": self.calldata.string, + "target_calldata": self.target_calldata.string, "chain_id": self.chain_id, - "contract": self.contract.address, + "target_contract": self.target_contract.address, "permission_key": self.permission_key.string, } @@ -107,8 +107,8 @@ class Status(Enum): class BidStatus(BaseModel): """ Args: - status: The status tag, either 'submitted', 'lost', or 'pending'. - result: The result of the bid: a transaction hash if the status is 'submitted', else None. + status: The status enum, either SUBMITTED, LOST, or PENDING. + result: The result of the bid: a transaction hash if the status is SUBMITTED, else None. """ status: Status @@ -152,14 +152,14 @@ class OpportunityBid(BaseModel): """ Args: amount: The amount of the bid in wei. - liquidator: The address of the liquidator. + executor: The address of the executor. permission_key: The permission key to bid on. signature: The signature of the bid. valid_until: The unix timestamp after which the bid becomes invalid. """ amount: int - liquidator: Address + executor: Address permission_key: HexString signature: SignedMessage valid_until: int @@ -171,7 +171,7 @@ class OpportunityBid(BaseModel): def to_dict(self): return { "amount": str(self.amount), - "liquidator": self.liquidator.address, + "executor": self.executor.address, "permission_key": self.permission_key.string, "signature": bytes(self.signature.signature).hex(), "valid_until": str(self.valid_until), @@ -181,8 +181,8 @@ def to_dict(self): class OpportunityBidInfo(BaseModel): """ Args: - opportunity_id: The ID of the liquidation opportunity. - opportunity_bid: The bid to submit on the liquidation opportunity. + opportunity_id: The ID of the opportunity. + opportunity_bid: The bid to submit on the opportunity. """ opportunity_id: UUID @@ -198,51 +198,49 @@ def to_dict(self): class TokenAmount(BaseModel): """ Args: - contract: The address of the token contract. + token: The address of the token contract. amount: The amount of the token. """ - contract: Address + token: Address amount: int class OpportunityParamsV1(BaseModel): """ Args: - calldata: The calldata for the contract call. + target_calldata: The calldata for the contract call. chain_id: The chain ID to bid on. - contract: The contract address to call. + target_contract: The contract address to call. permission_key: The permission key to bid on. - receipt_tokens: The tokens to receive in the liquidation. - repay_tokens: The tokens to repay in the liquidation. - value: The value to send with the contract call. + buy_tokens: The tokens to receive in the opportunity. + sell_tokens: The tokens to spend in the opportunity. + target_call_value: The value to send with the contract call. version: The version of the opportunity. """ - calldata: HexString + target_calldata: HexString chain_id: str - contract: Address + target_contract: Address permission_key: HexString - receipt_tokens: list[TokenAmount] - repay_tokens: list[TokenAmount] - value: int + buy_tokens: list[TokenAmount] + sell_tokens: list[TokenAmount] + target_call_value: int version: Literal["v1"] def to_dict(self): return { - "calldata": self.calldata.string, + "target_calldata": self.target_calldata.string, "chain_id": self.chain_id, - "contract": self.contract.address, + "target_contract": self.target_contract.address, "permission_key": self.permission_key.string, - "receipt_tokens": [ - (token.contract.address, str(token.amount)) - for token in self.receipt_tokens + "buy_tokens": [ + (token.token.address, str(token.amount)) for token in self.buy_tokens ], - "repay_tokens": [ - (token.contract.address, str(token.amount)) - for token in self.repay_tokens + "sell_tokens": [ + (token.token.address, str(token.amount)) for token in self.sell_tokens ], - "value": str(self.value), + "target_call_value": str(self.target_call_value), "version": self.version, } @@ -262,57 +260,53 @@ def to_dict(self): class OpportunityParamsWithMetadata(BaseModel): """ Args: - calldata: The calldata for the contract call. + target_calldata: The calldata for the contract call. chain_id: The chain ID to bid on. - contract: The contract address to call. + target_contract: The contract address to call. permission_key: The permission key to bid on. - receipt_tokens: The tokens to receive in the liquidation. - repay_tokens: The tokens to repay in the liquidation. - value: The value to send with the contract call. + buy_tokens: The tokens to receive in the opportunity. + sell_tokens: The tokens to spend in the opportunity. + target_call_value: The value to send with the contract call. version: The version of the opportunity. creation_time: The creation time of the opportunity. opportunity_id: The ID of the opportunity. """ - calldata: HexString + target_calldata: HexString chain_id: str - contract: Address + target_contract: Address permission_key: HexString - receipt_tokens: list[TokenAmount] - repay_tokens: list[TokenAmount] - value: int + buy_tokens: list[TokenAmount] + sell_tokens: list[TokenAmount] + target_call_value: int version: str creation_time: int opportunity_id: UUID @classmethod def from_dict(cls, obj: Dict[str, Any]): - obj["calldata"] = HexString(string=obj["calldata"]) - obj["contract"] = Address(address=obj["contract"]) + obj["target_calldata"] = HexString(string=obj["target_calldata"]) + obj["target_contract"] = Address(address=obj["target_contract"]) obj["permission_key"] = HexString(string=obj["permission_key"]) - obj["receipt_tokens"] = [ - TokenAmount( - contract=Address(address=token["contract"]), amount=token["amount"] - ) - for token in obj["receipt_tokens"] + obj["buy_tokens"] = [ + TokenAmount(token=Address(address=token["token"]), amount=token["amount"]) + for token in obj["buy_tokens"] ] - obj["repay_tokens"] = [ - TokenAmount( - contract=Address(address=token["contract"]), amount=token["amount"] - ) - for token in obj["repay_tokens"] + obj["sell_tokens"] = [ + TokenAmount(token=Address(address=token["token"]), amount=token["amount"]) + for token in obj["sell_tokens"] ] obj["opportunity_id"] = UUID(obj["opportunity_id"]) _obj = cls.model_validate( { - "calldata": obj["calldata"], + "target_calldata": obj["target_calldata"], "chain_id": obj["chain_id"], - "contract": obj["contract"], + "target_contract": obj["target_contract"], "permission_key": obj["permission_key"], - "receipt_tokens": obj["receipt_tokens"], - "repay_tokens": obj["repay_tokens"], - "value": obj["value"], + "buy_tokens": obj["buy_tokens"], + "sell_tokens": obj["sell_tokens"], + "target_call_value": obj["target_call_value"], "version": obj["version"], "creation_time": obj["creation_time"], "opportunity_id": obj["opportunity_id"], @@ -364,15 +358,15 @@ def to_dict(self): return {"method": str(self.method), "params": {"bid": self.bid.to_dict()}} -class PostLiquidationBidMessage(BaseModel): +class PostOpportunityBidMessage(BaseModel): """ Args: - method: A string literal "post_liquidation_bid". + method: A string literal "post_opportunity_bid". opportunity_id: The ID of the opportunity. opportunity_bid: The bid to post on the opportunity. """ - method: Literal["post_liquidation_bid"] + method: Literal["post_opportunity_bid"] opportunity_id: UUID opportunity_bid: OpportunityBid @@ -393,7 +387,7 @@ class ClientMessage(BaseModel): """ params: Union[ - SubscribeMessage, UnsubscribeMessage, PostBidMessage, PostLiquidationBidMessage + SubscribeMessage, UnsubscribeMessage, PostBidMessage, PostOpportunityBidMessage ] = Field(..., discriminator="method") def to_dict(self): diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index 5e87284fe3..2201b69811 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -3,8 +3,8 @@ import logging from eth_account.account import Account -from express_relay_client import ExpressRelayClient, sign_bid -from express_relay_types import * +from express_relay_utils.express_relay_client import ExpressRelayClient, sign_bid +from express_relay_utils.express_relay_types import * logger = logging.getLogger(__name__) @@ -18,22 +18,22 @@ def __init__(self, server_url: str, private_key: str): self.client = ExpressRelayClient( server_url, self.opportunity_callback, self.bid_status_callback ) - self.private_key = private_key - self.liquidator = Account.from_key(private_key).address + self.private_key = Bytes32(string=private_key).string + self.public_key = Account.from_key(private_key).address - def assess_liquidation_opportunity( + def assess_opportunity( self, opp: OpportunityParamsWithMetadata, ) -> OpportunityBidInfo | None: """ - Assesses whether a liquidation opportunity is worth liquidating; if so, returns an OpportunityBidInfo object. Otherwise returns None. + Assesses whether an opportunity is worth executing; if so, returns an OpportunityBidInfo object. Otherwise returns None. - This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the liquidation. - There are many ways to evaluate this, but the most common way is to check that the value of the amount the searcher will receive from the liquidation exceeds the value of the amount repaid. - Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation. + This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the opportunity. + There are many ways to evaluate this, but the most common way is to check that the value of the tokens the searcher will receive from execution exceeds the value of tokens spent. + Individual searchers will have their own methods to determine market impact and the profitability of executing an opportunity. This function can be expanded to include external prices to perform this evaluation. In this simple searcher, the function always (naively) returns an OpportunityBidInfo object with a default bid and valid_until timestamp. Args: - opp: A OpportunityParamsWithMetadata object, representing a single liquidation opportunity. + opp: A OpportunityParamsWithMetadata object, representing a single opportunity. Returns: If the opportunity is deemed worthwhile, this function can return an OpportunityBidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. """ @@ -45,12 +45,12 @@ def assess_liquidation_opportunity( async def opportunity_callback(self, opp: OpportunityParamsWithMetadata): """ - Callback function to run when a new liquidation opportunity is found. + Callback function to run when a new opportunity is found. Args: - opp: A OpportunityParamsWithMetadata object, representing a single liquidation opportunity. + opp: A OpportunityParamsWithMetadata object, representing a single opportunity. """ - opportunity_bid_info = self.assess_liquidation_opportunity(opp) + opportunity_bid_info = self.assess_opportunity(opp) if opportunity_bid_info: try: await self.client.submit_opportunity_bid(opportunity_bid_info) @@ -89,14 +89,14 @@ async def main(): "--private-key", type=str, required=True, - help="Private key of the searcher for signing calldata", + help="Private key of the searcher for signing calldata as a hex string", ) parser.add_argument( "--chain-ids", type=str, required=True, nargs="+", - help="Chain ID(s) of the network(s) to monitor for liquidation opportunities", + help="Chain ID(s) of the network(s) to monitor for opportunities", ) parser.add_argument( "--server-url", @@ -115,10 +115,8 @@ async def main(): log_handler.setFormatter(formatter) logger.addHandler(log_handler) - sk_liquidator = args.private_key - - simple_searcher = SimpleSearcher(args.server_url, sk_liquidator) - logger.info("Liquidator address: %s", simple_searcher.liquidator) + simple_searcher = SimpleSearcher(args.server_url, args.private_key) + logger.info("Searcher address: %s", simple_searcher.public_key) await simple_searcher.client.subscribe_chains(args.chain_ids) From c40c824f4f7c3bbabdaec6139dcfade5b4c64e55 Mon Sep 17 00:00:00 2001 From: ani Date: Wed, 13 Mar 2024 19:20:00 -0400 Subject: [PATCH 50/58] start to reorg classes --- .pre-commit-config.yaml | 4 +- .../python/express_relay_utils/__init__.py | 0 .../express_relay_client.py | 140 +++++--- .../express_relay_types.py | 316 ++++++------------ .../searcher/examples/simple_searcher.py | 61 ++-- express_relay/sdk/python/mypy.ini | 17 +- express_relay/sdk/python/poetry.lock | 75 ++--- 7 files changed, 274 insertions(+), 339 deletions(-) create mode 100644 express_relay/sdk/python/express_relay_utils/__init__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5fd99d2107..b8cb8ccdf7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -116,9 +116,9 @@ repos: name: black entry: poetry -C express_relay/sdk/python/express_relay_utils run black express_relay/sdk/python/express_relay_utils pass_filenames: false - language: system + language: "system" - id: pyflakes name: pyflakes entry: poetry -C express_relay/sdk/python/express_relay_utils run pyflakes express_relay/sdk/python/express_relay_utils pass_filenames: false - language: system + language: "system" diff --git a/express_relay/sdk/python/express_relay_utils/__init__.py b/express_relay/sdk/python/express_relay_utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 67ccd9178c..d02cc0fae2 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -27,17 +27,17 @@ def __init__( self, server_url: str, opportunity_callback: ( - Callable[[OpportunityParamsWithMetadata], Coroutine[Any, Any, Any]] | None + Callable[[Opportunity], Coroutine[Any, Any, Any]] | None ) = None, bid_status_callback: ( - Callable[[BidStatusWithId], Coroutine[Any, Any, Any]] | None + Callable[[BidStatus], Coroutine[Any, Any, Any]] | None ) = None, **kwargs, ): """ Args: server_url: The URL of the auction server. - opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. + opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type Opportunity. bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatus. kwargs: Keyword arguments to pass to the websocket connection. """ @@ -101,12 +101,17 @@ async def send_ws_message(self, method: str, params: dict) -> dict: """ await self.start_ws() - # validate the format of msg params.update({"method": method}) - msg_validated = ClientMessage.model_validate({"params": params}).to_dict() + + # validate the format of msg and construct the message dict to send + msg_validated = ClientMessage.model_validate({"params": params}).model_dump() + msg_validated["params"].pop("method") + msg_validated["method"] = method msg_validated["id"] = str(self.ws_msg_counter) self.ws_msg_counter += 1 + msg_validated = convert_to_server(msg_validated) + future = asyncio.get_event_loop().create_future() self.ws_msg_futures[msg_validated["id"]] = future @@ -169,8 +174,9 @@ async def submit_bid( Returns: The ID of the submitted bid. """ + bid_dict = bid.model_dump() if subscribe_to_updates: - result = await self.send_ws_message("post_bid", {"bid": bid}) + result = await self.send_ws_message("post_bid", bid_dict) bid_id = UUID(result.get("id")) else: async with httpx.AsyncClient() as client: @@ -178,7 +184,7 @@ async def submit_bid( urllib.parse.urlparse(self.server_url) ._replace(path="/v1/bids") .geturl(), - json=bid.to_dict(), + json=bid_dict, **kwargs, ) @@ -189,7 +195,7 @@ async def submit_bid( async def submit_opportunity_bid( self, - opportunity_bid_info: OpportunityBidInfo, + opportunity_bid: OpportunityBid, subscribe_to_updates: bool = True, **kwargs, ) -> UUID: @@ -203,26 +209,29 @@ async def submit_opportunity_bid( Returns: The ID of the submitted bid. """ + opportunity_bid_dict = opportunity_bid.model_dump() if subscribe_to_updates: result = await self.send_ws_message( "post_opportunity_bid", { - "opportunity_id": opportunity_bid_info.opportunity_id, - "opportunity_bid": opportunity_bid_info.opportunity_bid, + "opportunity_id": opportunity_bid.opportunity_id, + "amount": opportunity_bid.amount, + "executor": opportunity_bid.executor, + "permission_key": opportunity_bid.permission_key, + "signature": opportunity_bid.signature, + "valid_until": opportunity_bid.valid_until, }, ) bid_id = UUID(result.get("id")) else: - opportunity_bid_info_dict = opportunity_bid_info.to_dict() - opportunity_id = opportunity_bid_info_dict["opportunity_id"] - opportunity_bid = opportunity_bid_info_dict["opportunity_bid"] - async with httpx.AsyncClient() as client: resp = await client.post( urllib.parse.urlparse(self.server_url) - ._replace(path=f"/v1/opportunities/{opportunity_id}/bids") + ._replace( + path=f"/v1/opportunities/{opportunity_bid.opportunity_id}/bids" + ) .geturl(), - json=opportunity_bid, + json=opportunity_bid_dict, **kwargs, ) @@ -234,17 +243,17 @@ async def submit_opportunity_bid( async def ws_handler( self, opportunity_callback: ( - Callable[[OpportunityParamsWithMetadata], Coroutine[Any, Any, Any]] | None + Callable[[Opportunity], Coroutine[Any, Any, Any]] | None ) = None, bid_status_callback: ( - Callable[[BidStatusWithId], Coroutine[Any, Any, Any]] | None + Callable[[BidStatus], Coroutine[Any, Any, Any]] | None ) = None, ): """ Continually handles new ws messages as they are received from the server via websocket. Args: - opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type OpportunityParamsWithMetadata. + opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type Opportunity. bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatusWithId. """ if not self.ws: @@ -256,17 +265,21 @@ async def ws_handler( if msg_json.get("type"): if msg_json.get("type") == "new_opportunity": if opportunity_callback is not None: - opportunity = OpportunityParamsWithMetadata.from_dict( + opportunity = Opportunity.model_validate( msg_json.get("opportunity") ) - asyncio.create_task(opportunity_callback(opportunity)) + if opportunity: + asyncio.create_task(opportunity_callback(opportunity)) elif msg_json.get("type") == "bid_status_update": if bid_status_callback is not None: - bid_status_with_id = BidStatusWithId.from_dict( - msg_json.get("status") + id = msg_json.get("status").get("id") + status = msg_json.get("status").get("bid_status").get("status") + result = msg_json.get("status").get("bid_status").get("result") + bid_status = BidStatus( + id=id, status=Status(status), result=result ) - asyncio.create_task(bid_status_callback(bid_status_with_id)) + asyncio.create_task(bid_status_callback(bid_status)) elif msg_json.get("id"): future = self.ws_msg_futures.pop(msg_json["id"]) @@ -274,7 +287,7 @@ async def ws_handler( async def get_opportunities( self, chain_id: str | None = None, timeout_secs: int = 10 - ) -> list[OpportunityParamsWithMetadata]: + ) -> list[Opportunity]: """ Connects to the server and fetches opportunities. @@ -300,8 +313,7 @@ async def get_opportunities( resp.raise_for_status() opportunities = [ - OpportunityParamsWithMetadata.from_dict(opportunity) - for opportunity in resp.json() + Opportunity.model_validate(opportunity) for opportunity in resp.json() ] return opportunities @@ -323,7 +335,7 @@ async def submit_opportunity( urllib.parse.urlparse(self.server_url) ._replace(path="/v1/opportunities") .geturl(), - json=opportunity.to_dict(), + json=opportunity.params.model_dump(), timeout=timeout_secs, ) resp.raise_for_status() @@ -331,31 +343,27 @@ async def submit_opportunity( def sign_bid( - opportunity: OpportunityParamsWithMetadata, + opportunity: Opportunity, bid_amount: int, valid_until: int, private_key: str, -) -> OpportunityBidInfo: +) -> OpportunityBid: """ - Constructs a signature for a searcher's bid and returns the OpportunityBidInfo object to be submitted to the server. + Constructs a signature for a searcher's bid and returns the OpportunityBid object to be submitted to the server. Args: - opportunity: An object representing the opportunity, of type OpportunityParamsWithMetadata. + opportunity: An object representing the opportunity, of type Opportunity. bid_amount: An integer representing the amount of the bid (in wei). valid_until: An integer representing the unix timestamp until which the bid is valid. private_key: A 0x-prefixed hex string representing the searcher's private key. Returns: - A OpportunityBidInfo object, representing the transaction to submit to the server. This object contains the searcher's signature. + A OpportunityBid object, representing the transaction to submit to the server. This object contains the searcher's signature. """ sell_tokens = [ - (token.token.address, int(token.amount)) for token in opportunity.sell_tokens - ] - buy_tokens = [ - (token.token.address, int(token.amount)) for token in opportunity.buy_tokens + (token.token, int(token.amount)) for token in opportunity.sell_tokens ] - target_calldata = bytes.fromhex( - opportunity.target_calldata.string.replace("0x", "") - ) + buy_tokens = [(token.token, int(token.amount)) for token in opportunity.buy_tokens] + target_calldata = bytes.fromhex(opportunity.target_calldata.replace("0x", "")) digest = encode( [ @@ -370,9 +378,9 @@ def sign_bid( [ sell_tokens, buy_tokens, - opportunity.target_contract.address, + opportunity.target_contract, target_calldata, - int(opportunity.target_call_value), + opportunity.target_call_value, bid_amount, valid_until, ], @@ -380,18 +388,42 @@ def sign_bid( msg_data = web3.Web3.solidity_keccak(["bytes"], [digest]) signature = w3.eth.account.signHash(msg_data, private_key=private_key) - opportunity_bid_dict = { - "permission_key": opportunity.permission_key, - "amount": str(bid_amount), - "valid_until": str(valid_until), - "executor": Address(address=Account.from_key(private_key).address), - "signature": signature, - } - opportunity_bid = OpportunityBid.model_validate(opportunity_bid_dict) - - opportunity_bid_info = OpportunityBidInfo( + opportunity_bid = OpportunityBid( opportunity_id=opportunity.opportunity_id, - opportunity_bid=opportunity_bid, + permission_key=opportunity.permission_key, + amount=bid_amount, + valid_until=valid_until, + executor=Account.from_key(private_key).address, + signature=signature, ) - return opportunity_bid_info + return opportunity_bid + + +def convert_to_server(msg: dict): + if msg["method"] == "post_bid": + params = { + "bid": { + "amount": msg["params"]["amount"], + "target_contract": msg["params"]["target_contract"], + "chain_id": msg["params"]["chain_id"], + "target_calldata": msg["params"]["target_calldata"], + "permission_key": msg["params"]["permission_key"], + } + } + elif msg["method"] == "post_opportunity_bid": + params = { + "opportunity_id": msg["params"]["opportunity_id"], + "opportunity_bid": { + "amount": msg["params"]["amount"], + "executor": msg["params"]["executor"], + "permission_key": msg["params"]["permission_key"], + "signature": msg["params"]["signature"], + "valid_until": msg["params"]["valid_until"], + }, + } + else: + params = msg["params"] + + msg["params"] = params + return msg diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_types.py b/express_relay/sdk/python/express_relay_utils/express_relay_types.py index 7c4dd9580c..8588bba03e 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_types.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_types.py @@ -1,15 +1,18 @@ from enum import Enum from pydantic import BaseModel, field_validator, model_validator +from pydantic.functional_validators import WrapValidator, AfterValidator +from pydantic.functional_serializers import PlainSerializer from uuid import UUID import web3 from typing import Union, Dict, Any from pydantic import Field -from typing_extensions import Literal +from typing_extensions import Literal, Annotated +import warnings from eth_account.datastructures import SignedMessage -def assert_hex_string(s: str): +def maybe_hex_string(s: str): """ Validates that a string is a valid hex string. @@ -17,64 +20,69 @@ def assert_hex_string(s: str): s: The string to validate as a hex string. Can be '0x'-prefixed. """ if s.startswith("0x"): - s = s[2:] + s_set = set(s[2:]) + else: + s_set = set(s) - s_set = set(s) hex_set = set("0123456789abcdefABCDEF") - assert s_set.issubset(hex_set), "string must be a valid hex string" + assert s_set.issubset(hex_set), "string is not a valid hex string" + return s -class HexString(BaseModel): + +def maybe_bytes32(s: str): """ + Validates that a string is a valid 32-byte hex string. + Args: - string: The string to validate as a hex string. + s: The string to validate as a 32-byte hex string. Can be '0x'-prefixed. """ - - string: str - - @field_validator("string") - @classmethod - def check_hex(cls, v: str): - assert_hex_string(v) - - return v + maybe_hex_string(s) + if s.startswith("0x"): + assert len(s[2:]) == 64, "hex string is not 32 bytes long" + else: + assert len(s) == 64, "hex string is not 32 bytes long" + return s -class Address(BaseModel): +def maybe_address(s: str): """ + Validates that a string is a valid Ethereum address. + Args: - address: The string to validate as an Ethereum address. + s: The string to validate as an Ethereum address. Can be '0x'-prefixed. """ + ## TODO: check does this need to be 0x prefixed for stuff on rust side to work + assert web3.Web3.is_address(s), "string is not a valid Ethereum address" + return s + - address: str +HexString = Annotated[str, AfterValidator(maybe_hex_string)] +Bytes32 = Annotated[str, AfterValidator(maybe_bytes32)] +Address = Annotated[str, AfterValidator(maybe_address)] - @field_validator("address") - @classmethod - def check_address(cls, v: str): - assert web3.Web3.is_address(v), "string must be a valid Ethereum address" - return v +IntString = Annotated[int, PlainSerializer(lambda x: str(x), return_type=str)] +UUIDString = Annotated[UUID, PlainSerializer(lambda x: str(x), return_type=str)] +SignedMessageString = Annotated[ + SignedMessage, PlainSerializer(lambda x: bytes(x.signature).hex(), return_type=str) +] -class Bytes32(BaseModel): +class TokenAmount(BaseModel): """ - Args: - string: The hex string to validate as a 32-byte hash. + Attributes: + token: The address of the token contract. + amount: The amount of the token. """ - string: str - - @field_validator("string") - @classmethod - def check_bytes32(cls, v: str): - assert_hex_string(v) - assert len(v.replace("0x", "")) == 64, "string must be 32 bytes long" - return v + token: Address + amount: IntString class Bid(BaseModel): """ - Args: + Attributes: amount: The amount of the bid in wei. target_calldata: The calldata for the contract call. chain_id: The chain ID to bid on. @@ -82,21 +90,12 @@ class Bid(BaseModel): permission_key: The permission key to bid on. """ - amount: int + amount: IntString target_calldata: HexString chain_id: str target_contract: Address permission_key: HexString - def to_dict(self): - return { - "amount": str(self.amount), - "target_calldata": self.target_calldata.string, - "chain_id": self.chain_id, - "target_contract": self.target_contract.address, - "permission_key": self.permission_key.string, - } - class Status(Enum): SUBMITTED = "submitted" @@ -106,11 +105,13 @@ class Status(Enum): class BidStatus(BaseModel): """ - Args: + Attributes: + id: The ID of the bid. status: The status enum, either SUBMITTED, LOST, or PENDING. result: The result of the bid: a transaction hash if the status is SUBMITTED, else None. """ + id: UUIDString status: Status result: Bytes32 | None = Field(default=None) @@ -123,34 +124,10 @@ def check_result(self): return self -class BidStatusWithId(BaseModel): - """ - Args: - bid_status: The status of the bid. - id: The ID of the bid. - """ - - bid_status: BidStatus - id: UUID - - @classmethod - def from_dict(cls, obj: Dict[str, Any]): - bid_status_dict = obj["bid_status"] - status = bid_status_dict["status"] - result = bid_status_dict.get("result") - if result: - result = Bytes32(string=result) - obj["bid_status"] = BidStatus(status=status, result=result) - obj["id"] = UUID(obj["id"]) - - _obj = cls.model_validate({"bid_status": obj["bid_status"], "id": obj["id"]}) - - return _obj - - class OpportunityBid(BaseModel): """ - Args: + Attributes: + opportunity_id: The ID of the opportunity. amount: The amount of the bid in wei. executor: The address of the executor. permission_key: The permission key to bid on. @@ -158,57 +135,21 @@ class OpportunityBid(BaseModel): valid_until: The unix timestamp after which the bid becomes invalid. """ - amount: int + opportunity_id: UUIDString + amount: IntString executor: Address permission_key: HexString - signature: SignedMessage - valid_until: int + signature: SignedMessageString + valid_until: IntString model_config = { "arbitrary_types_allowed": True, } - def to_dict(self): - return { - "amount": str(self.amount), - "executor": self.executor.address, - "permission_key": self.permission_key.string, - "signature": bytes(self.signature.signature).hex(), - "valid_until": str(self.valid_until), - } - - -class OpportunityBidInfo(BaseModel): - """ - Args: - opportunity_id: The ID of the opportunity. - opportunity_bid: The bid to submit on the opportunity. - """ - - opportunity_id: UUID - opportunity_bid: OpportunityBid - - def to_dict(self): - return { - "opportunity_id": str(self.opportunity_id), - "opportunity_bid": self.opportunity_bid.to_dict(), - } - - -class TokenAmount(BaseModel): - """ - Args: - token: The address of the token contract. - amount: The amount of the token. - """ - - token: Address - amount: int - class OpportunityParamsV1(BaseModel): """ - Args: + Attributes: target_calldata: The calldata for the contract call. chain_id: The chain ID to bid on. target_contract: The contract address to call. @@ -225,29 +166,13 @@ class OpportunityParamsV1(BaseModel): permission_key: HexString buy_tokens: list[TokenAmount] sell_tokens: list[TokenAmount] - target_call_value: int + target_call_value: IntString version: Literal["v1"] - def to_dict(self): - return { - "target_calldata": self.target_calldata.string, - "chain_id": self.chain_id, - "target_contract": self.target_contract.address, - "permission_key": self.permission_key.string, - "buy_tokens": [ - (token.token.address, str(token.amount)) for token in self.buy_tokens - ], - "sell_tokens": [ - (token.token.address, str(token.amount)) for token in self.sell_tokens - ], - "target_call_value": str(self.target_call_value), - "version": self.version, - } - class OpportunityParams(BaseModel): """ - Args: + Attributes: params: The parameters of the opportunity. """ @@ -257,9 +182,9 @@ def to_dict(self): return self.params.to_dict() -class OpportunityParamsWithMetadata(BaseModel): +class Opportunity(BaseModel): """ - Args: + Attributes: target_calldata: The calldata for the contract call. chain_id: The chain ID to bid on. target_contract: The contract address to call. @@ -278,47 +203,24 @@ class OpportunityParamsWithMetadata(BaseModel): permission_key: HexString buy_tokens: list[TokenAmount] sell_tokens: list[TokenAmount] - target_call_value: int + target_call_value: IntString version: str - creation_time: int - opportunity_id: UUID - - @classmethod - def from_dict(cls, obj: Dict[str, Any]): - obj["target_calldata"] = HexString(string=obj["target_calldata"]) - obj["target_contract"] = Address(address=obj["target_contract"]) - obj["permission_key"] = HexString(string=obj["permission_key"]) - obj["buy_tokens"] = [ - TokenAmount(token=Address(address=token["token"]), amount=token["amount"]) - for token in obj["buy_tokens"] - ] - obj["sell_tokens"] = [ - TokenAmount(token=Address(address=token["token"]), amount=token["amount"]) - for token in obj["sell_tokens"] - ] - obj["opportunity_id"] = UUID(obj["opportunity_id"]) - - _obj = cls.model_validate( - { - "target_calldata": obj["target_calldata"], - "chain_id": obj["chain_id"], - "target_contract": obj["target_contract"], - "permission_key": obj["permission_key"], - "buy_tokens": obj["buy_tokens"], - "sell_tokens": obj["sell_tokens"], - "target_call_value": obj["target_call_value"], - "version": obj["version"], - "creation_time": obj["creation_time"], - "opportunity_id": obj["opportunity_id"], - } - ) - - return _obj - - -class SubscribeMessage(BaseModel): + creation_time: IntString + opportunity_id: UUIDString + + @model_validator(mode="after") + def check_version(self): + if self.version not in ["v1"]: + warnings.warn( + f"Cannot handle opportunity version: {self.version}. Please upgrade your client." + ) + return None + return self + + +class SubscribeMessageParams(BaseModel): """ - Args: + Attributes: method: A string literal "subscribe". chain_ids: The chain IDs to subscribe to. """ @@ -326,13 +228,10 @@ class SubscribeMessage(BaseModel): method: Literal["subscribe"] chain_ids: list[str] - def to_dict(self): - return {"method": str(self.method), "params": {"chain_ids": self.chain_ids}} - -class UnsubscribeMessage(BaseModel): +class UnsubscribeMessageParams(BaseModel): """ - Args: + Attributes: method: A string literal "unsubscribe". chain_ids: The chain IDs to subscribe to. """ @@ -340,55 +239,60 @@ class UnsubscribeMessage(BaseModel): method: Literal["unsubscribe"] chain_ids: list[str] - def to_dict(self): - return {"method": str(self.method), "params": {"chain_ids": self.chain_ids}} - -class PostBidMessage(BaseModel): +class PostBidMessageParams(BaseModel): """ - Args: + Attributes: method: A string literal "post_bid". - bid: The bid to post. + amount: The amount of the bid in wei. + target_calldata: The calldata for the contract call. + chain_id: The chain ID to bid on. + target_contract: The contract address to call. + permission_key: The permission key to bid on. """ method: Literal["post_bid"] - bid: Bid - - def to_dict(self): - return {"method": str(self.method), "params": {"bid": self.bid.to_dict()}} + amount: IntString + target_calldata: HexString + chain_id: str + target_contract: Address + permission_key: HexString -class PostOpportunityBidMessage(BaseModel): +class PostOpportunityBidMessageParams(BaseModel): """ - Args: + Attributes: method: A string literal "post_opportunity_bid". opportunity_id: The ID of the opportunity. - opportunity_bid: The bid to post on the opportunity. + amount: The amount of the bid in wei. + executor: The address of the executor. + permission_key: The permission key to bid on. + signature: The signature of the bid. + valid_until: The unix timestamp after which the bid becomes invalid. """ method: Literal["post_opportunity_bid"] - opportunity_id: UUID - opportunity_bid: OpportunityBid + opportunity_id: UUIDString + amount: IntString + executor: Address + permission_key: HexString + signature: SignedMessageString + valid_until: IntString - def to_dict(self): - return { - "method": str(self.method), - "params": { - "opportunity_id": str(self.opportunity_id), - "opportunity_bid": self.opportunity_bid.to_dict(), - }, - } + model_config = { + "arbitrary_types_allowed": True, + } class ClientMessage(BaseModel): """ - Args: + Attributes: params: The parameters of the message. """ params: Union[ - SubscribeMessage, UnsubscribeMessage, PostBidMessage, PostOpportunityBidMessage + SubscribeMessageParams, + UnsubscribeMessageParams, + PostBidMessageParams, + PostOpportunityBidMessageParams, ] = Field(..., discriminator="method") - - def to_dict(self): - return self.params.to_dict() diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index 2201b69811..d36e1e924e 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -14,72 +14,71 @@ class SimpleSearcher: - def __init__(self, server_url: str, private_key: str): + def __init__(self, server_url: str, private_key: Bytes32): self.client = ExpressRelayClient( server_url, self.opportunity_callback, self.bid_status_callback ) - self.private_key = Bytes32(string=private_key).string + self.private_key = private_key self.public_key = Account.from_key(private_key).address def assess_opportunity( self, - opp: OpportunityParamsWithMetadata, - ) -> OpportunityBidInfo | None: + opp: Opportunity, + ) -> OpportunityBid | None: """ - Assesses whether an opportunity is worth executing; if so, returns an OpportunityBidInfo object. Otherwise returns None. + Assesses whether an opportunity is worth executing; if so, returns an OpportunityBid object. Otherwise returns None. This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the opportunity. There are many ways to evaluate this, but the most common way is to check that the value of the tokens the searcher will receive from execution exceeds the value of tokens spent. Individual searchers will have their own methods to determine market impact and the profitability of executing an opportunity. This function can be expanded to include external prices to perform this evaluation. - In this simple searcher, the function always (naively) returns an OpportunityBidInfo object with a default bid and valid_until timestamp. + In this simple searcher, the function always (naively) returns an OpportunityBid object with a default bid and valid_until timestamp. Args: - opp: A OpportunityParamsWithMetadata object, representing a single opportunity. + opp: An object representing a single opportunity. Returns: - If the opportunity is deemed worthwhile, this function can return an OpportunityBidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. + If the opportunity is deemed worthwhile, this function can return an OpportunityBid object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None. """ - opportunity_bid_info = sign_bid( - opp, NAIVE_BID, VALID_UNTIL_MAX, self.private_key - ) + opportunity_bid = sign_bid(opp, NAIVE_BID, VALID_UNTIL_MAX, self.private_key) - return opportunity_bid_info + return opportunity_bid - async def opportunity_callback(self, opp: OpportunityParamsWithMetadata): + async def opportunity_callback(self, opp: Opportunity): """ Callback function to run when a new opportunity is found. Args: - opp: A OpportunityParamsWithMetadata object, representing a single opportunity. + opp: An object representing a single opportunity. """ - opportunity_bid_info = self.assess_opportunity(opp) - if opportunity_bid_info: + opportunity_bid = self.assess_opportunity(opp) + if opportunity_bid: try: - await self.client.submit_opportunity_bid(opportunity_bid_info) + await self.client.submit_opportunity_bid(opportunity_bid) logger.info( - f"Submitted bid amount {opportunity_bid_info.opportunity_bid.amount} for opportunity {str(opportunity_bid_info.opportunity_id)}" + f"Submitted bid amount {opportunity_bid.amount} for opportunity {str(opportunity_bid.opportunity_id)}" ) except Exception as e: logger.error( - f"Error submitting bid amount {opportunity_bid_info.opportunity_bid.amount} for opportunity {str(opportunity_bid_info.opportunity_id)}: {e}" + f"Error submitting bid amount {opportunity_bid.amount} for opportunity {str(opportunity_bid.opportunity_id)}: {e}" ) - async def bid_status_callback(self, bid_status_with_id: BidStatusWithId): + async def bid_status_callback(self, bid_status: BidStatus): """ Callback function to run when a bid status is updated. Args: - bid_status_with_id: A BidStatusWithId object, representing the status of a bid. + bid_status: An object representing the status of a bid. """ - bid_id = bid_status_with_id.id - bid_status = bid_status_with_id.bid_status - - if bid_status.status == Status("submitted"): - logger.info(f"Bid {bid_id} has been submitted in hash {bid_status.result}") - elif bid_status.status == Status("lost"): - logger.info(f"Bid {bid_id} was unsuccessful") - elif bid_status.status == Status("pending"): - logger.info(f"Bid {bid_id} is pending") + id = bid_status.id + status = bid_status.status + result = bid_status.result + + if status == Status("submitted"): + logger.info(f"Bid {id} has been submitted in hash {result}") + elif status == Status("lost"): + logger.info(f"Bid {id} was unsuccessful") + elif status == Status("pending"): + logger.info(f"Bid {id} is pending") else: - logger.error(f"Unrecognized status {bid_status.status} for bid {bid_id}") + logger.error(f"Unrecognized status {status} for bid {id}") async def main(): diff --git a/express_relay/sdk/python/mypy.ini b/express_relay/sdk/python/mypy.ini index 895701cc05..b789e41879 100644 --- a/express_relay/sdk/python/mypy.ini +++ b/express_relay/sdk/python/mypy.ini @@ -1,2 +1,17 @@ [mypy] -plugins = pydantic.mypy \ No newline at end of file +plugins = pydantic.mypy + +follow_imports = silent +warn_redundant_casts = True +warn_unused_ignores = True +disallow_any_generics = True +check_untyped_defs = True +no_implicit_reexport = True + +; # for strict mypy: (this is the tricky one :-)) +; disallow_untyped_defs = False + +[pydantic-mypy] +init_forbid_extra = True +init_typed = True +warn_required_dynamic_aliases = True diff --git a/express_relay/sdk/python/poetry.lock b/express_relay/sdk/python/poetry.lock index d60b4758fb..9b8e49e986 100644 --- a/express_relay/sdk/python/poetry.lock +++ b/express_relay/sdk/python/poetry.lock @@ -1241,13 +1241,13 @@ files = [ [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] @@ -1291,22 +1291,22 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest- [[package]] name = "protobuf" -version = "4.25.3" +version = "5.26.0" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, - {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, - {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, - {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, - {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, - {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, - {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, - {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, - {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, + {file = "protobuf-5.26.0-cp310-abi3-win32.whl", hash = "sha256:f9ecc8eb6f18037e0cbf43256db0325d4723f429bca7ef5cd358b7c29d65f628"}, + {file = "protobuf-5.26.0-cp310-abi3-win_amd64.whl", hash = "sha256:dfd29f6eb34107dccf289a93d44fb6b131e68888d090b784b691775ac84e8213"}, + {file = "protobuf-5.26.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:7e47c57303466c867374a17b2b5e99c5a7c8b72a94118e2f28efb599f19b4069"}, + {file = "protobuf-5.26.0-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e184175276edc222e2d5e314a72521e10049938a9a4961fe4bea9b25d073c03f"}, + {file = "protobuf-5.26.0-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:6ee9d1aa02f951c5ce10bf8c6cfb7604133773038e33f913183c8b5201350600"}, + {file = "protobuf-5.26.0-cp38-cp38-win32.whl", hash = "sha256:2c334550e1cb4efac5c8a3987384bf13a4334abaf5ab59e40479e7b70ecd6b19"}, + {file = "protobuf-5.26.0-cp38-cp38-win_amd64.whl", hash = "sha256:8eef61a90631c21b06b4f492a27e199a269827f046de3bb68b61aa84fcf50905"}, + {file = "protobuf-5.26.0-cp39-cp39-win32.whl", hash = "sha256:ca825f4eecb8c342d2ec581e6a5ad1ad1a47bededaecd768e0d3451ae4aaac2b"}, + {file = "protobuf-5.26.0-cp39-cp39-win_amd64.whl", hash = "sha256:efd4f5894c50bd76cbcfdd668cd941021333861ed0f441c78a83d8254a01cc9f"}, + {file = "protobuf-5.26.0-py3-none-any.whl", hash = "sha256:a49b6c5359bf34fb7bf965bf21abfab4476e4527d822ab5289ee3bf73f291159"}, + {file = "protobuf-5.26.0.tar.gz", hash = "sha256:82f5870d74c99addfe4152777bdf8168244b9cf0ac65f8eccf045ddfa9d80d9b"}, ] [[package]] @@ -1352,13 +1352,13 @@ files = [ [[package]] name = "pydantic" -version = "2.6.3" +version = "2.6.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, - {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, ] [package.dependencies] @@ -1460,6 +1460,17 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + [[package]] name = "pyunormalize" version = "15.1.0" @@ -1776,32 +1787,6 @@ files = [ {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, ] -[[package]] -name = "ruff" -version = "0.3.1" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6b82e3937d0d76554cd5796bc3342a7d40de44494d29ff490022d7a52c501744"}, - {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ae7954c8f692b70e6a206087ae3988acc9295d84c550f8d90b66c62424c16771"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b730f56ccf91225da0f06cfe421e83b8cc27b2a79393db9c3df02ed7e2bbc01"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c78bfa85637668f47bd82aa2ae17de2b34221ac23fea30926f6409f9e37fc927"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6abaad602d6e6daaec444cbf4d9364df0a783e49604c21499f75bb92237d4af"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0c21b6914c3c9a25a59497cbb1e5b6c2d8d9beecc9b8e03ee986e24eee072e"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434c3fc72e6311c85cd143c4c448b0e60e025a9ac1781e63ba222579a8c29200"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78a7025e6312cbba496341da5062e7cdd47d95f45c1b903e635cdeb1ba5ec2b9"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52b02bb46f1a79b0c1fa93f6495bc7e77e4ef76e6c28995b4974a20ed09c0833"}, - {file = "ruff-0.3.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11b5699c42f7d0b771c633d620f2cb22e727fb226273aba775a91784a9ed856c"}, - {file = "ruff-0.3.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:54e5dca3e411772b51194b3102b5f23b36961e8ede463776b289b78180df71a0"}, - {file = "ruff-0.3.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:951efb610c5844e668bbec4f71cf704f8645cf3106e13f283413969527ebfded"}, - {file = "ruff-0.3.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:09c7333b25e983aabcf6e38445252cff0b4745420fc3bda45b8fce791cc7e9ce"}, - {file = "ruff-0.3.1-py3-none-win32.whl", hash = "sha256:d937f9b99ebf346e0606c3faf43c1e297a62ad221d87ef682b5bdebe199e01f6"}, - {file = "ruff-0.3.1-py3-none-win_amd64.whl", hash = "sha256:c0318a512edc9f4e010bbaab588b5294e78c5cdc9b02c3d8ab2d77c7ae1903e3"}, - {file = "ruff-0.3.1-py3-none-win_arm64.whl", hash = "sha256:d3b60e44240f7e903e6dbae3139a65032ea4c6f2ad99b6265534ff1b83c20afa"}, - {file = "ruff-0.3.1.tar.gz", hash = "sha256:d30db97141fc2134299e6e983a6727922c9e03c031ae4883a6d69461de722ae7"}, -] - [[package]] name = "sniffio" version = "1.3.1" @@ -2072,4 +2057,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "d8096d6e5bc2ee1301aabd700fbc6a0bd52a089adfe0f1d680a64a3086bff74a" +content-hash = "3cb6f636bf63899e6d907f231f98d85d5d6b7ba6ad8678bb12d9cbba8ac2c38e" From 3423ee7907b6ed3e604a969bde1b9779c9bb4446 Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 14 Mar 2024 01:09:15 -0400 Subject: [PATCH 51/58] configure precommit to work --- .pre-commit-config.yaml | 5 +++++ .../python/express_relay_utils/express_relay_client.py | 10 +++++++++- .../python/express_relay_utils/express_relay_types.py | 6 +++--- .../searcher/examples/simple_searcher.py | 8 +++++++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8cb8ccdf7..5932a7f0da 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -122,3 +122,8 @@ repos: entry: poetry -C express_relay/sdk/python/express_relay_utils run pyflakes express_relay/sdk/python/express_relay_utils pass_filenames: false language: "system" + - id: mypy + name: mypy + entry: poetry -C express_relay/sdk/python/express_relay_utils run mypy express_relay/sdk/python/express_relay_utils + pass_filenames: false + language: "system" diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index d02cc0fae2..1d11f8a22f 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -15,7 +15,15 @@ from web3.auto import w3 -from express_relay_utils.express_relay_types import * +from express_relay_utils.express_relay_types import ( + Opportunity, + BidStatus, + ClientMessage, + Status, + Bid, + OpportunityBid, + OpportunityParams, +) class ExpressRelayClientException(Exception): diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_types.py b/express_relay/sdk/python/express_relay_utils/express_relay_types.py index 8588bba03e..316cc8238c 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_types.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_types.py @@ -1,10 +1,10 @@ from enum import Enum -from pydantic import BaseModel, field_validator, model_validator -from pydantic.functional_validators import WrapValidator, AfterValidator +from pydantic import BaseModel, model_validator +from pydantic.functional_validators import AfterValidator from pydantic.functional_serializers import PlainSerializer from uuid import UUID import web3 -from typing import Union, Dict, Any +from typing import Union from pydantic import Field from typing_extensions import Literal, Annotated import warnings diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index d36e1e924e..cbb48dc5f5 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -4,7 +4,13 @@ from eth_account.account import Account from express_relay_utils.express_relay_client import ExpressRelayClient, sign_bid -from express_relay_utils.express_relay_types import * +from express_relay_utils.express_relay_types import ( + Opportunity, + OpportunityBid, + Bytes32, + Status, + BidStatus, +) logger = logging.getLogger(__name__) From 559533eab3ea02b7d20c286539b98801ae2ae220 Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 14 Mar 2024 15:16:54 -0400 Subject: [PATCH 52/58] some cleanup --- .../express_relay_client.py | 44 +++++++++++-------- .../express_relay_types.py | 32 +++++++------- .../searcher/examples/simple_searcher.py | 27 ++++++------ 3 files changed, 54 insertions(+), 49 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 1d11f8a22f..08cd0012df 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -5,21 +5,18 @@ from typing import Callable, Any from collections.abc import Coroutine from uuid import UUID - import httpx import web3 import websockets from websockets.client import WebSocketClientProtocol from eth_abi import encode from eth_account.account import Account - from web3.auto import w3 - from express_relay_utils.express_relay_types import ( Opportunity, - BidStatus, + BidStatusUpdate, ClientMessage, - Status, + BidStatus, Bid, OpportunityBid, OpportunityParams, @@ -38,7 +35,7 @@ def __init__( Callable[[Opportunity], Coroutine[Any, Any, Any]] | None ) = None, bid_status_callback: ( - Callable[[BidStatus], Coroutine[Any, Any, Any]] | None + Callable[[BidStatusUpdate], Coroutine[Any, Any, Any]] | None ) = None, **kwargs, ): @@ -46,7 +43,7 @@ def __init__( Args: server_url: The URL of the auction server. opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type Opportunity. - bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatus. + bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatusUpdate. kwargs: Keyword arguments to pass to the websocket connection. """ parsed_url = urllib.parse.urlparse(server_url) @@ -176,7 +173,7 @@ async def submit_bid( Submits a bid to the auction server. Args: - bid_info: An object representing the bid to submit. + bid: An object representing the bid to submit. subscribe_to_updates: A boolean indicating whether to subscribe to the bid status updates. kwargs: Keyword arguments to pass to the HTTP post request. Returns: @@ -211,7 +208,7 @@ async def submit_opportunity_bid( Submits a bid on an opportunity to the server via websocket. Args: - opportunity_bid_info: An object representing the bid to submit on an opportunity. + opportunity_bid: An object representing the bid to submit on an opportunity. subscribe_to_updates: A boolean indicating whether to subscribe to the bid status updates. kwargs: Keyword arguments to pass to the HTTP post request. Returns: @@ -254,7 +251,7 @@ async def ws_handler( Callable[[Opportunity], Coroutine[Any, Any, Any]] | None ) = None, bid_status_callback: ( - Callable[[BidStatus], Coroutine[Any, Any, Any]] | None + Callable[[BidStatusUpdate], Coroutine[Any, Any, Any]] | None ) = None, ): """ @@ -262,7 +259,7 @@ async def ws_handler( Args: opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type Opportunity. - bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatusWithId. + bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatusUpdate. """ if not self.ws: raise ExpressRelayClientException("Websocket not connected") @@ -282,12 +279,14 @@ async def ws_handler( elif msg_json.get("type") == "bid_status_update": if bid_status_callback is not None: id = msg_json.get("status").get("id") - status = msg_json.get("status").get("bid_status").get("status") + bid_status = ( + msg_json.get("status").get("bid_status").get("status") + ) result = msg_json.get("status").get("bid_status").get("result") - bid_status = BidStatus( - id=id, status=Status(status), result=result + bid_status_update = BidStatusUpdate( + id=id, bid_status=BidStatus(bid_status), result=result ) - asyncio.create_task(bid_status_callback(bid_status)) + asyncio.create_task(bid_status_callback(bid_status_update)) elif msg_json.get("id"): future = self.ws_msg_futures.pop(msg_json["id"]) @@ -408,7 +407,15 @@ def sign_bid( return opportunity_bid -def convert_to_server(msg: dict): +def convert_to_server(msg: dict) -> dict: + """ + Converts the params of a client message to the format expected by the server. + + Args: + msg: The message to convert. + Returns: + The message with the params converted to the format expected by the server. + """ if msg["method"] == "post_bid": params = { "bid": { @@ -419,6 +426,7 @@ def convert_to_server(msg: dict): "permission_key": msg["params"]["permission_key"], } } + msg["params"] = params elif msg["method"] == "post_opportunity_bid": params = { "opportunity_id": msg["params"]["opportunity_id"], @@ -430,8 +438,6 @@ def convert_to_server(msg: dict): "valid_until": msg["params"]["valid_until"], }, } - else: - params = msg["params"] + msg["params"] = params - msg["params"] = params return msg diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_types.py b/express_relay/sdk/python/express_relay_utils/express_relay_types.py index 316cc8238c..6705640cab 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_types.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_types.py @@ -8,7 +8,7 @@ from pydantic import Field from typing_extensions import Literal, Annotated import warnings - +import string from eth_account.datastructures import SignedMessage @@ -19,14 +19,13 @@ def maybe_hex_string(s: str): Args: s: The string to validate as a hex string. Can be '0x'-prefixed. """ + ind = 0 if s.startswith("0x"): - s_set = set(s[2:]) - else: - s_set = set(s) - - hex_set = set("0123456789abcdefABCDEF") + ind = 2 - assert s_set.issubset(hex_set), "string is not a valid hex string" + assert all( + c in string.hexdigits for c in s[ind:] + ), "string is not a valid hex string" return s @@ -39,10 +38,12 @@ def maybe_bytes32(s: str): s: The string to validate as a 32-byte hex string. Can be '0x'-prefixed. """ maybe_hex_string(s) + ind = 0 if s.startswith("0x"): - assert len(s[2:]) == 64, "hex string is not 32 bytes long" - else: - assert len(s) == 64, "hex string is not 32 bytes long" + ind = 2 + + assert len(s[ind:]) == 64, "hex string is not 32 bytes long" + return s @@ -53,7 +54,6 @@ def maybe_address(s: str): Args: s: The string to validate as an Ethereum address. Can be '0x'-prefixed. """ - ## TODO: check does this need to be 0x prefixed for stuff on rust side to work assert web3.Web3.is_address(s), "string is not a valid Ethereum address" return s @@ -97,27 +97,27 @@ class Bid(BaseModel): permission_key: HexString -class Status(Enum): +class BidStatus(Enum): SUBMITTED = "submitted" LOST = "lost" PENDING = "pending" -class BidStatus(BaseModel): +class BidStatusUpdate(BaseModel): """ Attributes: id: The ID of the bid. - status: The status enum, either SUBMITTED, LOST, or PENDING. + bid_status: The status enum, either SUBMITTED, LOST, or PENDING. result: The result of the bid: a transaction hash if the status is SUBMITTED, else None. """ id: UUIDString - status: Status + bid_status: BidStatus result: Bytes32 | None = Field(default=None) @model_validator(mode="after") def check_result(self): - if self.status == Status("submitted"): + if self.bid_status == BidStatus("submitted"): assert self.result is not None, "result must be a valid 32-byte hash" else: assert self.result is None, "result must be None" diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py index cbb48dc5f5..4f26e80c7a 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py @@ -1,15 +1,14 @@ import argparse import asyncio import logging - from eth_account.account import Account from express_relay_utils.express_relay_client import ExpressRelayClient, sign_bid from express_relay_utils.express_relay_types import ( Opportunity, OpportunityBid, Bytes32, - Status, BidStatus, + BidStatusUpdate, ) logger = logging.getLogger(__name__) @@ -34,9 +33,9 @@ def assess_opportunity( """ Assesses whether an opportunity is worth executing; if so, returns an OpportunityBid object. Otherwise returns None. - This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the opportunity. - There are many ways to evaluate this, but the most common way is to check that the value of the tokens the searcher will receive from execution exceeds the value of tokens spent. - Individual searchers will have their own methods to determine market impact and the profitability of executing an opportunity. This function can be expanded to include external prices to perform this evaluation. + This function determines whether the given opportunity is worthwhile to execute. + There are many ways to evaluate this, but the most common way is to check that the value of the tokens the searcher will receive from execution exceeds the value of the tokens spent. + Individual searchers will have their own methods to determine market impact and the profitability of executing an opportunity. This function can use external prices to perform this evaluation. In this simple searcher, the function always (naively) returns an OpportunityBid object with a default bid and valid_until timestamp. Args: opp: An object representing a single opportunity. @@ -66,25 +65,25 @@ async def opportunity_callback(self, opp: Opportunity): f"Error submitting bid amount {opportunity_bid.amount} for opportunity {str(opportunity_bid.opportunity_id)}: {e}" ) - async def bid_status_callback(self, bid_status: BidStatus): + async def bid_status_callback(self, bid_status_update: BidStatusUpdate): """ Callback function to run when a bid status is updated. Args: - bid_status: An object representing the status of a bid. + bid_status_update: An object representing an update to the status of a bid. """ - id = bid_status.id - status = bid_status.status - result = bid_status.result + id = bid_status_update.id + bid_status = bid_status_update.bid_status + result = bid_status_update.result - if status == Status("submitted"): + if bid_status == BidStatus("submitted"): logger.info(f"Bid {id} has been submitted in hash {result}") - elif status == Status("lost"): + elif bid_status == BidStatus("lost"): logger.info(f"Bid {id} was unsuccessful") - elif status == Status("pending"): + elif bid_status == BidStatus("pending"): logger.info(f"Bid {id} is pending") else: - logger.error(f"Unrecognized status {status} for bid {id}") + logger.error(f"Unrecognized status {bid_status} for bid {id}") async def main(): From c586c60ad1078f8654e7c0bee0fffe39de8af968 Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 14 Mar 2024 16:17:01 -0400 Subject: [PATCH 53/58] reorg a bit --- .../express_relay_client.py | 49 ++----------------- .../express_relay_types.py | 38 ++++++++++++++ 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index 08cd0012df..fc320c2331 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -109,18 +109,15 @@ async def send_ws_message(self, method: str, params: dict) -> dict: params.update({"method": method}) # validate the format of msg and construct the message dict to send - msg_validated = ClientMessage.model_validate({"params": params}).model_dump() - msg_validated["params"].pop("method") - msg_validated["method"] = method - msg_validated["id"] = str(self.ws_msg_counter) + client_msg_validated = ClientMessage.model_validate({"params": params}) + msg = client_msg_validated.convert_to_server() + msg["id"] = str(self.ws_msg_counter) self.ws_msg_counter += 1 - msg_validated = convert_to_server(msg_validated) - future = asyncio.get_event_loop().create_future() - self.ws_msg_futures[msg_validated["id"]] = future + self.ws_msg_futures[msg["id"]] = future - await self.ws.send(json.dumps(msg_validated)) + await self.ws.send(json.dumps(msg)) # await the response for the sent ws message from the server msg = await future @@ -405,39 +402,3 @@ def sign_bid( ) return opportunity_bid - - -def convert_to_server(msg: dict) -> dict: - """ - Converts the params of a client message to the format expected by the server. - - Args: - msg: The message to convert. - Returns: - The message with the params converted to the format expected by the server. - """ - if msg["method"] == "post_bid": - params = { - "bid": { - "amount": msg["params"]["amount"], - "target_contract": msg["params"]["target_contract"], - "chain_id": msg["params"]["chain_id"], - "target_calldata": msg["params"]["target_calldata"], - "permission_key": msg["params"]["permission_key"], - } - } - msg["params"] = params - elif msg["method"] == "post_opportunity_bid": - params = { - "opportunity_id": msg["params"]["opportunity_id"], - "opportunity_bid": { - "amount": msg["params"]["amount"], - "executor": msg["params"]["executor"], - "permission_key": msg["params"]["permission_key"], - "signature": msg["params"]["signature"], - "valid_until": msg["params"]["valid_until"], - }, - } - msg["params"] = params - - return msg diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_types.py b/express_relay/sdk/python/express_relay_utils/express_relay_types.py index 6705640cab..95c91d9228 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_types.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_types.py @@ -296,3 +296,41 @@ class ClientMessage(BaseModel): PostBidMessageParams, PostOpportunityBidMessageParams, ] = Field(..., discriminator="method") + + def convert_to_server(self) -> dict: + """ + Converts the params of a ClientMessage model dict to the format expected by the server. + + Returns: + The message with the params converted to the format expected by the server. + """ + msg = self.model_dump() + method = msg["params"]["method"] + + if method == "post_bid": + params = { + "bid": { + "amount": msg["params"]["amount"], + "target_contract": msg["params"]["target_contract"], + "chain_id": msg["params"]["chain_id"], + "target_calldata": msg["params"]["target_calldata"], + "permission_key": msg["params"]["permission_key"], + } + } + msg["params"] = params + elif method == "post_opportunity_bid": + params = { + "opportunity_id": msg["params"]["opportunity_id"], + "opportunity_bid": { + "amount": msg["params"]["amount"], + "executor": msg["params"]["executor"], + "permission_key": msg["params"]["permission_key"], + "signature": msg["params"]["signature"], + "valid_until": msg["params"]["valid_until"], + }, + } + msg["params"] = params + + msg["method"] = method + + return msg From 5997608d30c8875ee83c0f7a4597ac3040d47b1f Mon Sep 17 00:00:00 2001 From: ani Date: Thu, 14 Mar 2024 23:56:14 -0400 Subject: [PATCH 54/58] address comments --- express_relay/sdk/python/README.md | 4 +- .../express_relay_client.py | 73 +++++++++---------- .../express_relay_types.py | 52 +++++++++---- 3 files changed, 74 insertions(+), 55 deletions(-) diff --git a/express_relay/sdk/python/README.md b/express_relay/sdk/python/README.md index 48e81434b4..8b7ac88ba4 100644 --- a/express_relay/sdk/python/README.md +++ b/express_relay/sdk/python/README.md @@ -1,6 +1,6 @@ -# Pyth Express Relay Python SDK +# Express Relay Python SDK -Utility library for searchers and protocols to interact with the Pyth Express Relay API. +Utility library for searchers and protocols to interact with the Express Relay API. The SDK includes searcher-side utilities and protocol-side utilities. The searcher-side utilities include a basic Searcher client for connecting to the Express Relay server as well as an example SimpleSearcher class that provides a simple workflow for assessing and bidding on liquidation opportunities. diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index fc320c2331..de0b74bdec 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -37,14 +37,16 @@ def __init__( bid_status_callback: ( Callable[[BidStatusUpdate], Coroutine[Any, Any, Any]] | None ) = None, - **kwargs, + ws_options: dict[str, Any] = {}, + httpx_options: dict[str, Any] = {}, ): """ Args: server_url: The URL of the auction server. opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type Opportunity. bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatusUpdate. - kwargs: Keyword arguments to pass to the websocket connection. + ws_options: Keyword arguments to pass to the websocket connection. + httpx_options: Keyword arguments to pass to the HTTP client. """ parsed_url = urllib.parse.urlparse(server_url) if parsed_url.scheme == "https": @@ -61,7 +63,8 @@ def __init__( self.ws_lock = asyncio.Lock() self.ws_loop: Task[Any] self.ws_msg_futures: dict[str, asyncio.Future] = {} - self.ws_options = kwargs + self.ws_options = ws_options + self.httpx_options = httpx_options self.opportunity_callback = opportunity_callback self.bid_status_callback = bid_status_callback @@ -94,23 +97,19 @@ async def get_ws_loop(self) -> asyncio.Task: return self.ws_loop - async def send_ws_message(self, method: str, params: dict) -> dict: + async def send_ws_message(self, client_msg: ClientMessage) -> dict: """ Sends a message to the server via websocket. Args: - method: The type of message to send. - params: The parameters to send in the message. + client_msg: The message to send. Returns: The result of the response message from the server. """ await self.start_ws() - params.update({"method": method}) - - # validate the format of msg and construct the message dict to send - client_msg_validated = ClientMessage.model_validate({"params": params}) - msg = client_msg_validated.convert_to_server() + # construct the message dict to send to server + msg = client_msg.convert_to_server() msg["id"] = str(self.ws_msg_counter) self.ws_msg_counter += 1 @@ -147,9 +146,11 @@ async def subscribe_chains(self, chain_ids: list[str]): chain_ids: A list of chain IDs to subscribe to. """ params = { + "method": "subscribe", "chain_ids": chain_ids, } - await self.send_ws_message("subscribe", params) + client_msg = ClientMessage.model_validate({"params": params}) + await self.send_ws_message(client_msg) async def unsubscribe_chains(self, chain_ids: list[str]): """ @@ -159,35 +160,35 @@ async def unsubscribe_chains(self, chain_ids: list[str]): chain_ids: A list of chain IDs to unsubscribe from. """ params = { + "method": "unsubscribe", "chain_ids": chain_ids, } - await self.send_ws_message("unsubscribe", params) + client_msg = ClientMessage.model_validate({"params": params}) + await self.send_ws_message(client_msg) - async def submit_bid( - self, bid: Bid, subscribe_to_updates: bool = True, **kwargs - ) -> UUID: + async def submit_bid(self, bid: Bid, subscribe_to_updates: bool = True) -> UUID: """ Submits a bid to the auction server. Args: bid: An object representing the bid to submit. subscribe_to_updates: A boolean indicating whether to subscribe to the bid status updates. - kwargs: Keyword arguments to pass to the HTTP post request. Returns: The ID of the submitted bid. """ bid_dict = bid.model_dump() if subscribe_to_updates: - result = await self.send_ws_message("post_bid", bid_dict) + bid_dict["method"] = "post_bid" + client_msg = ClientMessage.model_validate({"params": bid_dict}) + result = await self.send_ws_message(client_msg) bid_id = UUID(result.get("id")) else: - async with httpx.AsyncClient() as client: + async with httpx.AsyncClient(**self.httpx_options) as client: resp = await client.post( urllib.parse.urlparse(self.server_url) ._replace(path="/v1/bids") .geturl(), json=bid_dict, - **kwargs, ) resp.raise_for_status() @@ -199,7 +200,6 @@ async def submit_opportunity_bid( self, opportunity_bid: OpportunityBid, subscribe_to_updates: bool = True, - **kwargs, ) -> UUID: """ Submits a bid on an opportunity to the server via websocket. @@ -207,26 +207,25 @@ async def submit_opportunity_bid( Args: opportunity_bid: An object representing the bid to submit on an opportunity. subscribe_to_updates: A boolean indicating whether to subscribe to the bid status updates. - kwargs: Keyword arguments to pass to the HTTP post request. Returns: The ID of the submitted bid. """ opportunity_bid_dict = opportunity_bid.model_dump() if subscribe_to_updates: - result = await self.send_ws_message( - "post_opportunity_bid", - { - "opportunity_id": opportunity_bid.opportunity_id, - "amount": opportunity_bid.amount, - "executor": opportunity_bid.executor, - "permission_key": opportunity_bid.permission_key, - "signature": opportunity_bid.signature, - "valid_until": opportunity_bid.valid_until, - }, - ) + params = { + "method": "post_opportunity_bid", + "opportunity_id": opportunity_bid.opportunity_id, + "amount": opportunity_bid.amount, + "executor": opportunity_bid.executor, + "permission_key": opportunity_bid.permission_key, + "signature": opportunity_bid.signature, + "valid_until": opportunity_bid.valid_until, + } + client_msg = ClientMessage.model_validate({"params": params}) + result = await self.send_ws_message(client_msg) bid_id = UUID(result.get("id")) else: - async with httpx.AsyncClient() as client: + async with httpx.AsyncClient(**self.httpx_options) as client: resp = await client.post( urllib.parse.urlparse(self.server_url) ._replace( @@ -234,7 +233,6 @@ async def submit_opportunity_bid( ) .geturl(), json=opportunity_bid_dict, - **kwargs, ) resp.raise_for_status() @@ -267,7 +265,7 @@ async def ws_handler( if msg_json.get("type"): if msg_json.get("type") == "new_opportunity": if opportunity_callback is not None: - opportunity = Opportunity.model_validate( + opportunity = Opportunity.process_opportunity_dict( msg_json.get("opportunity") ) if opportunity: @@ -317,7 +315,8 @@ async def get_opportunities( resp.raise_for_status() opportunities = [ - Opportunity.model_validate(opportunity) for opportunity in resp.json() + Opportunity.process_opportunity_dict(opportunity) + for opportunity in resp.json() ] return opportunities diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_types.py b/express_relay/sdk/python/express_relay_utils/express_relay_types.py index 95c91d9228..7f8d959399 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_types.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_types.py @@ -4,7 +4,7 @@ from pydantic.functional_serializers import PlainSerializer from uuid import UUID import web3 -from typing import Union +from typing import Union, ClassVar from pydantic import Field from typing_extensions import Literal, Annotated import warnings @@ -12,7 +12,7 @@ from eth_account.datastructures import SignedMessage -def maybe_hex_string(s: str): +def check_hex_string(s: str): """ Validates that a string is a valid hex string. @@ -30,14 +30,14 @@ def maybe_hex_string(s: str): return s -def maybe_bytes32(s: str): +def check_bytes32(s: str): """ Validates that a string is a valid 32-byte hex string. Args: s: The string to validate as a 32-byte hex string. Can be '0x'-prefixed. """ - maybe_hex_string(s) + check_hex_string(s) ind = 0 if s.startswith("0x"): ind = 2 @@ -47,7 +47,7 @@ def maybe_bytes32(s: str): return s -def maybe_address(s: str): +def check_address(s: str): """ Validates that a string is a valid Ethereum address. @@ -58,9 +58,9 @@ def maybe_address(s: str): return s -HexString = Annotated[str, AfterValidator(maybe_hex_string)] -Bytes32 = Annotated[str, AfterValidator(maybe_bytes32)] -Address = Annotated[str, AfterValidator(maybe_address)] +HexString = Annotated[str, AfterValidator(check_hex_string)] +Bytes32 = Annotated[str, AfterValidator(check_bytes32)] +Address = Annotated[str, AfterValidator(check_address)] IntString = Annotated[int, PlainSerializer(lambda x: str(x), return_type=str)] UUIDString = Annotated[UUID, PlainSerializer(lambda x: str(x), return_type=str)] @@ -178,9 +178,6 @@ class OpportunityParams(BaseModel): params: Union[OpportunityParamsV1] = Field(..., discriminator="version") - def to_dict(self): - return self.params.to_dict() - class Opportunity(BaseModel): """ @@ -208,15 +205,38 @@ class Opportunity(BaseModel): creation_time: IntString opportunity_id: UUIDString + supported_versions: ClassVar[list[str]] = ["v1"] + unsupported_version_error_text: ClassVar[str] = ( + "Cannot handle opportunity version: {%s}. Please upgrade your client." + ) + @model_validator(mode="after") def check_version(self): - if self.version not in ["v1"]: - warnings.warn( - f"Cannot handle opportunity version: {self.version}. Please upgrade your client." - ) - return None + if self.version not in self.supported_versions: + raise Exception("Unsupported version") return self + @classmethod + def process_opportunity_dict(cls, opportunity_dict: dict): + """ + Processes an opportunity dictionary and converts to a class object. + + Args: + opportunity_dict: The opportunity dictionary to convert. + + Returns: + The opportunity as a class object. + """ + try: + return cls.model_validate(opportunity_dict) + except Exception as e: + if str(e) == "Unsupported version": + warnings.warn( + cls.unsupported_version_error_text % opportunity_dict["version"] + ) + return None + raise + class SubscribeMessageParams(BaseModel): """ From 9b0f395ab447009d08a93af55b0c1ff4352dbe8e Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 15 Mar 2024 11:14:48 -0400 Subject: [PATCH 55/58] chgs --- .../express_relay_client.py | 113 ++++++++++++------ .../express_relay_types.py | 38 ------ express_relay/sdk/python/pyproject.toml | 4 +- 3 files changed, 78 insertions(+), 77 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index de0b74bdec..a816fde51e 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -37,16 +37,18 @@ def __init__( bid_status_callback: ( Callable[[BidStatusUpdate], Coroutine[Any, Any, Any]] | None ) = None, - ws_options: dict[str, Any] = {}, - httpx_options: dict[str, Any] = {}, + timeout_response_secs: int = 10, + ws_options: dict[str, Any] | None = None, + http_options: dict[str, Any] | None = None, ): """ Args: server_url: The URL of the auction server. opportunity_callback: An async function that serves as the callback on a new opportunity. Should take in one external argument of type Opportunity. bid_status_callback: An async function that serves as the callback on a new bid status update. Should take in one external argument of type BidStatusUpdate. + timeout_response_secs: The number of seconds to wait for a response message from the server. ws_options: Keyword arguments to pass to the websocket connection. - httpx_options: Keyword arguments to pass to the HTTP client. + http_options: Keyword arguments to pass to the HTTP client. """ parsed_url = urllib.parse.urlparse(server_url) if parsed_url.scheme == "https": @@ -63,8 +65,13 @@ def __init__( self.ws_lock = asyncio.Lock() self.ws_loop: Task[Any] self.ws_msg_futures: dict[str, asyncio.Future] = {} + self.timeout_response_secs = timeout_response_secs + if ws_options is None: + ws_options = {} self.ws_options = ws_options - self.httpx_options = httpx_options + if http_options is None: + http_options = {} + self.http_options = http_options self.opportunity_callback = opportunity_callback self.bid_status_callback = bid_status_callback @@ -97,7 +104,49 @@ async def get_ws_loop(self) -> asyncio.Task: return self.ws_loop - async def send_ws_message(self, client_msg: ClientMessage) -> dict: + def convert_client_msg_to_server(self, client_msg: ClientMessage) -> dict: + """ + Converts the params of a ClientMessage model dict to the format expected by the server. + + Args: + client_msg: The message to send to the server. + Returns: + The message as a dict with the params converted to the format expected by the server. + """ + msg = client_msg.model_dump() + method = msg["params"]["method"] + msg["id"] = str(self.ws_msg_counter) + self.ws_msg_counter += 1 + + if method == "post_bid": + params = { + "bid": { + "amount": msg["params"]["amount"], + "target_contract": msg["params"]["target_contract"], + "chain_id": msg["params"]["chain_id"], + "target_calldata": msg["params"]["target_calldata"], + "permission_key": msg["params"]["permission_key"], + } + } + msg["params"] = params + elif method == "post_opportunity_bid": + params = { + "opportunity_id": msg["params"]["opportunity_id"], + "opportunity_bid": { + "amount": msg["params"]["amount"], + "executor": msg["params"]["executor"], + "permission_key": msg["params"]["permission_key"], + "signature": msg["params"]["signature"], + "valid_until": msg["params"]["valid_until"], + }, + } + msg["params"] = params + + msg["method"] = method + + return msg + + async def send_ws_msg(self, client_msg: ClientMessage) -> dict: """ Sends a message to the server via websocket. @@ -108,10 +157,7 @@ async def send_ws_message(self, client_msg: ClientMessage) -> dict: """ await self.start_ws() - # construct the message dict to send to server - msg = client_msg.convert_to_server() - msg["id"] = str(self.ws_msg_counter) - self.ws_msg_counter += 1 + msg = self.convert_client_msg_to_server(client_msg) future = asyncio.get_event_loop().create_future() self.ws_msg_futures[msg["id"]] = future @@ -119,9 +165,11 @@ async def send_ws_message(self, client_msg: ClientMessage) -> dict: await self.ws.send(json.dumps(msg)) # await the response for the sent ws message from the server - msg = await future + msg_response = await asyncio.wait_for( + future, timeout=self.timeout_response_secs + ) - return self.process_response_msg(msg) + return self.process_response_msg(msg_response) def process_response_msg(self, msg: dict) -> dict: """ @@ -150,7 +198,7 @@ async def subscribe_chains(self, chain_ids: list[str]): "chain_ids": chain_ids, } client_msg = ClientMessage.model_validate({"params": params}) - await self.send_ws_message(client_msg) + await self.send_ws_msg(client_msg) async def unsubscribe_chains(self, chain_ids: list[str]): """ @@ -164,7 +212,7 @@ async def unsubscribe_chains(self, chain_ids: list[str]): "chain_ids": chain_ids, } client_msg = ClientMessage.model_validate({"params": params}) - await self.send_ws_message(client_msg) + await self.send_ws_msg(client_msg) async def submit_bid(self, bid: Bid, subscribe_to_updates: bool = True) -> UUID: """ @@ -180,10 +228,10 @@ async def submit_bid(self, bid: Bid, subscribe_to_updates: bool = True) -> UUID: if subscribe_to_updates: bid_dict["method"] = "post_bid" client_msg = ClientMessage.model_validate({"params": bid_dict}) - result = await self.send_ws_message(client_msg) + result = await self.send_ws_msg(client_msg) bid_id = UUID(result.get("id")) else: - async with httpx.AsyncClient(**self.httpx_options) as client: + async with httpx.AsyncClient(**self.http_options) as client: resp = await client.post( urllib.parse.urlparse(self.server_url) ._replace(path="/v1/bids") @@ -222,10 +270,10 @@ async def submit_opportunity_bid( "valid_until": opportunity_bid.valid_until, } client_msg = ClientMessage.model_validate({"params": params}) - result = await self.send_ws_message(client_msg) + result = await self.send_ws_msg(client_msg) bid_id = UUID(result.get("id")) else: - async with httpx.AsyncClient(**self.httpx_options) as client: + async with httpx.AsyncClient(**self.http_options) as client: resp = await client.post( urllib.parse.urlparse(self.server_url) ._replace( @@ -266,18 +314,16 @@ async def ws_handler( if msg_json.get("type") == "new_opportunity": if opportunity_callback is not None: opportunity = Opportunity.process_opportunity_dict( - msg_json.get("opportunity") + msg_json["opportunity"] ) if opportunity: asyncio.create_task(opportunity_callback(opportunity)) elif msg_json.get("type") == "bid_status_update": if bid_status_callback is not None: - id = msg_json.get("status").get("id") - bid_status = ( - msg_json.get("status").get("bid_status").get("status") - ) - result = msg_json.get("status").get("bid_status").get("result") + id = msg_json["status"]["id"] + bid_status = msg_json["status"]["bid_status"]["status"] + result = msg_json["status"]["bid_status"].get("result") bid_status_update = BidStatusUpdate( id=id, bid_status=BidStatus(bid_status), result=result ) @@ -287,15 +333,12 @@ async def ws_handler( future = self.ws_msg_futures.pop(msg_json["id"]) future.set_result(msg_json) - async def get_opportunities( - self, chain_id: str | None = None, timeout_secs: int = 10 - ) -> list[Opportunity]: + async def get_opportunities(self, chain_id: str | None = None) -> list[Opportunity]: """ Connects to the server and fetches opportunities. Args: chain_id: The chain ID to fetch opportunities for. If None, fetches opportunities across all chains. - timeout_secs: The timeout for the HTTP request in seconds. Returns: A list of opportunities. """ @@ -309,27 +352,24 @@ async def get_opportunities( ._replace(path="/v1/opportunities") .geturl(), params=params, - timeout=timeout_secs, ) resp.raise_for_status() - opportunities = [ - Opportunity.process_opportunity_dict(opportunity) - for opportunity in resp.json() - ] + opportunities = [] + for opportunity in resp.json(): + opportunity_processed = Opportunity.process_opportunity_dict(opportunity) + if opportunity_processed: + opportunities.append(opportunity_processed) return opportunities - async def submit_opportunity( - self, opportunity: OpportunityParams, timeout_secs: int = 10 - ) -> UUID: + async def submit_opportunity(self, opportunity: OpportunityParams) -> UUID: """ Submits an opportunity to the server. Args: opportunity: An object representing the opportunity to submit. - timeout_secs: The timeout for the HTTP request in seconds. Returns: The ID of the submitted opportunity. """ @@ -339,7 +379,6 @@ async def submit_opportunity( ._replace(path="/v1/opportunities") .geturl(), json=opportunity.params.model_dump(), - timeout=timeout_secs, ) resp.raise_for_status() return UUID(resp.json()["opportunity_id"]) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_types.py b/express_relay/sdk/python/express_relay_utils/express_relay_types.py index 7f8d959399..6016ba69c4 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_types.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_types.py @@ -316,41 +316,3 @@ class ClientMessage(BaseModel): PostBidMessageParams, PostOpportunityBidMessageParams, ] = Field(..., discriminator="method") - - def convert_to_server(self) -> dict: - """ - Converts the params of a ClientMessage model dict to the format expected by the server. - - Returns: - The message with the params converted to the format expected by the server. - """ - msg = self.model_dump() - method = msg["params"]["method"] - - if method == "post_bid": - params = { - "bid": { - "amount": msg["params"]["amount"], - "target_contract": msg["params"]["target_contract"], - "chain_id": msg["params"]["chain_id"], - "target_calldata": msg["params"]["target_calldata"], - "permission_key": msg["params"]["permission_key"], - } - } - msg["params"] = params - elif method == "post_opportunity_bid": - params = { - "opportunity_id": msg["params"]["opportunity_id"], - "opportunity_bid": { - "amount": msg["params"]["amount"], - "executor": msg["params"]["executor"], - "permission_key": msg["params"]["permission_key"], - "signature": msg["params"]["signature"], - "valid_until": msg["params"]["valid_until"], - }, - } - msg["params"] = params - - msg["method"] = method - - return msg diff --git a/express_relay/sdk/python/pyproject.toml b/express_relay/sdk/python/pyproject.toml index 5f4a9febb3..ebe14d17cf 100644 --- a/express_relay/sdk/python/pyproject.toml +++ b/express_relay/sdk/python/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "express-relay-utils" -version = "0.0.1" -description = "Utilities for searchers and protocols to interact with the express relay protocol." +version = "0.1.0" +description = "Utilities for searchers and protocols to interact with the Express Relay protocol." authors = ["dourolabs"] license = "Proprietary" readme = "README.md" From dce3eeeee35aa9db3c7d84fd6692935d6015e8b8 Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 15 Mar 2024 11:17:43 -0400 Subject: [PATCH 56/58] fgt --- .../sdk/python/express_relay_utils/express_relay_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay_utils/express_relay_client.py index a816fde51e..f549d9876a 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay_utils/express_relay_client.py @@ -346,7 +346,7 @@ async def get_opportunities(self, chain_id: str | None = None) -> list[Opportuni if chain_id: params["chain_id"] = chain_id - async with httpx.AsyncClient() as client: + async with httpx.AsyncClient(**self.http_options) as client: resp = await client.get( urllib.parse.urlparse(self.server_url) ._replace(path="/v1/opportunities") @@ -373,7 +373,7 @@ async def submit_opportunity(self, opportunity: OpportunityParams) -> UUID: Returns: The ID of the submitted opportunity. """ - async with httpx.AsyncClient() as client: + async with httpx.AsyncClient(**self.http_options) as client: resp = await client.post( urllib.parse.urlparse(self.server_url) ._replace(path="/v1/opportunities") From b3d35b635a9e4c660721419709ff286cdd83e3a0 Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 15 Mar 2024 14:30:02 -0400 Subject: [PATCH 57/58] morechgs --- .../workflows/pypi-express-relay-utils.yml | 10 +++---- express_relay/sdk/python/README.md | 6 ++--- .../__init__.py | 0 .../express_relay_client.py | 2 +- .../express_relay_types.py | 27 +++++++++---------- .../searcher/examples/simple_searcher.py | 4 +-- express_relay/sdk/python/mypy.ini | 3 --- express_relay/sdk/python/pyproject.toml | 4 +-- 8 files changed, 25 insertions(+), 31 deletions(-) rename express_relay/sdk/python/{express_relay_utils => express_relay}/__init__.py (100%) rename express_relay/sdk/python/{express_relay_utils => express_relay}/express_relay_client.py (99%) rename express_relay/sdk/python/{express_relay_utils => express_relay}/express_relay_types.py (93%) rename express_relay/sdk/python/{express_relay_utils => express_relay}/searcher/examples/simple_searcher.py (97%) diff --git a/.github/workflows/pypi-express-relay-utils.yml b/.github/workflows/pypi-express-relay-utils.yml index 889d74a465..86509a84db 100644 --- a/.github/workflows/pypi-express-relay-utils.yml +++ b/.github/workflows/pypi-express-relay-utils.yml @@ -1,9 +1,9 @@ -name: Upload express-relay-utils Python Package to PyPI +name: Upload express-relay Python Package to PyPI on: push: tags: - - "v*" + - "python-v*" jobs: deploy: @@ -14,8 +14,8 @@ jobs: - name: Install dependencies run: | python3 -m pip install --upgrade poetry - poetry -C express_relay/sdk/python/express_relay_utils build + poetry -C express_relay/sdk/python/express_relay build - name: Build and publish run: | - poetry -C express_relay/sdk/python/express_relay_utils build - poetry -C express_relay/sdk/python/express_relay_utils publish --username __token__ --password ${{ secrets.PYPI_TOKEN }} + poetry -C express_relay/sdk/python/express_relay build + poetry -C express_relay/sdk/python/express_relay publish --username __token__ --password ${{ secrets.PYPI_TOKEN }} diff --git a/express_relay/sdk/python/README.md b/express_relay/sdk/python/README.md index 8b7ac88ba4..d8c6691376 100644 --- a/express_relay/sdk/python/README.md +++ b/express_relay/sdk/python/README.md @@ -11,15 +11,15 @@ The SDK includes searcher-side utilities and protocol-side utilities. The search ### poetry ``` -$ poetry add express-relay-utils +$ poetry add express-relay ``` ## Quickstart -To run the simple searcher script, navigate to `express_relay_utils/` and run +To run the simple searcher script, navigate to `python/` and run ``` -$ python3 -m searcher.examples.simple_searcher --private-key --chain-id development --verbose --server-url https://per-staging.dourolabs.app/ +$ python3 -m express_relay.searcher.examples.simple_searcher --private-key --chain-id development --verbose --server-url https://per-staging.dourolabs.app/ ``` This simple example runs a searcher that queries the Express Relay liquidation server for available liquidation opportunities and naively submits a bid on each available opportunity. diff --git a/express_relay/sdk/python/express_relay_utils/__init__.py b/express_relay/sdk/python/express_relay/__init__.py similarity index 100% rename from express_relay/sdk/python/express_relay_utils/__init__.py rename to express_relay/sdk/python/express_relay/__init__.py diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_client.py b/express_relay/sdk/python/express_relay/express_relay_client.py similarity index 99% rename from express_relay/sdk/python/express_relay_utils/express_relay_client.py rename to express_relay/sdk/python/express_relay/express_relay_client.py index f549d9876a..1e794df025 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_client.py +++ b/express_relay/sdk/python/express_relay/express_relay_client.py @@ -12,7 +12,7 @@ from eth_abi import encode from eth_account.account import Account from web3.auto import w3 -from express_relay_utils.express_relay_types import ( +from express_relay.express_relay_types import ( Opportunity, BidStatusUpdate, ClientMessage, diff --git a/express_relay/sdk/python/express_relay_utils/express_relay_types.py b/express_relay/sdk/python/express_relay/express_relay_types.py similarity index 93% rename from express_relay/sdk/python/express_relay_utils/express_relay_types.py rename to express_relay/sdk/python/express_relay/express_relay_types.py index 6016ba69c4..9150110b94 100644 --- a/express_relay/sdk/python/express_relay_utils/express_relay_types.py +++ b/express_relay/sdk/python/express_relay/express_relay_types.py @@ -11,6 +11,9 @@ import string from eth_account.datastructures import SignedMessage +class UnsupportedOpportunityVersionException(Exception): + pass + def check_hex_string(s: str): """ @@ -206,15 +209,13 @@ class Opportunity(BaseModel): opportunity_id: UUIDString supported_versions: ClassVar[list[str]] = ["v1"] - unsupported_version_error_text: ClassVar[str] = ( - "Cannot handle opportunity version: {%s}. Please upgrade your client." - ) - @model_validator(mode="after") - def check_version(self): - if self.version not in self.supported_versions: - raise Exception("Unsupported version") - return self + @model_validator(mode="before") + @classmethod + def check_version(cls, data): + if data['version'] not in cls.supported_versions: + raise UnsupportedOpportunityVersionException(f"Cannot handle opportunity version: {data['version']}. Please upgrade your client.") + return data @classmethod def process_opportunity_dict(cls, opportunity_dict: dict): @@ -229,13 +230,9 @@ def process_opportunity_dict(cls, opportunity_dict: dict): """ try: return cls.model_validate(opportunity_dict) - except Exception as e: - if str(e) == "Unsupported version": - warnings.warn( - cls.unsupported_version_error_text % opportunity_dict["version"] - ) - return None - raise + except UnsupportedOpportunityVersionException as e: + warnings.warn(str(e)) + return None class SubscribeMessageParams(BaseModel): diff --git a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay/searcher/examples/simple_searcher.py similarity index 97% rename from express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py rename to express_relay/sdk/python/express_relay/searcher/examples/simple_searcher.py index 4f26e80c7a..2152c67414 100644 --- a/express_relay/sdk/python/express_relay_utils/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay/searcher/examples/simple_searcher.py @@ -2,8 +2,8 @@ import asyncio import logging from eth_account.account import Account -from express_relay_utils.express_relay_client import ExpressRelayClient, sign_bid -from express_relay_utils.express_relay_types import ( +from express_relay.express_relay_client import ExpressRelayClient, sign_bid +from express_relay.express_relay_types import ( Opportunity, OpportunityBid, Bytes32, diff --git a/express_relay/sdk/python/mypy.ini b/express_relay/sdk/python/mypy.ini index b789e41879..34b29a5e17 100644 --- a/express_relay/sdk/python/mypy.ini +++ b/express_relay/sdk/python/mypy.ini @@ -8,9 +8,6 @@ disallow_any_generics = True check_untyped_defs = True no_implicit_reexport = True -; # for strict mypy: (this is the tricky one :-)) -; disallow_untyped_defs = False - [pydantic-mypy] init_forbid_extra = True init_typed = True diff --git a/express_relay/sdk/python/pyproject.toml b/express_relay/sdk/python/pyproject.toml index ebe14d17cf..92941c7c78 100644 --- a/express_relay/sdk/python/pyproject.toml +++ b/express_relay/sdk/python/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] -name = "express-relay-utils" -version = "0.1.0" +name = "express-relay" +version = "0.2.0" description = "Utilities for searchers and protocols to interact with the Express Relay protocol." authors = ["dourolabs"] license = "Proprietary" From 6090e3b741d99cb9edbc3d159a52019145334ea9 Mon Sep 17 00:00:00 2001 From: ani Date: Fri, 15 Mar 2024 15:01:26 -0400 Subject: [PATCH 58/58] some more chgs --- .github/workflows/ci-pre-commit.yml | 2 +- .pre-commit-config.yaml | 6 +++--- .../express_relay/{express_relay_client.py => client.py} | 2 +- .../express_relay/searcher/examples/simple_searcher.py | 4 ++-- .../express_relay/{express_relay_types.py => types.py} | 7 +++++-- 5 files changed, 12 insertions(+), 9 deletions(-) rename express_relay/sdk/python/express_relay/{express_relay_client.py => client.py} (99%) rename express_relay/sdk/python/express_relay/{express_relay_types.py => types.py} (97%) diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index a757326e69..ce8c3eab15 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -40,7 +40,7 @@ jobs: path: ~/.cache/pypoetry key: poetry-cache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}-${{ env.POETRY_VERSION }} - name: Install poetry dependencies - run: poetry -C express_relay/sdk/python/express_relay_utils install + run: poetry -C express_relay/sdk/python/express_relay install shell: sh - uses: pre-commit/action@v3.0.0 if: ${{ github.event_name == 'pull_request' }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5932a7f0da..13872e7565 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -114,16 +114,16 @@ repos: # For express relay python files - id: black name: black - entry: poetry -C express_relay/sdk/python/express_relay_utils run black express_relay/sdk/python/express_relay_utils + entry: poetry -C express_relay/sdk/python/express_relay run black express_relay/sdk/python/express_relay pass_filenames: false language: "system" - id: pyflakes name: pyflakes - entry: poetry -C express_relay/sdk/python/express_relay_utils run pyflakes express_relay/sdk/python/express_relay_utils + entry: poetry -C express_relay/sdk/python/express_relay run pyflakes express_relay/sdk/python/express_relay pass_filenames: false language: "system" - id: mypy name: mypy - entry: poetry -C express_relay/sdk/python/express_relay_utils run mypy express_relay/sdk/python/express_relay_utils + entry: poetry -C express_relay/sdk/python/express_relay run mypy express_relay/sdk/python/express_relay pass_filenames: false language: "system" diff --git a/express_relay/sdk/python/express_relay/express_relay_client.py b/express_relay/sdk/python/express_relay/client.py similarity index 99% rename from express_relay/sdk/python/express_relay/express_relay_client.py rename to express_relay/sdk/python/express_relay/client.py index 1e794df025..27151e85ef 100644 --- a/express_relay/sdk/python/express_relay/express_relay_client.py +++ b/express_relay/sdk/python/express_relay/client.py @@ -12,7 +12,7 @@ from eth_abi import encode from eth_account.account import Account from web3.auto import w3 -from express_relay.express_relay_types import ( +from express_relay.types import ( Opportunity, BidStatusUpdate, ClientMessage, diff --git a/express_relay/sdk/python/express_relay/searcher/examples/simple_searcher.py b/express_relay/sdk/python/express_relay/searcher/examples/simple_searcher.py index 2152c67414..813eab45f7 100644 --- a/express_relay/sdk/python/express_relay/searcher/examples/simple_searcher.py +++ b/express_relay/sdk/python/express_relay/searcher/examples/simple_searcher.py @@ -2,8 +2,8 @@ import asyncio import logging from eth_account.account import Account -from express_relay.express_relay_client import ExpressRelayClient, sign_bid -from express_relay.express_relay_types import ( +from express_relay.client import ExpressRelayClient, sign_bid +from express_relay.types import ( Opportunity, OpportunityBid, Bytes32, diff --git a/express_relay/sdk/python/express_relay/express_relay_types.py b/express_relay/sdk/python/express_relay/types.py similarity index 97% rename from express_relay/sdk/python/express_relay/express_relay_types.py rename to express_relay/sdk/python/express_relay/types.py index 9150110b94..3d854778aa 100644 --- a/express_relay/sdk/python/express_relay/express_relay_types.py +++ b/express_relay/sdk/python/express_relay/types.py @@ -11,6 +11,7 @@ import string from eth_account.datastructures import SignedMessage + class UnsupportedOpportunityVersionException(Exception): pass @@ -213,8 +214,10 @@ class Opportunity(BaseModel): @model_validator(mode="before") @classmethod def check_version(cls, data): - if data['version'] not in cls.supported_versions: - raise UnsupportedOpportunityVersionException(f"Cannot handle opportunity version: {data['version']}. Please upgrade your client.") + if data["version"] not in cls.supported_versions: + raise UnsupportedOpportunityVersionException( + f"Cannot handle opportunity version: {data['version']}. Please upgrade your client." + ) return data @classmethod