From 5da6baf4d381b7b1a1849b484dc37859b4b774a6 Mon Sep 17 00:00:00 2001 From: zielvna Date: Thu, 4 Jul 2024 14:15:20 +0200 Subject: [PATCH 01/12] refactor get all pool keys and get all positions --- sdk/contracts/invariant/invariant.json | 10 +- sdk/contracts/invariant/invariant.wasm | Bin 119056 -> 118600 bytes sdk/package.json | 5 +- sdk/src/abis/invariant.ts | 10 +- sdk/src/consts.ts | 2 + sdk/src/invariant.ts | 58 +++++- sdk/src/schema.ts | 2 +- sdk/tests/get-all.test.ts | 183 ++++++++++++++++++ src/contracts/collections/pool_keys.rs | 32 ++- src/contracts/entrypoints.rs | 4 +- ...teraction_with_pool_on_removed_fee_tier.rs | 6 +- src/lib.rs | 8 +- src/test_helpers/entrypoints.rs | 4 +- 13 files changed, 280 insertions(+), 44 deletions(-) create mode 100644 sdk/tests/get-all.test.ts diff --git a/sdk/contracts/invariant/invariant.json b/sdk/contracts/invariant/invariant.json index 8939d7fe..bc539ddb 100644 --- a/sdk/contracts/invariant/invariant.json +++ b/sdk/contracts/invariant/invariant.json @@ -1,6 +1,6 @@ { "source": { - "hash": "0x86f442ed070586bbb8157cda6fdc259b84f953e62727d779806988f81937cd1f", + "hash": "0x8947bd15dbb2c5b5236b7f6d16651ce1f4fedd8661c3710f0ca0efa48a38730e", "language": "ink! 5.0.0", "compiler": "rustc 1.77.0", "build_info": { @@ -1400,9 +1400,9 @@ "label": "size", "type": { "displayName": [ - "u8" + "u16" ], - "type": 4 + "type": 10 } }, { @@ -1417,7 +1417,7 @@ ], "default": false, "docs": [], - "label": "InvariantTrait::get_pools", + "label": "InvariantTrait::get_pool_keys", "mutates": false, "payable": false, "returnType": { @@ -1427,7 +1427,7 @@ ], "type": 92 }, - "selector": "0x6dd2d776" + "selector": "0x57d47dcb" }, { "args": [], diff --git a/sdk/contracts/invariant/invariant.wasm b/sdk/contracts/invariant/invariant.wasm index 98751cd2470b67479ca399f42d9c386d30e871b2..d5872933d80301677af1d4af278517836db51322 100644 GIT binary patch delta 26207 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 Xz~A%JEgkn>h&>s6cKT!Pz?J_Ggg*Xw delta 27095 zcmchg3!EL-UEk--+{fO%_pU}-t+aa2++9h#dRXhkmSoG8ch=}-$H;b^)Oq+3V-jcO zK&-m`@ZmM<#N5 zzjJ2h-d#y{LId$fJ2Pj_ocW*sCj!%1nST6n z-ycaA{P2jEoDL3j{87)JbpM6dB`=$YlIote7x~G|jM?g+Hp#QkRJb~Arb9pS$7t!8 z}wnDo_waV)j!L9H67>#Tm2XO`hxq|AB$3AIGurn*L!=he_h!%*IEMo$dK@u05V ze~j}dYBi&y{a!O_?P@QeskXbllEcc~?J9>IyW3&P@w49UcAMiLaWovi!m-Bje{o#G z@x5kuyN}}sI4mt_ z6E67lPCo2UL(o=VEic-ZpYo@-7;lIAXpl2zn>ilnJbA*O9{0U%!Fj2KpL~Kx4UeAF z#f%*%u0^SzJZk&Jn`l15Y2d|MIGwfQ#{HaLuw=yI(K&ZI%ISG`x{lLB!E|KC4exSP zK*6?PCWzKeMUO|LQ_-PED6Y ziuT!Vr;tO7?ztQ^yNl(Jb3HgIuJfqX9ML(3F>0*o+$!ft#3X&w<5*$3j%SALshC>I zSD!~wxnTBZgW&b(>m$Ihy)*x)><)+ zL}R+xkjqrEjYp%kN112ezm5l@&2xnOxV@UW_dW3y(Ym8?5N))AZ{!NFn88w~2^){b zm1y(Pcre-^Z3xhYxRB(^!ibJ`jp%<$8&)%`Qj(oYuN862ic;3Cei0`JI$-Gw79DMy zl}~EM;Nt?@!x_DmVxJQlH!JZ%UNdsdpY5SRAGSJGo_y67=xdu+EI zkm-0Z5lc8>-eEilkJ=uQdZd!!crYBxj-+q0`!fc;lY-=;nXhz7CGMl!MG}piFs&xe z(Y9}k{Y$NK4IAS$*D$G;v;R_$<|@wSTyapK$E7_M406TUd^9aIXM)>3>awA$NLS`c zLl016vDHMO$Cc8V38@u2GZ>AbIIXDZKsg8LKNRjeXs)0ztDv=1m~dOj@!+hHa)`>)=2{Ot9T5vzG&;Hc&~;YY???V)Q)7p&w^#48SABPo#HM*w|? z9<`#j+Pozf#b{R)j*W}NyU$C$IJd!Qi!iMQhgKIHDm`49+)%3MZ^=Ea$SJoRaynY5 zsBV3`RW$u3bQF&Mlc{L>P3Y*iNk!`jU{xW2pUhP>eJJ@{7bN~bI=UP-AL-RmY0wd4 z+mAUFwdyzPR8)Y{RkLcLW{lFKX49Znua>PXv@A`eW^2(ar)G5PRWqkX>yticUs=%p zbESDLE_LURygl-9`wku?L^jD7&Mvv<=JqmW1z=aKFb3U8o`0rdgF=DZ3k1BWe-`A3 zoM*H#u)RGKBww;UJHnjg^X^>AE$AuYVF8lG5+ICa|?*hD{)njhqyj(%AkENk^K<4 ztYBngPn5bCz6Ym14!r>pAe>y9GwpYoJ|4lwe`kBLgwZ_KOMWZw0Nefx{PucMhChC$ zay6Lzm1infdLF!lf5{hZA6WM5&!jdz3zJ`S=~+|qQo>eGnyrNeokK+g^p!3LiGUz; z%`K3*1TO#JL!D|ex;v^SW4mcd&RB`@E<`8!S4%}mn1NcrC^RCSs}kpWX1A%B9f?t{ z6s6>)I~rs#X~EFpLh7yLDWtC4>_3)LpXs`Q|87}pm2`-=E8!~iFS$&{)owEWl7bP1 z)jpK`WtS7zqf3Jf*Y)fl!5I|!6nAt%USu_geyEkrm<|QWf;jCfQwDVQx5+pcz|HrR z)PK55y#)txt$YyrxKj#tzDa#8=SmKN|^j1rlKcYd@?^POfQ`lzH0hW<{gUvy3tJ$72l?_&t<%oN6A#N#=HN`yB{t2#KH1`*XjiQr1l@~R>!LN=IBeqRXW)?K{CX5FvENz1LYD0cL+ zi>b?>I(A(uPPh4q+UJeE6# zxCQ864)d9)RHVn<%x51x{vL($sN-10c-fA1t@m}bWMi)zLeB}}agbw5-Cs8Ek?Seb zuaWHhYt4#OMY6oI_2-sM`j3N3WU^_RrKgFvyTkWEgYM3r=`J&uYuq*HOx3WrXvU(u#DQdQgV4}&JiA&1r zaNVL#k`i=mK{s2;Qb;H(n}3XzyV1{fOJN^Z{+UyUvRT)pb*yr{r!hKW?#EO$)gbkl}Tpj?WI{g&CR?i zsesD=qSM&bZZso15>+=GL%2Y3{3|-m^sQ4>9^`iQ^g_QRE4$2 zm(HoF22Ynje-?&#Dzx>Oa8I2zFSD+oa1_LqR#P%ewb1bErL@m?g?LqtbqA@tUxZ2W zp`|MQ|AfFeEE@w~4i1x*Ex*t;R54e|FL%jQL9ku#)wNzt<3=|gI<>KNSzPC`CGMMwD1-6KHl4=7FQAo0W+?C|h-hwMM zNX03S6s7sgpw=Zo>#{MJ)>-0-Hq5t<0r`Uz{6~&5OuZVe*)kTSy^ea?tQ(R|@|Q zG|Ceu>RR(X*ehsDYO}d-HDkK0wyYL7)$;;i0v6_|_~+6R!tE|1<+)~RY;zYQzTL^@ zG6<>`1l0?AJ}Y`ueM(D+EW;hG09jSJT*k7ncgSy)bNs4MR~mNdA-U5{f|5|xW4*td z3AHNn5#5J-oV9%PKh1&^wVz9@NPY8aPk33R9ts}M+d#sG_7drk1pw3LPy({`h$p^|L}p3wTki(lL}W#^ZEcY#Tv+7tlplJX+qDd}mm%IfJ}(e%{~aKVjuQ=AZMonosDw zw$-1}anm-+)YP7%E!%=4YFMz=@?DY$ha?Ifo6(=H2>CTAKhuz2SK>}L%+9UC_BB=bgnazH|8I& zcB=bW4d?V&mH3Kv@u+H7e2!J4N@V{{cPdd38ZsUG$gyfC_|V;3mn;c(LsWEFsmT;~uDP_WT&ilhfXG zwrA1|-&0`^fNI*7^6}u#cmwOGHF{7sPs~AzL1Yta!f-hwws)8(c{tjzjoLa#{NQ07 z$O@fa~GMME0@jbZ3aV2Co|rj#UM9D`-B=LI7M&PI7x$ zZbqI=1g3k*scK@MI;l&%A!0dRmIp0G8>^{B+v%chZP_*+oa$rY z!Bdx0eGyYd+uq?OOJdczjp!Aspmkv$6=?ADT&L`AD9JiU!y?^){K2ACqnxX63(ktC zgwLIRoEyu=Zp{tm(bS+R>^7aCN%=#$jOax*+yA)bE4&m2$qIgVj<2;4s%hpXd0^f@`6jpnM);Fl9MTEpOdun z>t$S@mz*1UgFlr$`%x38!_iQ9_tc=QW(-_PYJ1oh(ogRb{l)ySpX7LXg~=qx$}5pK zn_n^6F1NdEUKCx5wZp&?oVa~C%ZS*C7lqN74m}gkr+o2beK+Uc*>>k?vd?_fj0DUp z+qBEPDeYs!f@WuJ!pJ-%6RbA8eph=HMvR_mvB)cJ%4)TR2HQ`&dxRlYe)#}FcztCO zj2uaf7SG+_15vxz;8xRo*54&5V|hKcC3_2L!`?deTpZ3eMVV~VDG$8mS_W6m_U$%N z3e=+bta++kCKvRWm`u(@VL6^mu9;u)U~;-2{pe~aX4utW!$nuaql$g(Ts>k&JbA7@ zZEY(%x7#dS)us4YV>(xJs|8$Dvn6}XhSUO6Jvl^T7%$bUttl2_FLO`Pp#m*8HKyscSqjUj6hW}h?a&A_>&G+Zncmi zq!8*OrIOQut&IZ zl3+ZXMqnLT4o)-usPWP$40lJ~X79N^mervzV4`aLNfc?b_h?Ov?Qam%j-(Z$wB&3K z*GLqsSQ<6wKJL`m-MDxNyh{x|v13fNgub|z{?16W;SS#50HU)Yy7G>QKew%jMxxDc zKy6gYA1oNnkT^_%-IAuPA!p*kEZpH_)h_{n$Qp_b!a?^DtVz^E5|U{2^Tf zn5XjVV;rTNZ~}9XGHQG#dPTG`G0_<6c-2kjW^+e2bCY>%dXE@^RY>Wie@IBDQaU84 zQ`CoGPLkKusund6I2{D8E0beV=*S7fs+d%26@0X>Fa6KxROj~gHX6}S{>psb8Ldjy zPjl`F7$7q|;9k?Q{kKPUATgt}MjaaSI$Wax@gPGS&$D@D$v7(w76}O4tbaL2>$0*{ zo=dLzEUhbOy}@-pMzc@zOa+f!c0Nw)0$LBzy0C1OTkE6sFs+NSWv?@9(>Pk%gA^9R z25IW?g~G26O+CJFsnzKdvXKi6)gP@YoZ#82I3&loI60{}MmT#657&0yfO}jF6mIfl z%2hM@{GUDbw}jW2MRJ0MuyBpXxu|ztV_djKEUr^?7b|(@Fqd636TS)`k8mu!NQdiL zuDbyY&R1%eU3ZHae9hu$Rrof1CMGcE&l>s6yQ7$Ay2;5PS2GsB#P&}1$Sr1NI)r<6 zLK}_>K`oCY94i~t!8=1*anj&`a~84ZGQYhbDg@IPE$V&b0*}lZ>T!;R%fal;8gIb) z)h@fXM>(f1=26L=f-gkxR%p;p7{2KiGxBCT>te>c$wKayL17s-5KXq*u!a^~YmU4) zxZJFAyp(0-u29UAX@FQ8vfuko^V%CQL~DH`k%>mbBSx<}S8u;wMvpPuo8h~F70x_? zjOTQ$v+j;)9fRDs-(0t4T@#ONJ+2H_jwrbxy<)V2w8C;3!DK(X-`u!TLott>*74v( zwF?1R`&Kjj_HjmK%cR8*Mz!0?oIMHW1Dd+-Q)ohiHHnT3=^_Oo9U%NXj;Tur$rf}e zFo`xKKz#P2x0>sQ*J*;stMRB}6YX#Hl9vFtzU(Wvnib;$N(K5h5OL3vF0tW_hmKX9 zlTw&p$wqE7_nS&~=r*&f|4kZ2reDdvc$?X8?#$NTZbs6sd*SI>mTk|~ndqCEY?Yok z(5a4+B{4B3!;_TU?BXV7SA_w16K06!+G}>?6noeb?2r%FM17i5G7Iu-6I<73}MA17?j-s)HU$;goPyF_pguCJ1C zzoom0cdbXRHRf0FDij|fZOre3VM`;ki{REI$IXMW2`1T)Z10ETQ9#=?-F469nl00` zgORi8^%~rD{_bYP`eqA$Ji?~%%B0t9-7D@sDH-v$WDIp9+;=?K>L0P8BPyX3;Ms=a z7*0;=q|WT61KDR^&yr%zQt{w4yS}WE#ERKL(^&oj^^D~ffHHQ6+1N1_x`SS^V*STz z)A0~nR~9oZ?xFw#C}1Bby)s(fi{WS< zD0Km(jFx8~zr)1i@^JFL3(IKcB<4)HE3d{l*Rv>Enf=8bX3gFu08&jl$Zf)omC+i8 z?=;KKWVD@nv|5c_wOnK0dPHws?FeXO3*Ts#rh^J?wnG8FBU+YR<>vXD5YQhDk@j~& zc6LZ~u#(0`w6gmmCfAP2Fg6;Z5U|5MXDxF)I8q_N$j>h{m~jv}%0E?5&b;ayMQcGj z)f7t`FN++(XEZ=ga3#C-4QA=i0TB$EcZvNBR1$C&bmnwWok7}~Kvd63W*1%d_BT=t zSONo&+tn7J@l*B4?5?Er{#1QBUV^Pe2Re&KJs-=Ez1Ay}tk*1I4Dxit%1*=sQT^r> zoUs^w%L;bw9I8A>1)`OF?Y%FVeM#^zm59m6--S6XmQNkV2srEGEMj9=s{UL3m7Fsy zR>xD~d^QdnLZ4RvVrxd zhzg%RK!5p{s?_Ol1BkKj9=De6UUjk>uNS?%(mGie;ww=C%`wmY3~_2LfYK}gg1-Zz_7*_!)IUrPP<7_3)y?3Zr_AV*$Iw#0lgaKun-hM1x#M`>x`m&yv%qXOh zPKNR!ds}u)Z&rWZ5kg{>)E0L@0EqsSRssEhP7llS{XXY-mGSEqM zmsxGrWjEYqK4?t#yLX`_H{4?;vXyt6#wsL|9B;@FM3r83qP(hPQ+J!I?lVy>qyoV% z*oHpgvSw&a$uURqc|7XoiG8_7Yj^I(tzy8XZtQPH>!Wch9k*p??l$|@XhtNVMIuHu zvQQ~%kmSFN9b*ErhxAX!!<07A+@qgqCAE&c!ll z>4lG6e3q(|DvPAFHX4{OtZx=OE`|;}ci9`vQ+#JLp6JSGf+R`q=XjrLNa4SNLD?7Y zGrJa}h^5xAbp(xM6W;|cDl!wpELZ?2T!#Nrj6GIu-)!SH$57>45np|fYSR0Hpan>} zMDN0ZjEfH2!+JzYC7S;RJ$bzdCmI#ovoXCNG==q zW!vvJn}>LZJgEj;*V^0kC!uOX_Wk#ptpjsz?aO|Z?)T4W+pBT0S^i>@*dJ#$Gi>js z>9iYleQQ_K32c4qJ1}B`D{YSg^hd(tCkMRB=+yC8Ee^3oqId6MY3*@#@ zP};O*KZ1SX-d*ij2{=oA@U#s z%cSyfCzz&A7$jkOfL*1KNscpOE8kk?Fx@J=?Ebh5#BkaIKo< zseZ>xLZyv!7&KxT#Dtk?>xM#1by`1Yc}P)O8v?(NY<7xo)XFZ*K>&+6WnX){S+-Oc zpFxfY40S8Oek_aMp*Vn+->go*mYY_S^XN*!gpID0ohq{WG7W35H=5@kak_q|Jyl@g z9K3=o8&GW6WH-# zywhySe&7M~;1U}xz*D5rjI~B^1iYxJ>lHZOYaTFD+4sEDJh5Ube$Y7bC~+Sli=+&LCTNz--=y0LRT|fcFIS z5K~xfU&n%G@yKaJWHr?;`_co#Y{drLmaq*3|3?h%s z4v&~^4RHl1aI*$+2stH^4#EZ#I$`*5VVH9qk&!Mb^f~jDhOYn));`)e}D#LHu3_nmR2WyjxeK#6JhL7MRJTiQW zYc|8LQg3+>+o~o%f8>dp&F}+%GO8)VZ0(Pj8LLHKp$6Vdg7!D1nNiDA)ff;tqE3UX2VU-%8;M3L!t35Ts&E8u(K@xze-O^x? z_^KGPJ46*jexpGZLpBIeRiJ(sZe%Ba9|D3WQpVHe3r=FrLh=6;`g7D0Q zZ}(J8J|0VD>hQrOi|a(Y{v3;4Q51xl0Nv=s;rlCSELpJ@;<=$8g?d%Ewl8)l2y zi%*`S3Eo7;-i?ASB;{r}3a<3DnkmT-PlwB*K&}ZJcdOq4B&gytGQ~7kM5Bj}Cgvge zKD42ay)K<_fAS@BGu~GvOkt9V+FW+cTHtTZdd6&%?Q?%0O!ShU$3*Lx;sQ*O{YGp| z(jw|e_VQ1#yI_F*Fyz7^B}yt*)ehKT_YXDqwZB8BWZ*aeY1e@cD>`IhRg{l^%GL(h zKZH`cex1rblxo5LgFM(wp8uta`}}r~#?d0-ld@AZ5PMZ602pU~YKl3!-7oao?BNV~ zo5e)zCiz`g9UbFYjba1NO1G6s=JtoDiw9hRtuf#H2H-PGQkOKpS_;C-{%Xm`e&Nqr zrTsN!e~r@q8qWS2%!}@_Kju)_A2*%-HC%y>5|}kVj?Vmag2_-^mH7=g^J_TsOJVWz z`iz+4Y2Vr(Z-^GiCsb7(6^^)>7xQ*q1uxh!LN_|i<=SSGRkKNSC*L%+yxjQ^H(<^T zzpc`biV420?N*}xRK(k&_$YSW7JS-}DPX6>WNgr`8f`d@tD^o=xi+CODuA&E3~}}I zdjNc4-Jcvo2u+UE)F?^yC&Vgo9i3w)vgZC|I|V>o_bxjrIMP2Jq}x^Q=pXZ6G)SRe z+ZN#v*XIoNAf)vv0^c^!d<3+(#qPc~6#)XuALkH~ip<>)Y80N0 zC(l!pj6w(g@+^cRJIo=5;rZ=yDB3uN*U&bThIBemOJDdM66{sUpS4)sXm+Pi+0WkU zAcfq@KA^<;g~}}wk1q)^hBf;h!kLmKU8?vaWN-!LmhQ^8Ot<`I+NC}0rM)8Bhv(Cd zCOHA@hkfF`Fo^X>+bYtr7YNVo#Rf)=uDvu+C+vl}S(nekWp&i*-^Edh7LG1&HQX(Y zhVveyk&-KdRsvkXNjsx4cvs3CtdUq_Af_S=RMOirY0nzwJVUUBLb3v@{E0U8sL)?v zH;&j#9<|Yyvf+gCAT(nap2Ri-#+M35sn;Y=)NXEXj10O#h3w%t0NGhDj?)U1T8GQ_ zz8WI{dp30I)D!CMx<@RIVu7(Xat32@RVVu}L@A`+&5wBS1{VkuF30U&M{K>5i-X$? z-x+W#T@sz*U4yjC?c6Yfx-i(HBZH!w zHVPJ+X`PE>j~m9!qo%+U08kWsyTAf~VG=Jjct6LPe$p+G6KP3Bo4d7+Mc~2*p7J4x zQLcq&TyN`sEz1e%i6XP1?^hvaszbGVbG}gTLd#1m)CU%;gr8c0m%9hmdKT&f2}j8s zdv_yaI|+tDYU(k{Z`b9W18}c!YwyXkX|c*4pazf3gxgq|SAXK*<%&Fe!{8R0)<$Tr}wN zIkqqi=I}^CLm6~5$Q(OCit)L=6Vp84ie)UOMH@`lFE4A-t!8k|V z{7sFh7#)KxN0fH~Zc&v888GA+d>SP&ydj+ByFDAir)EWh-=M+f@%!4>VQ2lA4A*=# zj2B@+1RJbbHQ7X_na@8gY3RPZ!IbR03KGPJ3jzQT)J_g^FeK43`~ydMvA1i?4_EMN zi{~nR*sYCy$+GT(i`I(zl7-y{ZEoyKsF>zUGHhu1%O5mvGxh9sKVkF@ii)Zu0G|#{5lcC+yVGjp%?%Q z6n^CcRJ#LI?ErFE_3&Ogy#O#?OiIs4SPmR z?!!Ekz3jkmbDAeG1PS3DVdkEPbjwQ>uMj#EGm}7x!_U z((ht`SJCg3sq~@WYa@{NmBzV9yRogg+TDkCja1Xc5M(F#*V~W9O7_m^b zLm`MVH8CFhOvDGeF~W?*7El!8yDW5?s5?Z0+I+^UZH*M_8q)Z7xLOM`;25Xu>!l)F zvzE%Gl`#~z^*z>@=3viYRLvG;i!M-LDS3&M=}KZ$_$pe^5`D!Mxh&N2=@9RU6iT_p zd%L~h0b05nKcmRr<8c`>8(Q*xED}Je9|PfC(TKjUYweT}FSAS4fj;-a(+GXjKGkj6 zH{NT8uTh+3_u8UqiHEyr#(~q)3R-%pjgn3cs@ZoQVo{(nr|L-x0nHe#OSiRKk{taW zwT861ggbADXc*JRg})`35C+o$mw|viiE!Kcgddb-ZdNlIB}z zFU|m+NZ3a@L2mp=zUtR4oCLkuVQ-Zh0ZRZyc|?U3iYC-ZB(FII5;h7)Z&Pta{cRW0 zbh>P})s`T<`BGJGSE`vOka>yYq&UdBj5xS1`|}?$>)Gz|?)RDDZ2ga#H3M^%Fgy2< z*>sW4sVI#W7iAq(;GQ|uRqADXQ0fE?CX+s-iMADQe>>5z9 zBjr}Rc`{#I$W1C6e}Z+MAiSO4Jau{fYHe*Yq*gHSV*(lK$@(1&7Q!$4>YXUv;_rYf5_QOK5IL%wpXS$SiIhlqk`c(PxGrXT8drImPYO1bS`(h zYf4VO)$G~#qbl>+UCr68nm=ikSXZ)}>WG$HA9VE_$B&ihI4DVNK)1;6Rc7L`#Uj~t zS#c>&J8^~^=VIqPPTK`ssj>2qdiMASOq~6P513Uoc*_g3=RaW9c3Kmx1iDO7X{OC8 zW5chO5XOwUFy@Y@%&uR4;DhG%W@IhQud+FmA9u7kDHIsjkH(c(>$5K(HY+F0)JoEP zMRY2KSbjUeti|S1?#gQK9rL{tglf+}Dz$tL7rq6ASeN~$A2XXTWh?KRFT7W?6_(bh zBFRpiSgm|)(YiF80&OKhGBwPGaw~o`u4W(nakhb%hVtrmvQA7TuR=4lX(KTNlrQ`^ z#hFeNP`0TQ>Ay-Z^$ zxR&jOEa2Z#SGCktFQTqChdRa6$DjjRYB;D*>NKzwlUYu;=$8trZhe-C(V~go%=GBv zrbxh5!@;SioGTSSR)9&Yvi^^lC22v#3U%&D%j!81q-C(ECoR(tV%fBeEdwqsBkFK- zAStu&R8l4a5Tf!rJZwRLt%kbA1OxA;yf@|yT9q?MKd!=5^#cqtpG*00=SA3H71+%= zVAPZ!uhY5Rb0a{l|=xLTWG4ujOo$BRu4E$J+CzdqnkO4hh|YBy}X;pS|~LGkWLd9^58{ZVE;7!|DQFTbGVC!GrRVeZ}q z640|%zB$P`({`I+2i!*I9zNt8=$U&y>-;Au_&>`$Oc|PMN5W@F7q^Kl}1XkGmq)4zG*mamtUDMN8&n@PW(VhocG0 z-7-?1{z9I*^DXe))LkE*@Zr-CjOWvO zNs8rbo4P-#eO%0EwjvNj@Y<)ODayiUIYzGV@}y2NY%H98OiY}#eTcMu8Gl#P`&0@> z4wY&9F#P4RNZW_XwB4TBv|Vacbm8rmiYH0im8j=*fK*ki+D092kF?&bt-Ta;D4g?I zw&7Qkt>Wni5igTFiae;xcT6kLbhaeDv3Njkc)ZGw_<0(Ou22@Q=QM|bQBI2lDJNhA z2dmSQz!y6aV_Nt`izEh;RJXOS6(x4R5>aRX#e@~g}FP}8Guc?zv)Qe=I zK0m6+!(Eo`=42N|xLf1VONP6A&}GBjT5^_JfI4+5d~aDx4x{B2ateM@Al%6HG4#xx z@eMka{(x`6dc$F&=%}7v90@v#&;X4+FYzEHQ{rLous@4EZLN;d?fkP%`S446h_Zm^ z#LM3t&a?ZUGLsn4zkkY1a|l0fh6m { + const [positions, poolKeysCount] = await this.getPositions( + owner, + POSITIONS_ENTRIES_LIMIT, + 0n, + options + ) + + const promises: Promise<[[Position, Pool, Tick, Tick][], bigint]>[] = [] + for (let i = 1; i < Math.ceil(Number(poolKeysCount) / Number(POSITIONS_ENTRIES_LIMIT)); i++) { + promises.push( + this.getPositions( + owner, + POSITIONS_ENTRIES_LIMIT, + BigInt(i) * POSITIONS_ENTRIES_LIMIT, + options + ) + ) + } + + const positionsEntries = await Promise.all(promises) + return [...positions, ...positionsEntries.map(([positions]) => positions).flat(1)] + } + + async _getAllPositions( + owner: string, + options: ContractOptions = { + storageDepositLimit: this.storageDepositLimit, + refTime: this.gasLimit.refTime.toNumber(), + proofSize: this.gasLimit.proofSize.toNumber() + } ): Promise { return sendQuery( this.contract, @@ -855,7 +888,7 @@ export class Invariant { proofSize: options.proofSize }) as WeightV2, options.storageDepositLimit, - InvariantQuery.GetPools, + InvariantQuery.GetPoolKeys, [size, offset] ) if (result.ok) { @@ -865,6 +898,27 @@ export class Invariant { } } + async getAllPoolKeys( + options: ContractOptions = { + storageDepositLimit: this.storageDepositLimit, + refTime: this.gasLimit.refTime.toNumber(), + proofSize: this.gasLimit.proofSize.toNumber() + } + ): Promise { + const [poolKeys, poolKeysCount] = await this.getPoolKeys(MAX_POOL_KEYS_RETURNED, 0n, options) + + const promises: Promise<[PoolKey[], bigint]>[] = [] + for (let i = 1; i < Math.ceil(Number(poolKeysCount) / Number(MAX_POOL_KEYS_RETURNED)); i++) { + promises.push( + this.getPoolKeys(MAX_POOL_KEYS_RETURNED, BigInt(i) * MAX_POOL_KEYS_RETURNED, options) + ) + } + + const poolKeysEntries = await Promise.all(promises) + console.log(poolKeysEntries) + return [...poolKeys, ...poolKeysEntries.map(([poolKeys]) => poolKeys).flat(1)] + } + createPoolTx( poolKey: PoolKey, initSqrtPrice: SqrtPrice, diff --git a/sdk/src/schema.ts b/sdk/src/schema.ts index 16f59ecf..10c8c5c5 100644 --- a/sdk/src/schema.ts +++ b/sdk/src/schema.ts @@ -7,7 +7,7 @@ export enum InvariantQuery { GetFeeTiers = `${invariantActionPrefix}getFeeTiers`, FeeTierExist = `${invariantActionPrefix}feeTierExist`, GetPool = `${invariantActionPrefix}getPool`, - GetPools = `${invariantActionPrefix}getPools`, + GetPoolKeys = `${invariantActionPrefix}getPoolKeys`, GetTick = `${invariantActionPrefix}getTick`, IsTickInitialized = `${invariantActionPrefix}isTickInitialized`, GetPosition = `${invariantActionPrefix}getPosition`, diff --git a/sdk/tests/get-all.test.ts b/sdk/tests/get-all.test.ts new file mode 100644 index 00000000..de7c709a --- /dev/null +++ b/sdk/tests/get-all.test.ts @@ -0,0 +1,183 @@ +import { PoolKey, toPercentage } from '@invariant-labs/a0-sdk-wasm/invariant_a0_wasm.js' +import { Keyring } from '@polkadot/api' +import { Invariant } from '../src/invariant' +import { Network } from '../src/network' +import { PSP22 } from '../src/psp22' +import { initPolkadotApi, newFeeTier, newPoolKey } from '../src/utils' +import { assert } from 'chai' +import { describe, it } from 'mocha' +import { SQRT_PRICE_DENOMINATOR } from '../src/consts' + +const api = await initPolkadotApi(Network.Local) + +const keyring = new Keyring({ type: 'sr25519' }) +const account = await keyring.addFromUri('//Alice') + +let invariant = await Invariant.deploy(api, Network.Local, account, toPercentage(1n, 2n)) +let token0Address = await PSP22.deploy(api, account, 1000000000000n, 'Coin', 'COIN', 0n) +let token1Address = await PSP22.deploy(api, account, 1000000000000n, 'Coin', 'COIN', 0n) +let token2Address = await PSP22.deploy(api, account, 1000000000000n, 'Coin', 'COIN', 0n) +const psp22 = await PSP22.load(api, Network.Local) + +const feeTier = newFeeTier(6000000000n, 10n) + +let poolKey = newPoolKey(token0Address, token1Address, feeTier) + +describe('get-all', async () => { + beforeEach(async () => { + invariant = await Invariant.deploy(api, Network.Local, account, toPercentage(1n, 2n)) + token0Address = await PSP22.deploy(api, account, 1000000000000n, 'Coin', 'COIN', 0n) + token1Address = await PSP22.deploy(api, account, 1000000000000n, 'Coin', 'COIN', 0n) + token2Address = await PSP22.deploy(api, account, 1000000000000n, 'Coin', 'COIN', 0n) + + poolKey = newPoolKey(token0Address, token1Address, feeTier) + + await psp22.approve(account, invariant.contract.address.toString(), 10000000000n, token0Address) + await psp22.approve(account, invariant.contract.address.toString(), 10000000000n, token1Address) + await psp22.approve(account, invariant.contract.address.toString(), 10000000000n, token2Address) + }) + + it('get all pool keys works', async function () { + this.timeout(300000) + + const feeTiers = Array.from(Array(10).keys()).map(i => newFeeTier(BigInt(i + 1), BigInt(i + 1))) + const expectedPoolKeys: PoolKey[] = [] + for (const feeTier of feeTiers) { + await invariant.addFeeTier(account, feeTier) + + const poolKey = newPoolKey(token0Address, token1Address, feeTier) + expectedPoolKeys.push(poolKey) + await invariant.createPool(account, poolKey, SQRT_PRICE_DENOMINATOR) + } + + const poolKeys = await invariant.getAllPoolKeys() + assert.equal(poolKeys.length, 10) + + poolKeys.map((poolKey, index) => { + assert.deepEqual(poolKey, expectedPoolKeys[index]) + }) + }) + + it('get all pool keys above single query limit works', async function () { + this.timeout(300000) + + const feeTiers = Array.from(Array(100).keys()).map(i => + newFeeTier(BigInt(i + 1), BigInt(i + 1)) + ) + const expectedPoolKeys: PoolKey[] = [] + for (const feeTier of feeTiers) { + await invariant.addFeeTier(account, feeTier) + + let poolKey = newPoolKey(token0Address, token1Address, feeTier) + expectedPoolKeys.push(poolKey) + await invariant.createPool(account, poolKey, SQRT_PRICE_DENOMINATOR) + poolKey = newPoolKey(token0Address, token2Address, feeTier) + expectedPoolKeys.push(poolKey) + await invariant.createPool(account, poolKey, SQRT_PRICE_DENOMINATOR) + poolKey = newPoolKey(token1Address, token2Address, feeTier) + expectedPoolKeys.push(poolKey) + await invariant.createPool(account, poolKey, SQRT_PRICE_DENOMINATOR) + } + + const poolKeys = await invariant.getAllPoolKeys() + assert.equal(poolKeys.length, 300) + + poolKeys.map((poolKey, index) => { + assert.deepEqual(poolKey, expectedPoolKeys[index]) + }) + }) + + it('get all positions works', async function () { + this.timeout(300000) + + await invariant.addFeeTier(account, feeTier) + await invariant.createPool( + account, + newPoolKey(token0Address, token1Address, feeTier), + SQRT_PRICE_DENOMINATOR + ) + for (let i = 0; i < 10; i++) { + await invariant.createPosition( + account, + poolKey, + -10n, + 10n, + 1000000n, + SQRT_PRICE_DENOMINATOR, + 0n + ) + } + + const positionsEntries = await invariant.getAllPositions(account.address) + assert.equal(positionsEntries.length, 10) + + for (const [index, [position, pool, lowerTick, upperTick]] of positionsEntries.entries()) { + const expectedPosition = await invariant.getPosition(account.address, BigInt(index)) + const expectedPool = await invariant.getPool( + expectedPosition.poolKey.tokenX, + expectedPosition.poolKey.tokenY, + expectedPosition.poolKey.feeTier + ) + const expectedLowerTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.lowerTickIndex + ) + const expectedUpperTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.upperTickIndex + ) + + assert.deepEqual(position, expectedPosition) + assert.deepEqual(pool, expectedPool) + assert.deepEqual(lowerTick, expectedLowerTick) + assert.deepEqual(upperTick, expectedUpperTick) + } + }) + + it('get all positions above single query limit works', async function () { + this.timeout(300000) + + await invariant.addFeeTier(account, feeTier) + await invariant.createPool( + account, + newPoolKey(token0Address, token1Address, feeTier), + SQRT_PRICE_DENOMINATOR + ) + for (let i = 0; i < 50; i++) { + await invariant.createPosition( + account, + poolKey, + -10n, + 10n, + 1000000n, + SQRT_PRICE_DENOMINATOR, + 0n + ) + } + + const positionsEntries = await invariant.getAllPositions(account.address) + assert.equal(positionsEntries.length, 50) + + for (const [index, [position, pool, lowerTick, upperTick]] of positionsEntries.entries()) { + const expectedPosition = await invariant.getPosition(account.address, BigInt(index)) + const expectedPool = await invariant.getPool( + expectedPosition.poolKey.tokenX, + expectedPosition.poolKey.tokenY, + expectedPosition.poolKey.feeTier + ) + const expectedLowerTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.lowerTickIndex + ) + const expectedUpperTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.upperTickIndex + ) + + assert.deepEqual(position, expectedPosition) + assert.deepEqual(pool, expectedPool) + assert.deepEqual(lowerTick, expectedLowerTick) + assert.deepEqual(upperTick, expectedUpperTick) + } + }) +}) diff --git a/src/contracts/collections/pool_keys.rs b/src/contracts/collections/pool_keys.rs index 3f1c8f1c..f3e72b0d 100644 --- a/src/contracts/collections/pool_keys.rs +++ b/src/contracts/collections/pool_keys.rs @@ -2,11 +2,6 @@ use crate::contracts::{InvariantError, PoolKey}; use alloc::vec::Vec; use ink::storage::Mapping; -// size of PoolKey = 32+32+8+2 = 74 bytes, max size of ink! storage cell is 16384 bytes -// that is 16384 - 32 (first 32B is the size of vec) = 16352 bytes left for memory -// 16352 / 74 = 220 elements. Adding 221st element will panic because of not enough memory in the storage cell -const MAX_POOL_KEYS_RETURNED: u8 = 220; - #[ink::storage_item] #[derive(Debug, Default)] pub struct PoolKeys { @@ -62,21 +57,18 @@ impl PoolKeys { self.pool_keys.get(pool_key).is_some() } - pub fn get_all(&self, size: u8, offset: u16) -> Result, InvariantError> { - if size > MAX_POOL_KEYS_RETURNED || offset > self.pool_keys_length { - return Err(InvariantError::InvalidSize); - } + pub fn get_all(&self, size: u16, offset: u16) -> Vec { + let offset_with_size = offset.checked_add(size).unwrap(); - let mut pool_keys = Vec::new(); - for i in offset..self.pool_keys_length { - if size as u16 == i { - return Ok(pool_keys); - } + let max = if offset_with_size > self.pool_keys_length { + self.pool_keys_length + } else { + offset_with_size + }; - let pool_key = self.pool_keys_by_index.get(i).unwrap(); - pool_keys.push(pool_key); - } - Ok(pool_keys) + (offset..max) + .map(|index| self.pool_keys_by_index.get(index).unwrap()) + .collect() } pub fn count(&self) -> u16 { @@ -137,14 +129,14 @@ mod tests { }; let new_pool_key = PoolKey::new(token_x, token_y, fee_tier).unwrap(); - let result = pool_keys.get_all(3, 0).unwrap(); + let result = pool_keys.get_all(3, 0); assert_eq!(result, vec![]); assert_eq!(result.len(), 0); pool_keys.add(pool_key).unwrap(); pool_keys.add(new_pool_key).unwrap(); - let result = pool_keys.get_all(3, 0).unwrap(); + let result = pool_keys.get_all(3, 0); assert_eq!(result, vec![pool_key, new_pool_key]); assert_eq!(result.len(), 2); } diff --git a/src/contracts/entrypoints.rs b/src/contracts/entrypoints.rs index 7ef8e06f..3ab82cf1 100644 --- a/src/contracts/entrypoints.rs +++ b/src/contracts/entrypoints.rs @@ -361,11 +361,11 @@ pub trait InvariantTrait { #[ink(message)] fn is_tick_initialized(&self, key: PoolKey, index: i32) -> bool; - /// Retrieves listed pools + /// Retrieves listed pool keys /// - `size`: Amount of pool keys to retrive /// - `offset`: The offset from which retrive pools. #[ink(message)] - fn get_pools(&self, size: u8, offset: u16) -> Result<(Vec, u16), InvariantError>; + fn get_pool_keys(&self, size: u16, offset: u16) -> Result<(Vec, u16), InvariantError>; /// Retrieves listed pools for provided token pair /// - `token0`: Address of first token diff --git a/src/e2e/interaction_with_pool_on_removed_fee_tier.rs b/src/e2e/interaction_with_pool_on_removed_fee_tier.rs index 5385a909..b892900c 100644 --- a/src/e2e/interaction_with_pool_on_removed_fee_tier.rs +++ b/src/e2e/interaction_with_pool_on_removed_fee_tier.rs @@ -21,7 +21,7 @@ pub mod e2e_tests { use test_helpers::{ add_fee_tier, address_of, approve, balance_of, change_fee_receiver, claim_fee, create_dex, create_pool, create_position, create_tokens, fee_tier_exist, get_all_positions, get_pool, - get_pools, get_position, init_basic_pool, init_basic_position, init_basic_swap, + get_pool_keys, get_position, init_basic_pool, init_basic_position, init_basic_swap, init_dex_and_tokens, mint, positions_equals, remove_fee_tier, remove_position, swap, transfer_position, withdraw_protocol_fee, }; @@ -101,9 +101,9 @@ pub mod e2e_tests { ) .unwrap(); } - // Get Pools + // Get Pool Keys { - let pools = get_pools!(client, dex, 1, 0).unwrap(); + let pools = get_pool_keys!(client, dex, 1, 0).unwrap(); assert_eq!(pools.0.len(), 1); } // Transfer position diff --git a/src/lib.rs b/src/lib.rs index 26cb4595..7ed15c33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -937,8 +937,12 @@ pub mod invariant { } #[ink(message)] - fn get_pools(&self, size: u8, offset: u16) -> Result<(Vec, u16), InvariantError> { - let pool_keys = self.pool_keys.get_all(size, offset)?; + fn get_pool_keys( + &self, + size: u16, + offset: u16, + ) -> Result<(Vec, u16), InvariantError> { + let pool_keys = self.pool_keys.get_all(size, offset); let pool_keys_count = self.pool_keys.count(); Ok((pool_keys, pool_keys_count)) } diff --git a/src/test_helpers/entrypoints.rs b/src/test_helpers/entrypoints.rs index fb1137db..dfba7164 100644 --- a/src/test_helpers/entrypoints.rs +++ b/src/test_helpers/entrypoints.rs @@ -520,10 +520,10 @@ macro_rules! is_tick_initialized { } #[macro_export] -macro_rules! get_pools { +macro_rules! get_pool_keys { ($client:ident, $dex:ident, $size:expr, $offset:expr) => {{ let mut call_builder = $dex.call_builder::(); - let call = call_builder.get_pools($size, $offset); + let call = call_builder.get_pool_keys($size, $offset); $client .call(&ink_e2e::alice(), &call) .dry_run() From f170e1f83af2a625274d7c733ae229c158388f91 Mon Sep 17 00:00:00 2001 From: zielvna Date: Fri, 5 Jul 2024 11:39:14 +0200 Subject: [PATCH 02/12] add ability to enter positions count and skip pages in get all positions --- sdk/src/invariant.ts | 48 +++++++-- sdk/tests/get-all.test.ts | 212 +++++++++++++++++++++++++++++--------- 2 files changed, 200 insertions(+), 60 deletions(-) diff --git a/sdk/src/invariant.ts b/sdk/src/invariant.ts index bf752391..40f2b0b4 100644 --- a/sdk/src/invariant.ts +++ b/sdk/src/invariant.ts @@ -63,6 +63,9 @@ import { getMinTick } from './utils.js' import { SubmittableExtrinsic } from '@polkadot/api/types/submittable' + +type Page = { index: number; entries: [Position, Pool, Tick, Tick][] } + export class Invariant { contract: ContractPromise api: ApiPromise @@ -525,21 +528,41 @@ export class Invariant { async getAllPositions( owner: string, + positionsCount?: bigint, + skipPages?: number[], options: ContractOptions = { storageDepositLimit: this.storageDepositLimit, refTime: this.gasLimit.refTime.toNumber(), proofSize: this.gasLimit.proofSize.toNumber() } - ): Promise<[Position, Pool, Tick, Tick][]> { - const [positions, poolKeysCount] = await this.getPositions( - owner, - POSITIONS_ENTRIES_LIMIT, - 0n, - options - ) + ): Promise { + const pages: Page[] = [] + + if (!positionsCount) { + const [positionEntries, retrievedPositionCount] = await this.getPositions( + owner, + POSITIONS_ENTRIES_LIMIT, + 0n, + options + ) + + pages.push({ index: 1, entries: positionEntries }) + positionsCount = retrievedPositionCount + } const promises: Promise<[[Position, Pool, Tick, Tick][], bigint]>[] = [] - for (let i = 1; i < Math.ceil(Number(poolKeysCount) / Number(POSITIONS_ENTRIES_LIMIT)); i++) { + const pageIds: number[] = [] + + for ( + let i = pages.length; + i < Math.ceil(Number(positionsCount) / Number(POSITIONS_ENTRIES_LIMIT)); + i++ + ) { + if (skipPages?.includes(i + 1)) { + continue + } + + pageIds.push(i + 1) promises.push( this.getPositions( owner, @@ -550,8 +573,12 @@ export class Invariant { ) } - const positionsEntries = await Promise.all(promises) - return [...positions, ...positionsEntries.map(([positions]) => positions).flat(1)] + const positionsEntriesList = await Promise.all(promises) + const retrievedPages: Page[] = positionsEntriesList.map(([positionsEntries], index) => { + return { index: pageIds[index], entries: positionsEntries } + }) + + return [...pages, ...retrievedPages] } async _getAllPositions( @@ -915,7 +942,6 @@ export class Invariant { } const poolKeysEntries = await Promise.all(promises) - console.log(poolKeysEntries) return [...poolKeys, ...poolKeysEntries.map(([poolKeys]) => poolKeys).flat(1)] } diff --git a/sdk/tests/get-all.test.ts b/sdk/tests/get-all.test.ts index de7c709a..82c3011c 100644 --- a/sdk/tests/get-all.test.ts +++ b/sdk/tests/get-all.test.ts @@ -6,7 +6,7 @@ import { PSP22 } from '../src/psp22' import { initPolkadotApi, newFeeTier, newPoolKey } from '../src/utils' import { assert } from 'chai' import { describe, it } from 'mocha' -import { SQRT_PRICE_DENOMINATOR } from '../src/consts' +import { POSITIONS_ENTRIES_LIMIT, SQRT_PRICE_DENOMINATOR } from '../src/consts' const api = await initPolkadotApi(Network.Local) @@ -38,7 +38,7 @@ describe('get-all', async () => { }) it('get all pool keys works', async function () { - this.timeout(300000) + this.timeout(30000) const feeTiers = Array.from(Array(10).keys()).map(i => newFeeTier(BigInt(i + 1), BigInt(i + 1))) const expectedPoolKeys: PoolKey[] = [] @@ -59,7 +59,7 @@ describe('get-all', async () => { }) it('get all pool keys above single query limit works', async function () { - this.timeout(300000) + this.timeout(30000) const feeTiers = Array.from(Array(100).keys()).map(i => newFeeTier(BigInt(i + 1), BigInt(i + 1)) @@ -88,7 +88,7 @@ describe('get-all', async () => { }) it('get all positions works', async function () { - this.timeout(300000) + this.timeout(30000) await invariant.addFeeTier(account, feeTier) await invariant.createPool( @@ -100,42 +100,47 @@ describe('get-all', async () => { await invariant.createPosition( account, poolKey, - -10n, - 10n, + -BigInt((i + 1) * 10), + BigInt((i + 1) * 10), 1000000n, SQRT_PRICE_DENOMINATOR, 0n ) } - const positionsEntries = await invariant.getAllPositions(account.address) - assert.equal(positionsEntries.length, 10) + const pages = await invariant.getAllPositions(account.address) + assert.equal(pages.map(page => page.entries).flat(1).length, 10) - for (const [index, [position, pool, lowerTick, upperTick]] of positionsEntries.entries()) { - const expectedPosition = await invariant.getPosition(account.address, BigInt(index)) - const expectedPool = await invariant.getPool( - expectedPosition.poolKey.tokenX, - expectedPosition.poolKey.tokenY, - expectedPosition.poolKey.feeTier - ) - const expectedLowerTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.lowerTickIndex - ) - const expectedUpperTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.upperTickIndex - ) + for (const { index, entries } of pages) { + for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + const expectedPosition = await invariant.getPosition( + account.address, + BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) + ) + const expectedPool = await invariant.getPool( + expectedPosition.poolKey.tokenX, + expectedPosition.poolKey.tokenY, + expectedPosition.poolKey.feeTier + ) + const expectedLowerTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.lowerTickIndex + ) + const expectedUpperTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.upperTickIndex + ) - assert.deepEqual(position, expectedPosition) - assert.deepEqual(pool, expectedPool) - assert.deepEqual(lowerTick, expectedLowerTick) - assert.deepEqual(upperTick, expectedUpperTick) + assert.deepEqual(position, expectedPosition) + assert.deepEqual(pool, expectedPool) + assert.deepEqual(lowerTick, expectedLowerTick) + assert.deepEqual(upperTick, expectedUpperTick) + } } }) it('get all positions above single query limit works', async function () { - this.timeout(300000) + this.timeout(30000) await invariant.addFeeTier(account, feeTier) await invariant.createPool( @@ -147,37 +152,146 @@ describe('get-all', async () => { await invariant.createPosition( account, poolKey, - -10n, - 10n, + -BigInt((i + 1) * 10), + BigInt((i + 1) * 10), 1000000n, SQRT_PRICE_DENOMINATOR, 0n ) } - const positionsEntries = await invariant.getAllPositions(account.address) - assert.equal(positionsEntries.length, 50) + const pages = await invariant.getAllPositions(account.address) + assert.equal(pages.map(page => page.entries).flat(1).length, 50) - for (const [index, [position, pool, lowerTick, upperTick]] of positionsEntries.entries()) { - const expectedPosition = await invariant.getPosition(account.address, BigInt(index)) - const expectedPool = await invariant.getPool( - expectedPosition.poolKey.tokenX, - expectedPosition.poolKey.tokenY, - expectedPosition.poolKey.feeTier - ) - const expectedLowerTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.lowerTickIndex + for (const { index, entries } of pages) { + for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + const expectedPosition = await invariant.getPosition( + account.address, + BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) + ) + const expectedPool = await invariant.getPool( + expectedPosition.poolKey.tokenX, + expectedPosition.poolKey.tokenY, + expectedPosition.poolKey.feeTier + ) + const expectedLowerTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.lowerTickIndex + ) + const expectedUpperTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.upperTickIndex + ) + + assert.deepEqual(position, expectedPosition) + assert.deepEqual(pool, expectedPool) + assert.deepEqual(lowerTick, expectedLowerTick) + assert.deepEqual(upperTick, expectedUpperTick) + } + } + }) + + it('get all positions with positions count', async function () { + this.timeout(300000) + + await invariant.addFeeTier(account, feeTier) + await invariant.createPool( + account, + newPoolKey(token0Address, token1Address, feeTier), + SQRT_PRICE_DENOMINATOR + ) + for (let i = 0; i < 50; i++) { + await invariant.createPosition( + account, + poolKey, + -BigInt((i + 1) * 10), + BigInt((i + 1) * 10), + 1000000n, + SQRT_PRICE_DENOMINATOR, + 0n ) - const expectedUpperTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.upperTickIndex + } + + const pages = await invariant.getAllPositions(account.address, 30n) + assert.equal(pages.map(page => page.entries).flat(1).length, 32) + + for (const { index, entries } of pages) { + for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + const expectedPosition = await invariant.getPosition( + account.address, + BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) + ) + const expectedPool = await invariant.getPool( + expectedPosition.poolKey.tokenX, + expectedPosition.poolKey.tokenY, + expectedPosition.poolKey.feeTier + ) + const expectedLowerTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.lowerTickIndex + ) + const expectedUpperTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.upperTickIndex + ) + + assert.deepEqual(position, expectedPosition) + assert.deepEqual(pool, expectedPool) + assert.deepEqual(lowerTick, expectedLowerTick) + assert.deepEqual(upperTick, expectedUpperTick) + } + } + }) + + it('get all positions with skip pages', async function () { + this.timeout(300000) + + await invariant.addFeeTier(account, feeTier) + await invariant.createPool( + account, + newPoolKey(token0Address, token1Address, feeTier), + SQRT_PRICE_DENOMINATOR + ) + for (let i = 0; i < 100; i++) { + await invariant.createPosition( + account, + poolKey, + -BigInt((i + 1) * 10), + BigInt((i + 1) * 10), + 1000000n, + SQRT_PRICE_DENOMINATOR, + 0n ) + } + + const pages = await invariant.getAllPositions(account.address, undefined, [2, 4]) + assert.equal(pages.map(page => page.entries).flat(1).length, 64) + + for (const { index, entries } of pages) { + for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + const expectedPosition = await invariant.getPosition( + account.address, + BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) + ) + const expectedPool = await invariant.getPool( + expectedPosition.poolKey.tokenX, + expectedPosition.poolKey.tokenY, + expectedPosition.poolKey.feeTier + ) + const expectedLowerTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.lowerTickIndex + ) + const expectedUpperTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.upperTickIndex + ) - assert.deepEqual(position, expectedPosition) - assert.deepEqual(pool, expectedPool) - assert.deepEqual(lowerTick, expectedLowerTick) - assert.deepEqual(upperTick, expectedUpperTick) + assert.deepEqual(position, expectedPosition) + assert.deepEqual(pool, expectedPool) + assert.deepEqual(lowerTick, expectedLowerTick) + assert.deepEqual(upperTick, expectedUpperTick) + } } }) }) From 81a3d72fb04e50ee9bdc00f41107251db84b63df Mon Sep 17 00:00:00 2001 From: zielvna Date: Fri, 5 Jul 2024 12:16:44 +0200 Subject: [PATCH 03/12] refactor get all positions --- sdk/src/invariant.ts | 17 ++++++++++--- sdk/tests/get-all.test.ts | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/sdk/src/invariant.ts b/sdk/src/invariant.ts index 40f2b0b4..8072b625 100644 --- a/sdk/src/invariant.ts +++ b/sdk/src/invariant.ts @@ -537,25 +537,34 @@ export class Invariant { } ): Promise { const pages: Page[] = [] + let firstPageIndex = 0 + let actualPositionsCount = positionsCount + + for (let i = 1; i < Number.MAX_SAFE_INTEGER; i++) { + if (!skipPages?.includes(i)) { + firstPageIndex = i + break + } + } if (!positionsCount) { const [positionEntries, retrievedPositionCount] = await this.getPositions( owner, POSITIONS_ENTRIES_LIMIT, - 0n, + BigInt(firstPageIndex - 1) * POSITIONS_ENTRIES_LIMIT, options ) pages.push({ index: 1, entries: positionEntries }) - positionsCount = retrievedPositionCount + actualPositionsCount = retrievedPositionCount } const promises: Promise<[[Position, Pool, Tick, Tick][], bigint]>[] = [] const pageIds: number[] = [] for ( - let i = pages.length; - i < Math.ceil(Number(positionsCount) / Number(POSITIONS_ENTRIES_LIMIT)); + let i = positionsCount ? firstPageIndex - 1 : firstPageIndex; + i < Math.ceil(Number(actualPositionsCount) / Number(POSITIONS_ENTRIES_LIMIT)); i++ ) { if (skipPages?.includes(i + 1)) { diff --git a/sdk/tests/get-all.test.ts b/sdk/tests/get-all.test.ts index 82c3011c..790e0058 100644 --- a/sdk/tests/get-all.test.ts +++ b/sdk/tests/get-all.test.ts @@ -293,5 +293,57 @@ describe('get-all', async () => { assert.deepEqual(upperTick, expectedUpperTick) } } + + it('get all positions with positions count and skip pages', async function () { + this.timeout(300000) + + await invariant.addFeeTier(account, feeTier) + await invariant.createPool( + account, + newPoolKey(token0Address, token1Address, feeTier), + SQRT_PRICE_DENOMINATOR + ) + for (let i = 0; i < 100; i++) { + await invariant.createPosition( + account, + poolKey, + -BigInt((i + 1) * 10), + BigInt((i + 1) * 10), + 1000000n, + SQRT_PRICE_DENOMINATOR, + 0n + ) + } + + const pages = await invariant.getAllPositions(account.address, 90n, [1, 2]) + assert.equal(pages.map(page => page.entries).flat(1).length, 32) + + for (const { index, entries } of pages) { + for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + const expectedPosition = await invariant.getPosition( + account.address, + BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) + ) + const expectedPool = await invariant.getPool( + expectedPosition.poolKey.tokenX, + expectedPosition.poolKey.tokenY, + expectedPosition.poolKey.feeTier + ) + const expectedLowerTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.lowerTickIndex + ) + const expectedUpperTick = await invariant.getTick( + expectedPosition.poolKey, + expectedPosition.upperTickIndex + ) + + assert.deepEqual(position, expectedPosition) + assert.deepEqual(pool, expectedPool) + assert.deepEqual(lowerTick, expectedLowerTick) + assert.deepEqual(upperTick, expectedUpperTick) + } + } + }) }) }) From 55d5621ece1cad7eb95fbde95d787904028a41f3 Mon Sep 17 00:00:00 2001 From: zielvna Date: Fri, 5 Jul 2024 12:25:08 +0200 Subject: [PATCH 04/12] optimize get all positions --- sdk/src/invariant.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/sdk/src/invariant.ts b/sdk/src/invariant.ts index 8072b625..888cd86c 100644 --- a/sdk/src/invariant.ts +++ b/sdk/src/invariant.ts @@ -536,10 +536,7 @@ export class Invariant { proofSize: this.gasLimit.proofSize.toNumber() } ): Promise { - const pages: Page[] = [] let firstPageIndex = 0 - let actualPositionsCount = positionsCount - for (let i = 1; i < Number.MAX_SAFE_INTEGER; i++) { if (!skipPages?.includes(i)) { firstPageIndex = i @@ -547,8 +544,10 @@ export class Invariant { } } + let pages: Page[] = [] + let actualPositionsCount = positionsCount if (!positionsCount) { - const [positionEntries, retrievedPositionCount] = await this.getPositions( + const [positionEntries, positionsCount] = await this.getPositions( owner, POSITIONS_ENTRIES_LIMIT, BigInt(firstPageIndex - 1) * POSITIONS_ENTRIES_LIMIT, @@ -556,11 +555,11 @@ export class Invariant { ) pages.push({ index: 1, entries: positionEntries }) - actualPositionsCount = retrievedPositionCount + actualPositionsCount = positionsCount } const promises: Promise<[[Position, Pool, Tick, Tick][], bigint]>[] = [] - const pageIds: number[] = [] + const pageIndexes: number[] = [] for ( let i = positionsCount ? firstPageIndex - 1 : firstPageIndex; @@ -571,7 +570,7 @@ export class Invariant { continue } - pageIds.push(i + 1) + pageIndexes.push(i + 1) promises.push( this.getPositions( owner, @@ -583,11 +582,14 @@ export class Invariant { } const positionsEntriesList = await Promise.all(promises) - const retrievedPages: Page[] = positionsEntriesList.map(([positionsEntries], index) => { - return { index: pageIds[index], entries: positionsEntries } - }) + pages = [ + ...pages, + ...positionsEntriesList.map(([positionsEntries], index) => { + return { index: pageIndexes[index], entries: positionsEntries } + }) + ] - return [...pages, ...retrievedPages] + return pages } async _getAllPositions( From 075479e7f9e9ef4c9f5a25d203a2c6538fc806ca Mon Sep 17 00:00:00 2001 From: zielvna Date: Fri, 5 Jul 2024 12:41:52 +0200 Subject: [PATCH 05/12] fix tests --- sdk/tests/position.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/tests/position.test.ts b/sdk/tests/position.test.ts index 8b8301f6..4eeae217 100644 --- a/sdk/tests/position.test.ts +++ b/sdk/tests/position.test.ts @@ -125,7 +125,7 @@ describe('position', async () => { objectEquals(result.events[4], expectedRemovePositionEvent, ['timestamp']) assertThrowsAsync(invariant.getPosition(account.address, 0n), InvariantError.PositionNotFound) - const positions = await invariant.getAllPositions(account.address) + const positions = await invariant._getAllPositions(account.address) assert.deepEqual(positions.length, 0) } { From 1bcca55def75ba158709b08cb7a838ef9edfbb7f Mon Sep 17 00:00:00 2001 From: zielvna Date: Fri, 5 Jul 2024 16:04:06 +0200 Subject: [PATCH 06/12] fix tests --- sdk/tests/get-all.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/tests/get-all.test.ts b/sdk/tests/get-all.test.ts index 790e0058..9517336e 100644 --- a/sdk/tests/get-all.test.ts +++ b/sdk/tests/get-all.test.ts @@ -24,7 +24,9 @@ const feeTier = newFeeTier(6000000000n, 10n) let poolKey = newPoolKey(token0Address, token1Address, feeTier) describe('get-all', async () => { - beforeEach(async () => { + beforeEach(async function () { + this.timeout(10000) + invariant = await Invariant.deploy(api, Network.Local, account, toPercentage(1n, 2n)) token0Address = await PSP22.deploy(api, account, 1000000000000n, 'Coin', 'COIN', 0n) token1Address = await PSP22.deploy(api, account, 1000000000000n, 'Coin', 'COIN', 0n) From 536fee0343dc542104e66ba24c331aebed3524e3 Mon Sep 17 00:00:00 2001 From: zielvna Date: Fri, 5 Jul 2024 16:26:12 +0200 Subject: [PATCH 07/12] fix get all tests --- sdk/tests/get-all.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/tests/get-all.test.ts b/sdk/tests/get-all.test.ts index 9517336e..3bfd32a0 100644 --- a/sdk/tests/get-all.test.ts +++ b/sdk/tests/get-all.test.ts @@ -60,7 +60,7 @@ describe('get-all', async () => { }) }) - it('get all pool keys above single query limit works', async function () { + it('get all pool keys above single query limit works', async function (done) { this.timeout(30000) const feeTiers = Array.from(Array(100).keys()).map(i => @@ -87,6 +87,8 @@ describe('get-all', async () => { poolKeys.map((poolKey, index) => { assert.deepEqual(poolKey, expectedPoolKeys[index]) }) + + done() }) it('get all positions works', async function () { From 221fc1f55fdf668fe4307b003945c5d85e8d4ef9 Mon Sep 17 00:00:00 2001 From: zielvna Date: Fri, 5 Jul 2024 16:43:45 +0200 Subject: [PATCH 08/12] fix tests --- sdk/tests/get-all.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sdk/tests/get-all.test.ts b/sdk/tests/get-all.test.ts index 3bfd32a0..fc3a0319 100644 --- a/sdk/tests/get-all.test.ts +++ b/sdk/tests/get-all.test.ts @@ -60,8 +60,8 @@ describe('get-all', async () => { }) }) - it('get all pool keys above single query limit works', async function (done) { - this.timeout(30000) + it('get all pool keys above single query limit works', async function () { + this.timeout(120000) const feeTiers = Array.from(Array(100).keys()).map(i => newFeeTier(BigInt(i + 1), BigInt(i + 1)) @@ -87,8 +87,6 @@ describe('get-all', async () => { poolKeys.map((poolKey, index) => { assert.deepEqual(poolKey, expectedPoolKeys[index]) }) - - done() }) it('get all positions works', async function () { From 167d6f96662978790a5c862b9abdb75fd53fd152 Mon Sep 17 00:00:00 2001 From: zielvna Date: Mon, 8 Jul 2024 10:24:40 +0200 Subject: [PATCH 09/12] refactor get all positions method in sdk --- sdk/src/invariant.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sdk/src/invariant.ts b/sdk/src/invariant.ts index 888cd86c..9e9a6e30 100644 --- a/sdk/src/invariant.ts +++ b/sdk/src/invariant.ts @@ -536,13 +536,7 @@ export class Invariant { proofSize: this.gasLimit.proofSize.toNumber() } ): Promise { - let firstPageIndex = 0 - for (let i = 1; i < Number.MAX_SAFE_INTEGER; i++) { - if (!skipPages?.includes(i)) { - firstPageIndex = i - break - } - } + const firstPageIndex = skipPages?.find(i => !skipPages.includes(i)) || 1 let pages: Page[] = [] let actualPositionsCount = positionsCount From f7e51977fc57ee36ff1a2ca1a2073cf95939f3e5 Mon Sep 17 00:00:00 2001 From: zielvna Date: Mon, 8 Jul 2024 10:44:31 +0200 Subject: [PATCH 10/12] remove ticks from get positions entrypoint --- sdk/contracts/invariant/invariant.json | 6 +-- sdk/contracts/invariant/invariant.wasm | Bin 118600 -> 117810 bytes sdk/src/abis/invariant.ts | 6 +-- sdk/src/invariant.ts | 6 +-- sdk/src/wasm/consts.rs | 3 +- sdk/tests/get-all.test.ts | 61 ++----------------------- sdk/tests/get-positions.test.ts | 40 ---------------- src/contracts/entrypoints.rs | 2 +- src/lib.rs | 10 +--- 9 files changed, 16 insertions(+), 118 deletions(-) diff --git a/sdk/contracts/invariant/invariant.json b/sdk/contracts/invariant/invariant.json index bc539ddb..c40cf461 100644 --- a/sdk/contracts/invariant/invariant.json +++ b/sdk/contracts/invariant/invariant.json @@ -1,6 +1,6 @@ { "source": { - "hash": "0x8947bd15dbb2c5b5236b7f6d16651ce1f4fedd8661c3710f0ca0efa48a38730e", + "hash": "0xba8d2adeacedf76483f1b5e50843cdf9e09f979a0be15def9c09711cd6821c4e", "language": "ink! 5.0.0", "compiler": "rustc 1.77.0", "build_info": { @@ -4649,9 +4649,7 @@ "def": { "tuple": [ 15, - 25, - 36, - 36 + 25 ] } } diff --git a/sdk/contracts/invariant/invariant.wasm b/sdk/contracts/invariant/invariant.wasm index d5872933d80301677af1d4af278517836db51322..11f0d6ac4b96e025929915196e12cc6694ec8e1f 100644 GIT binary patch delta 29132 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 IzqITB0k^mjSpWb4 delta 29559 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 diff --git a/sdk/src/abis/invariant.ts b/sdk/src/abis/invariant.ts index dee0992f..ec0e838a 100644 --- a/sdk/src/abis/invariant.ts +++ b/sdk/src/abis/invariant.ts @@ -1,7 +1,7 @@ export const abi = ` { "source": { - "hash": "0x8947bd15dbb2c5b5236b7f6d16651ce1f4fedd8661c3710f0ca0efa48a38730e", + "hash": "0xba8d2adeacedf76483f1b5e50843cdf9e09f979a0be15def9c09711cd6821c4e", "language": "ink! 5.0.0", "compiler": "rustc 1.77.0", "build_info": { @@ -4650,9 +4650,7 @@ export const abi = ` "def": { "tuple": [ 15, - 25, - 36, - 36 + 25 ] } } diff --git a/sdk/src/invariant.ts b/sdk/src/invariant.ts index 9e9a6e30..f221d801 100644 --- a/sdk/src/invariant.ts +++ b/sdk/src/invariant.ts @@ -64,7 +64,7 @@ import { } from './utils.js' import { SubmittableExtrinsic } from '@polkadot/api/types/submittable' -type Page = { index: number; entries: [Position, Pool, Tick, Tick][] } +type Page = { index: number; entries: [Position, Pool][] } export class Invariant { contract: ContractPromise @@ -507,7 +507,7 @@ export class Invariant { refTime: this.gasLimit.refTime.toNumber(), proofSize: this.gasLimit.proofSize.toNumber() } - ): Promise<[[Position, Pool, Tick, Tick][], bigint]> { + ): Promise<[[Position, Pool][], bigint]> { const result = await sendQuery( this.contract, this.api.registry.createType('WeightV2', { @@ -552,7 +552,7 @@ export class Invariant { actualPositionsCount = positionsCount } - const promises: Promise<[[Position, Pool, Tick, Tick][], bigint]>[] = [] + const promises: Promise<[[Position, Pool][], bigint]>[] = [] const pageIndexes: number[] = [] for ( diff --git a/sdk/src/wasm/consts.rs b/sdk/src/wasm/consts.rs index 2dc7201f..50b4fbb4 100644 --- a/sdk/src/wasm/consts.rs +++ b/sdk/src/wasm/consts.rs @@ -29,8 +29,7 @@ pub const POOL_KEY_SIZE: usize = ACCOUNT_ID_SIZE + ACCOUNT_ID_SIZE + (64 + 8); pub const POSITION_SIZE: usize = POOL_KEY_SIZE + 128 + 32 + 32 + 128 + 128 + 64 + 128 + 128; pub const POOL_SIZE: usize = 128 + 128 + 32 + 128 + 128 + 128 + 128 + 64 + 64 + ACCOUNT_ID_SIZE; pub const TICK_SIZE: usize = 32 + 8 + 128 + 128 + 128 + 128 + 128 + 64; -pub const POSITIONS_ENTRIES_LIMIT: usize = - (MAX_RESULT_SIZE - 32) / (POSITION_SIZE + POOL_SIZE + TICK_SIZE + TICK_SIZE); +pub const POSITIONS_ENTRIES_LIMIT: usize = (MAX_RESULT_SIZE - 32) / (POSITION_SIZE + POOL_SIZE); #[wasm_wrapper] pub fn get_global_max_sqrt_price() -> u128 { diff --git a/sdk/tests/get-all.test.ts b/sdk/tests/get-all.test.ts index fc3a0319..988f3116 100644 --- a/sdk/tests/get-all.test.ts +++ b/sdk/tests/get-all.test.ts @@ -114,7 +114,7 @@ describe('get-all', async () => { assert.equal(pages.map(page => page.entries).flat(1).length, 10) for (const { index, entries } of pages) { - for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + for (const [positionIndex, [position, pool]] of entries.entries()) { const expectedPosition = await invariant.getPosition( account.address, BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) @@ -124,19 +124,9 @@ describe('get-all', async () => { expectedPosition.poolKey.tokenY, expectedPosition.poolKey.feeTier ) - const expectedLowerTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.lowerTickIndex - ) - const expectedUpperTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.upperTickIndex - ) assert.deepEqual(position, expectedPosition) assert.deepEqual(pool, expectedPool) - assert.deepEqual(lowerTick, expectedLowerTick) - assert.deepEqual(upperTick, expectedUpperTick) } } }) @@ -166,7 +156,7 @@ describe('get-all', async () => { assert.equal(pages.map(page => page.entries).flat(1).length, 50) for (const { index, entries } of pages) { - for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + for (const [positionIndex, [position, pool]] of entries.entries()) { const expectedPosition = await invariant.getPosition( account.address, BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) @@ -176,19 +166,9 @@ describe('get-all', async () => { expectedPosition.poolKey.tokenY, expectedPosition.poolKey.feeTier ) - const expectedLowerTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.lowerTickIndex - ) - const expectedUpperTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.upperTickIndex - ) assert.deepEqual(position, expectedPosition) assert.deepEqual(pool, expectedPool) - assert.deepEqual(lowerTick, expectedLowerTick) - assert.deepEqual(upperTick, expectedUpperTick) } } }) @@ -218,7 +198,7 @@ describe('get-all', async () => { assert.equal(pages.map(page => page.entries).flat(1).length, 32) for (const { index, entries } of pages) { - for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + for (const [positionIndex, [position, pool]] of entries.entries()) { const expectedPosition = await invariant.getPosition( account.address, BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) @@ -228,19 +208,9 @@ describe('get-all', async () => { expectedPosition.poolKey.tokenY, expectedPosition.poolKey.feeTier ) - const expectedLowerTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.lowerTickIndex - ) - const expectedUpperTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.upperTickIndex - ) assert.deepEqual(position, expectedPosition) assert.deepEqual(pool, expectedPool) - assert.deepEqual(lowerTick, expectedLowerTick) - assert.deepEqual(upperTick, expectedUpperTick) } } }) @@ -270,7 +240,7 @@ describe('get-all', async () => { assert.equal(pages.map(page => page.entries).flat(1).length, 64) for (const { index, entries } of pages) { - for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + for (const [positionIndex, [position, pool]] of entries.entries()) { const expectedPosition = await invariant.getPosition( account.address, BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) @@ -280,19 +250,8 @@ describe('get-all', async () => { expectedPosition.poolKey.tokenY, expectedPosition.poolKey.feeTier ) - const expectedLowerTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.lowerTickIndex - ) - const expectedUpperTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.upperTickIndex - ) - assert.deepEqual(position, expectedPosition) assert.deepEqual(pool, expectedPool) - assert.deepEqual(lowerTick, expectedLowerTick) - assert.deepEqual(upperTick, expectedUpperTick) } } @@ -321,7 +280,7 @@ describe('get-all', async () => { assert.equal(pages.map(page => page.entries).flat(1).length, 32) for (const { index, entries } of pages) { - for (const [positionIndex, [position, pool, lowerTick, upperTick]] of entries.entries()) { + for (const [positionIndex, [position, pool]] of entries.entries()) { const expectedPosition = await invariant.getPosition( account.address, BigInt((index - 1) * Number(POSITIONS_ENTRIES_LIMIT) + positionIndex) @@ -331,19 +290,9 @@ describe('get-all', async () => { expectedPosition.poolKey.tokenY, expectedPosition.poolKey.feeTier ) - const expectedLowerTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.lowerTickIndex - ) - const expectedUpperTick = await invariant.getTick( - expectedPosition.poolKey, - expectedPosition.upperTickIndex - ) assert.deepEqual(position, expectedPosition) assert.deepEqual(pool, expectedPool) - assert.deepEqual(lowerTick, expectedLowerTick) - assert.deepEqual(upperTick, expectedUpperTick) } } }) diff --git a/sdk/tests/get-positions.test.ts b/sdk/tests/get-positions.test.ts index 037375c9..48d6d7c1 100644 --- a/sdk/tests/get-positions.test.ts +++ b/sdk/tests/get-positions.test.ts @@ -70,29 +70,9 @@ describe('get-positions', async () => { feeProtocolTokenY: 0n, feeReceiver: account.address } - const firstExpectedLowerTick = { - index: -10n, - sign: true, - liquidityChange: 1000000000000n, - liquidityGross: 1000000000000n, - sqrtPrice: 999500149965000000000000n, - feeGrowthOutsideX: 0n, - feeGrowthOutsideY: 0n - } - const firstExpectedUpperTick = { - index: 10n, - sign: false, - liquidityChange: 1000000000000n, - liquidityGross: 1000000000000n, - sqrtPrice: 1000500100010000000000000n, - feeGrowthOutsideX: 0n, - feeGrowthOutsideY: 0n - } objectEquals(result[0][0][0], firstExpectedPosition, ['lastBlockNumber']) objectEquals(result[0][0][1], firstExpectedPool, ['startTimestamp', 'lastTimestamp']) - objectEquals(result[0][0][2], firstExpectedLowerTick, ['secondsOutside']) - objectEquals(result[0][0][3], firstExpectedUpperTick, ['secondsOutside']) const secondExpectedPosition = { poolKey, @@ -114,29 +94,9 @@ describe('get-positions', async () => { feeProtocolTokenY: 0n, feeReceiver: account.address } - const secondExpectedLowerTick = { - index: -20n, - sign: true, - liquidityChange: 1000000000000n, - liquidityGross: 1000000000000n, - sqrtPrice: 999000549780000000000000n, - feeGrowthOutsideX: 0n, - feeGrowthOutsideY: 0n - } - const secondExpectedUpperTick = { - index: 20n, - sign: false, - liquidityChange: 1000000000000n, - liquidityGross: 1000000000000n, - sqrtPrice: 1001000450120000000000000n, - feeGrowthOutsideX: 0n, - feeGrowthOutsideY: 0n - } objectEquals(result[0][1][0], secondExpectedPosition, ['lastBlockNumber']) objectEquals(result[0][1][1], secondExpectedPool, ['startTimestamp', 'lastTimestamp']) - objectEquals(result[0][1][2], secondExpectedLowerTick, ['secondsOutside']) - objectEquals(result[0][1][3], secondExpectedUpperTick, ['secondsOutside']) }) it('get positions less than exist', async () => { diff --git a/src/contracts/entrypoints.rs b/src/contracts/entrypoints.rs index 3ab82cf1..0564fad9 100644 --- a/src/contracts/entrypoints.rs +++ b/src/contracts/entrypoints.rs @@ -235,7 +235,7 @@ pub trait InvariantTrait { owner_id: AccountId, size: u32, offset: u32, - ) -> Result<(Vec<(Position, Pool, Tick, Tick)>, u32), InvariantError>; + ) -> Result<(Vec<(Position, Pool)>, u32), InvariantError>; /// Allows an authorized user (owner of the position) to claim collected fees. /// diff --git a/src/lib.rs b/src/lib.rs index 7ed15c33..e4ede8d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -699,19 +699,13 @@ pub mod invariant { owner_id: AccountId, size: u32, offset: u32, - ) -> Result<(Vec<(Position, Pool, Tick, Tick)>, u32), InvariantError> { + ) -> Result<(Vec<(Position, Pool)>, u32), InvariantError> { let positions = self.positions.get_all(owner_id, size, offset); let mut entries = vec![]; for position in &positions { let pool = self.pools.get(position.pool_key)?; - let lower_tick = self - .ticks - .get(position.pool_key, position.lower_tick_index)?; - let upper_tick = self - .ticks - .get(position.pool_key, position.upper_tick_index)?; - entries.push((*position, pool, lower_tick, upper_tick)) + entries.push((*position, pool)) } Ok((entries, self.positions.get_length(owner_id))) From 5cffcf3d48c85e6ecd50d7e5ae22747b31d2531f Mon Sep 17 00:00:00 2001 From: zielvna Date: Mon, 8 Jul 2024 12:12:25 +0200 Subject: [PATCH 11/12] bump version --- sdk-usage/package-lock.json | 45 +++++++------------------------------ sdk-usage/package.json | 2 +- sdk/package-lock.json | 12 +++++----- sdk/package.json | 4 ++-- sdk/src/consts.ts | 8 +++---- 5 files changed, 21 insertions(+), 50 deletions(-) diff --git a/sdk-usage/package-lock.json b/sdk-usage/package-lock.json index 0d48669b..0c349e96 100644 --- a/sdk-usage/package-lock.json +++ b/sdk-usage/package-lock.json @@ -1,7 +1,7 @@ { "name": "sdk-test", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -9,19 +9,16 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@invariant-labs/a0-sdk": "^0.2.8", + "@invariant-labs/a0-sdk": "../sdk", "dotenv": "^16.4.5" }, "devDependencies": { "typescript": "^5.4.2" } }, - "../a0-sdk": { - "extraneous": true - }, "../sdk": { "name": "@invariant-labs/a0-sdk", - "version": "0.2.9", + "version": "0.2.10", "license": "ISC", "dependencies": { "@invariant-labs/a0-sdk-wasm": "^0.1.20", @@ -48,7 +45,8 @@ }, "node_modules/dotenv": { "version": "16.4.5", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { "node": ">=12" }, @@ -57,9 +55,10 @@ } }, "node_modules/typescript": { - "version": "5.4.5", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -68,33 +67,5 @@ "node": ">=14.17" } } - }, - "dependencies": { - "@invariant-labs/a0-sdk": { - "version": "file:../sdk", - "requires": { - "@invariant-labs/a0-sdk-wasm": "^0.1.20", - "@polkadot/api": "^10.12.4", - "@polkadot/api-contract": "^10.12.4", - "@scio-labs/use-inkathon": "^0.6.3", - "@types/chai": "^4.3.11", - "@types/mocha": "^10.0.6", - "@typescript-eslint/eslint-plugin": "^6.16.0", - "@typescript-eslint/parser": "^6.16.0", - "chai": "^5.0.0", - "eslint": "^8.56.0", - "ts-mocha": "^10.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.3.3", - "wasm-pack": "^0.12.1" - } - }, - "dotenv": { - "version": "16.4.5" - }, - "typescript": { - "version": "5.4.5", - "dev": true - } } } diff --git a/sdk-usage/package.json b/sdk-usage/package.json index 44f8be14..b9630fe8 100644 --- a/sdk-usage/package.json +++ b/sdk-usage/package.json @@ -19,7 +19,7 @@ "author": "", "license": "ISC", "dependencies": { - "@invariant-labs/a0-sdk": "^0.2.8", + "@invariant-labs/a0-sdk": "../sdk", "dotenv": "^16.4.5" }, "devDependencies": { diff --git a/sdk/package-lock.json b/sdk/package-lock.json index b7e0dc66..4bed9fab 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,15 +1,15 @@ { "name": "@invariant-labs/a0-sdk", - "version": "0.2.9", + "version": "0.2.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@invariant-labs/a0-sdk", - "version": "0.2.9", + "version": "0.2.10", "license": "ISC", "dependencies": { - "@invariant-labs/a0-sdk-wasm": "^0.1.20", + "@invariant-labs/a0-sdk-wasm": "^0.1.21", "@polkadot/api": "^10.12.4", "@polkadot/api-contract": "^10.12.4", "@scio-labs/use-inkathon": "^0.6.3", @@ -523,9 +523,9 @@ "dev": true }, "node_modules/@invariant-labs/a0-sdk-wasm": { - "version": "0.1.20", - "resolved": "https://registry.npmjs.org/@invariant-labs/a0-sdk-wasm/-/a0-sdk-wasm-0.1.20.tgz", - "integrity": "sha512-28eWwgSDezIfp3WBQkjUm9e3MSjofov1KrxuBoOJCHd78BtOCeiGA5ab2imonn3ZHSPobSe9i8aus3BEHfzDIw==" + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@invariant-labs/a0-sdk-wasm/-/a0-sdk-wasm-0.1.21.tgz", + "integrity": "sha512-I+Y4byfEPzm8kihZp7Yoft1gLrn0zOXWrhmvdMJ0/6Kz5uwP7QyRkPPIdfAla/sVLGERwkg9gsalfN5Uv2gx9A==" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", diff --git a/sdk/package.json b/sdk/package.json index e828ba90..0da00a58 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@invariant-labs/a0-sdk", - "version": "0.2.9", + "version": "0.2.10", "collaborators": [ "Invariant Labs" ], @@ -61,7 +61,7 @@ "@polkadot/api": "^10.12.4", "@polkadot/api-contract": "^10.12.4", "@scio-labs/use-inkathon": "^0.6.3", - "@invariant-labs/a0-sdk-wasm": "^0.1.20", + "@invariant-labs/a0-sdk-wasm": "^0.1.21", "ts-node": "^10.9.2", "typescript": "^5.3.3" }, diff --git a/sdk/src/consts.ts b/sdk/src/consts.ts index 66d3c070..9cd093e7 100644 --- a/sdk/src/consts.ts +++ b/sdk/src/consts.ts @@ -37,10 +37,10 @@ export const DEFAULT_LOCAL = 'ws://127.0.0.1:9944' export const TESTNET_WAZERO_ADDRESS = '5EFDb7mKbougLtr5dnwd5KDfZ3wK55JPGPLiryKq4uRMPR46' -export const TESTNET_INVARIANT_ADDRESS = '5DwKDnzBTy3REu87qyygeLygCqNHmBApiK2FfJif2GvRFRrN' -export const TESTNET_BTC_ADDRESS = '5CwK99HSSankDPFR3hAeW7CX95VGVtYy2Fy864eon54Lx4AX' -export const TESTNET_ETH_ADDRESS = '5E4Cd4Z76ZfTFxbV8yrKjGwyJ5h1NLNxog2zFTAh49w52Phy' -export const TESTNET_USDC_ADDRESS = '5Cju3x5gMDos4ALNJxQ3QUb955NQZhSYZgQ1t9TEH9kBfwAr' +export const TESTNET_INVARIANT_ADDRESS = '5FxTEpmxrqr8Nwx7u1Y7heTFJiSe8xU1zZMfWmuC5f1UJMbf' +export const TESTNET_BTC_ADDRESS = '5DPvVudq6bCZ84jwFSsRwD7hDh4aaGh7XzpJUKXWDDHMHXaL' +export const TESTNET_ETH_ADDRESS = '5GmqniLhEcZCsn7vrer6yHzn7giapNG6N4fHx9exXHn29MvC' +export const TESTNET_USDC_ADDRESS = '5Cr6gjWk5cUavkbo9d6GTZBGjf4XavQf6WpiLkgT9UCmGQYP' export const FEE_GROWTH_DENOMINATOR = getFeeGrowthDenominator() export const FIXED_POINT_DENOMINATOR = getFixedPointDenominator() From 3966f861ad8173a34d9eb4dc38f1e9139878920e Mon Sep 17 00:00:00 2001 From: zielvna Date: Mon, 8 Jul 2024 15:05:00 +0200 Subject: [PATCH 12/12] fix tests --- sdk/tests/get-all.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk/tests/get-all.test.ts b/sdk/tests/get-all.test.ts index 988f3116..dc9c25b9 100644 --- a/sdk/tests/get-all.test.ts +++ b/sdk/tests/get-all.test.ts @@ -182,7 +182,7 @@ describe('get-all', async () => { newPoolKey(token0Address, token1Address, feeTier), SQRT_PRICE_DENOMINATOR ) - for (let i = 0; i < 50; i++) { + for (let i = 0; i < 60; i++) { await invariant.createPosition( account, poolKey, @@ -194,8 +194,8 @@ describe('get-all', async () => { ) } - const pages = await invariant.getAllPositions(account.address, 30n) - assert.equal(pages.map(page => page.entries).flat(1).length, 32) + const pages = await invariant.getAllPositions(account.address, 50n) + assert.equal(pages.map(page => page.entries).flat(1).length, 51) for (const { index, entries } of pages) { for (const [positionIndex, [position, pool]] of entries.entries()) { @@ -224,7 +224,7 @@ describe('get-all', async () => { newPoolKey(token0Address, token1Address, feeTier), SQRT_PRICE_DENOMINATOR ) - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 200; i++) { await invariant.createPosition( account, poolKey, @@ -237,7 +237,7 @@ describe('get-all', async () => { } const pages = await invariant.getAllPositions(account.address, undefined, [2, 4]) - assert.equal(pages.map(page => page.entries).flat(1).length, 64) + assert.equal(pages.map(page => page.entries).flat(1).length, 102) for (const { index, entries } of pages) { for (const [positionIndex, [position, pool]] of entries.entries()) { @@ -264,7 +264,7 @@ describe('get-all', async () => { newPoolKey(token0Address, token1Address, feeTier), SQRT_PRICE_DENOMINATOR ) - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 160n; i++) { await invariant.createPosition( account, poolKey, @@ -276,8 +276,8 @@ describe('get-all', async () => { ) } - const pages = await invariant.getAllPositions(account.address, 90n, [1, 2]) - assert.equal(pages.map(page => page.entries).flat(1).length, 32) + const pages = await invariant.getAllPositions(account.address, 140n, [1, 2]) + assert.equal(pages.map(page => page.entries).flat(1).length, 38) for (const { index, entries } of pages) { for (const [positionIndex, [position, pool]] of entries.entries()) {