From 50ed630bf4769c1239e642e0e9b189948c47d1c4 Mon Sep 17 00:00:00 2001 From: beer-1 <147697694+beer-1@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:14:42 +0900 Subject: [PATCH] fix: audit (#126) * add verify function * fix dex single_asset_provide to use more explicit condition * fix nft to mintable by collection object owner * fix tests and add comments --- crates/gas/src/initia_stdlib.rs | 1 + crates/natives/src/crypto/secp256k1.rs | 66 ++- precompile/binaries/minlib/collection.mv | Bin 4824 -> 4708 bytes precompile/binaries/minlib/dex.mv | Bin 13002 -> 13002 bytes precompile/binaries/minlib/initia_nft.mv | Bin 3928 -> 3973 bytes precompile/binaries/minlib/nft.mv | Bin 3775 -> 3853 bytes precompile/binaries/minlib/object.mv | Bin 4450 -> 4514 bytes precompile/binaries/minlib/secp256k1.mv | Bin 1201 -> 1326 bytes .../binaries/minlib/soul_bound_token.mv | Bin 4973 -> 5018 bytes precompile/binaries/stdlib/collection.mv | Bin 4824 -> 4708 bytes precompile/binaries/stdlib/dex.mv | Bin 13002 -> 13002 bytes precompile/binaries/stdlib/initia_nft.mv | Bin 3928 -> 3973 bytes precompile/binaries/stdlib/nft.mv | Bin 3775 -> 3853 bytes precompile/binaries/stdlib/object.mv | Bin 4450 -> 4514 bytes precompile/binaries/stdlib/secp256k1.mv | Bin 1201 -> 1326 bytes .../binaries/stdlib/soul_bound_token.mv | Bin 4973 -> 5018 bytes .../modules/initia_stdlib/sources/block.move | 2 +- .../sources/crypto/secp256k1.move | 53 +++ .../modules/initia_stdlib/sources/dex.move | 388 +++++++++--------- .../sources/fa/fungible_asset.move | 6 +- .../modules/initia_stdlib/sources/object.move | 11 + .../sources/token/collection.move | 22 +- .../sources/token/initia_nft.move | 9 +- .../initia_stdlib/sources/token/nft.move | 107 +++-- .../sources/token/soul_bound_token.move | 8 +- .../modules/minitia_stdlib/sources/block.move | 2 +- .../sources/crypto/secp256k1.move | 53 +++ .../modules/minitia_stdlib/sources/dex.move | 388 +++++++++--------- .../minitia_stdlib/sources/object.move | 11 + .../sources/token/collection.move | 23 +- .../sources/token/initia_nft.move | 9 +- .../minitia_stdlib/sources/token/nft.move | 107 +++-- .../sources/token/soul_bound_token.move | 8 +- 33 files changed, 797 insertions(+), 477 deletions(-) diff --git a/crates/gas/src/initia_stdlib.rs b/crates/gas/src/initia_stdlib.rs index 3e0b8e71..cdf18f66 100644 --- a/crates/gas/src/initia_stdlib.rs +++ b/crates/gas/src/initia_stdlib.rs @@ -47,6 +47,7 @@ crate::macros::define_gas_parameters!( [crypto_ed25519_per_msg_byte_hashing: InternalGasPerByte, "crypto.ed25519.per_msg_byte_hashing", 220], [crypto_secp256k1_base: InternalGas, "crypto.secp256k1.base", 551], + [crypto_secp256k1_per_sig_verify: InternalGasPerArg, "crypto.secp256k1.per_sig_verify", 981492], [crypto_secp256k1_per_ecdsa_recover: InternalGasPerArg, "crypto.secp256k1.per_ecdsa_recover", 5918360], [crypto_secp256k1_per_pubkey_deserialize: InternalGasPerArg, "crypto.secp256k1.per_pubkey_deserialize", 139688], [crypto_secp256k1_per_sig_deserialize: InternalGasPerArg, "crypto.secp256k1.per_sig_deserialize", 1378], diff --git a/crates/natives/src/crypto/secp256k1.rs b/crates/natives/src/crypto/secp256k1.rs index 0603e541..0e4cbcc0 100644 --- a/crates/natives/src/crypto/secp256k1.rs +++ b/crates/natives/src/crypto/secp256k1.rs @@ -4,7 +4,9 @@ use move_vm_runtime::native_functions::NativeFunction; use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; use libsecp256k1::{ - recover, util::MESSAGE_SIZE, util::SIGNATURE_SIZE, Message, RecoveryId, Signature, + recover, + util::{COMPRESSED_PUBLIC_KEY_SIZE, MESSAGE_SIZE, SIGNATURE_SIZE}, + verify, Message, PublicKey, RecoveryId, Signature, }; use smallvec::{smallvec, SmallVec}; @@ -33,6 +35,56 @@ fn read_hash(data: &[u8]) -> Result<[u8; MESSAGE_SIZE], TryFromSliceError> { data.try_into() } +fn read_pubkey(data: &[u8]) -> Result<[u8; COMPRESSED_PUBLIC_KEY_SIZE], TryFromSliceError> { + data.try_into() +} + +pub fn native_verify( + context: &mut SafeNativeContext, + _ty_args: Vec, + mut arguments: VecDeque, +) -> SafeNativeResult> { + let gas_params = &context.native_gas_params.initia_stdlib; + context.charge(gas_params.crypto_secp256k1_base)?; + + debug_assert!(_ty_args.is_empty()); + debug_assert!(arguments.len() == 3); + + let signature = safely_pop_arg!(arguments, Vec); + let pubkey = safely_pop_arg!(arguments, Vec); + let message = safely_pop_arg!(arguments, Vec); + + let msg = match read_hash(&message) { + Ok(mh) => Message::parse(&mh), + Err(_) => { + return Err(SafeNativeError::Abort { + abort_code: UNABLE_TO_DESERIALIZE, + }); + } + }; + + context.charge(gas_params.crypto_secp256k1_per_pubkey_deserialize * NumArgs::one())?; + let pk = match read_pubkey(&pubkey) { + Ok(pk) => match PublicKey::parse_compressed(&pk) { + Ok(pk) => pk, + Err(_) => return Ok(smallvec![Value::bool(false)]), + }, + Err(_) => return Ok(smallvec![Value::bool(false)]), + }; + + context.charge(gas_params.crypto_secp256k1_per_sig_deserialize * NumArgs::one())?; + let sig = match read_signature(&signature) { + Ok(sig) => match Signature::parse_standard(&sig) { + Ok(sig) => sig, + Err(_) => return Ok(smallvec![Value::bool(false)]), + }, + Err(_) => return Ok(smallvec![Value::bool(false)]), + }; + + context.charge(gas_params.crypto_secp256k1_per_sig_verify * NumArgs::one())?; + Ok(smallvec![Value::bool(verify(&msg, &sig, &pk))]) +} + pub fn native_recover_public_key( context: &mut SafeNativeContext, _ty_args: Vec, @@ -102,7 +154,7 @@ pub fn native_recover_public_key( use rand_core::OsRng; #[cfg(feature = "testing")] -use libsecp256k1::{sign, PublicKey, SecretKey}; +use libsecp256k1::{sign, SecretKey}; #[cfg(feature = "testing")] pub fn native_test_only_generate_keys( @@ -151,10 +203,10 @@ pub fn make_all( builder: &SafeNativeBuilder, ) -> impl Iterator + '_ { let mut natives = vec![]; - natives.extend([( - "recover_public_key_internal", - native_recover_public_key as RawSafeNative, - )]); + natives.extend([ + ("verify_internal", native_verify as RawSafeNative), + ("recover_public_key_internal", native_recover_public_key), + ]); #[cfg(feature = "testing")] natives.extend([ @@ -162,7 +214,7 @@ pub fn make_all( "generate_keys", native_test_only_generate_keys as RawSafeNative, ), - ("sign", native_test_only_sign as RawSafeNative), + ("sign", native_test_only_sign), ]); builder.make_named_natives(natives) diff --git a/precompile/binaries/minlib/collection.mv b/precompile/binaries/minlib/collection.mv index 1f8024b34a8210b4f932c76a6d047835dcf8b724..7ddcbb9c37b09bf7a3b2573d49a9000f21c4c2d5 100644 GIT binary patch delta 458 zcmXv~J!lj`6n^i`&FtLH%Pl9&zI(W7Aq^Uy zrnZ%~Q`;F&TiL#h7p$3Y_tVORx4*}T;SB_m>9#$EDM;l+y z?roHTIWzQWLVLX5)SmJhbJaaHMh8jk=|}}CS{(9IRRMmQXUfoY9d^>a8QT*5e{>5c9YN~nMqz~UoZEHd6``KziaA8R3O|?jbq@pNp z-IqbDE(HGoSApPC5W2Km!G$hd6vUNF1)b|_7H7WicfNBL=X3sjVfLl^Vlx165e(wN zdD47@2expPz98R;^~~C^_(zdrKSkDiF5l7AR&d?f2>a6gksp#z3Tv}h8JS~%FRZQi z7s)X@59D}(0-Pvq12~zKQ=QE`9w87m?JIG?AxokP7m8nou$wF30V-(^!q`tDQ!>+lo8&PTaB= z|C-BjE3v!7ACb#rI*n7&}P+Yvty diff --git a/precompile/binaries/minlib/dex.mv b/precompile/binaries/minlib/dex.mv index 6f232c4cd9928abe1264328bea832d9e4011af9a..6ce43899215cba53842cb304edbee9b6fd396486 100644 GIT binary patch delta 14 VcmX?=dMb5;jRK?EW?O|`ApkD$1wjA+ delta 14 VcmX?=dMb5;jRK>}W?O|`ApkDq1wQ}) diff --git a/precompile/binaries/minlib/initia_nft.mv b/precompile/binaries/minlib/initia_nft.mv index 6479ba0a0df9a02de1529b9320b9e1f71e7a62a4..74e59982a4cf2a8a7b862c50935de8723be62bf9 100644 GIT binary patch delta 567 zcmY*WJ#Q015Z&32ySJF(;VE3u_5B3a?nK@>FUBOzLlC}^pmNB0Ym zXb=(txu8H)=~GkF(jZaNuxEr2Q|yl3d-Hal_G{$JFejgR_Rdgx*SLq&oN?&5|<>O)k_r>WzGIc{{pw-Q6$hGF*~%p*2ITBZn$c1@GTf zQ+>w<+El{#(H#OseiVyL&M^W_u`(FNGNTw{xwIaKCZ~C#PECsvRB~mZk!e#+-7_7k zVGWZS3j<=5+6B@PL*n*(0LJB2(2RHfZp+6Y0jA|e*n^^Mg@&#nPo{iUY5&2U@=cg` scP(|qk1Hzos!U0Pq_qc><~pDM@6;*xqwPlJw%|4w0JppX0AYjv21U9*SO5S3 delta 541 zcmY+AJ#Q015Qca5>-KKOd%MnAHgSA8EZd3W_zSRfMTEqM6X*~^lxg!bph2{euA@Uj zq6N95F9?Yc zk^HpmQOmZ<8-xi?K@QbehgO^L{0MOdWE1N|Cf8`eP^va)oy;i3RGtOTS-DS6-7Wj{ z+@X@&fKJN;^6H`-kVlWM&1^!9+Q_gyXBhLaaez}F?$9(SQTZd%R{PxQGN&jsAEDuM zkN}IyrDHg(9@~-%$*RH-xviU)lLw@^zr@VTW?ssdq QJ3NdEz=N&;K!n-<0NzkJqW}N^ diff --git a/precompile/binaries/minlib/nft.mv b/precompile/binaries/minlib/nft.mv index 1ad111a9bcb4eda38f1bab54ecb839f8c759174a..3f72b4258c1d9e0d6968b559c86fda6c2801e39c 100644 GIT binary patch delta 1732 zcma)7O>7%Q6n^vOXLo02$DYlGBz5fA`E&E1rhiLm3lsu{N{xgnao`Yz)TDvpNU1Bh znH%E3p{&Fm35g?za_7XkQjriBD((mgAt5e3@Wx5iP!x%gJ@&l$-uJ$F^WN_F_0Jm{ zUyc7}6aZKTIp$~h)h|&z!iIc|KVtMi{;J|{ZBsq4t?D24Q>FjTF4^yf?&fHZJ#hT?rQRhKg7W6LA8psTr_VutrnH08Cp1oUsO?Sz5Ao>VTiD zAc#4iF%a`ZIRm*ci~s{Fwm1@8mxAl^7y?^4A%Lyc5acP5fm{^`e2x6!bUFrob|$R> zjHx<{<=HU9xgdp~Z;)3Z6d9gRo4_ul;jbUa92(5oV18kW)Ll#>h%e{J5MHD;yfiBS zUye!gl_294b&`Bvog>EedD?l+ENoGcyl)qS@iUoa`MlPjB##vaY*f7fv z2?(#5GD-QE4WDpCXlVNg??E70p2IbcQr+LhZ`ieHd%v^Q+uIl2tq(hu!J@O-eXqB9 zr_;IVkF59JeZRBalY1X^JNvS;zrVL%x!c{k+q>22_I9_odYzl)Wie8|BkJYXVzzuJ zz9>Hyi{(xES^1~@g+G+Z{fA26c}-OlhF-tEabt7i?d$7rP2ITFX>M=bzP;PsY2Ml0 zp$N_0JI(H1uSxXHLCEr__~-t%2G2ArfWFKsryTdfOB=b!#s+O-xYb5jW2Ci&K|}bK zsLM$cGxa30kJ?WA*d;zu5FekkQiP6Sp+@%(5)qtrkac2rHHcf1OY6@X9<4xu(a*K#fgjMPi zKAIV7M#l$@T=*dQ$#nr~0}bVW4rOimFKXd4nxclMR&+Fbu!Y#`+); z$JS0jJuMxEi~l=seR?k}1$oK=ogAs-mQj#lxuz+R>XV>=#*Cn+96h-VV@e#DLXS|T zt>hL{jCkQvqzYZh3+fR(6*wv9a~#)#nAQ4Web|53{Xs|itgO2RJ6&FM^K7BqaSLp% HEZyQiz)GdM delta 1640 zcma)6J#1V>5T2R+dGGD++q-@D{FnQ&&-VHJbN(fe3n2kweu9KZNrj9P9|eR&GM1n_ z+JGqF(a}IfjUozYpr%4XLP&_hH4>CaRLpy}r34DIcbdDMo%!~g*>Crw&UfvbU$lO= z8UP$ZPWeS~<3o}^QA_(-CmkIHzZ(bWiTWP>fgaETK0nNn;MDs}kxL_=ZMJEA!(qk-^Y6P+$G6cEY$Pv`a z1Ocpii7~ECv5f|tCMpN4&k*3o1y0^<5~wYif!dS=Vw+K5$IJjeW|{!w8Q;cOT`Vm; z?mKLsFmr&H3~vdcax^NNRd%?*953q*#8+}cf~R;6SC=HvHO0iAE*U&iMLz478R7a0 zQ`lYQ_2=R>NM2YcF7!oa12^V30bbhQ-hm{wbyu(DA#LV9Z{^Lj)2($E7gy4afz~dT z<;Nx%o0zM#fZCxV9ZUYAA=1temj4A?i;h$|i6gWc6Da=1UN18gTV8w=OF0hYpg5{b zahY}yAKaxcaiezoaI}9k+PnM4(cU|w(LwukelWT-I@-T=hgbIB9v$r6yY=Si_EGV= zoYHD^czExycvrTIm(`&7R4&C)mBr3)b{a?R)P9A@tKMr9BWBMEBR%-Zy=yY zF9JDj!RjDfBaZWfQZgc=ViiHS1+pE!h}#bmE1e`#vqh z_=L*QlLNI~29yde2M3rqM6N#ei3_%7Lx_jqGLyN6v5tH_AOkln*;+6*v&LGRX2RDU zxSVO`>%zMs%GYe{CQ>&fH)$vAWJuK>?_(qK2q>mA%NNGuP&hKQB4omSXBAW<)ccmk zYoZSMP!ek!MQhqZqsj;kiy<$w!UUI0VN$Ujh=xEX&84ayfHFS~XvznmZLtx*WfxYrCFnkD%{tHHDU`wZ`Qdgltl8?GS%Nke3Zn`km z{fJ5!&#YKQGBDq+Y^sh<&Q!77r&iU7bG36K9Y?GGyKiHDyVpv3+5=Zkfx>{wXKABp zxRLt(QxZL?Rm!i8Mbu>l<=`vBN?T`5rWA=`ni4B?Js+l)XtTuRp05bnD#>iMPxWCQ d)&0r+4E5qIekZq!5Bw@F7hn4|+$xU!;2$AYk5T{t diff --git a/precompile/binaries/minlib/object.mv b/precompile/binaries/minlib/object.mv index 57356452cb90dae86ef2dcb7c2f0dea3efb21598..f9942a005ccf885cf272a37624af86d25ea04773 100644 GIT binary patch delta 1006 zcmZ8fJ8u&~5T2dg+dJQ`&v)i*@^Bo-2EURxxi}9K-jMJNk0gMU3L*pr5+I@U6d*cK zqHQQ>Ku{skga%PSMUfvs0UZqz2#Fs+!EAUcS9~}7&3xa??cAsGlgiTL!dE2$AYhQk z{1`v>48=FhJ9qeJ=6(_PeD8Zu3Rg4FrT;p&s2=1|ujZfX^viNNcwZKo4`o5>92B7f zRj5M~Mj(MUOsy?Fx(%&v4elsacw;?+L24WvU=xl5sY&PszrzJX*j3a3)BR+#yCi_^ zp)~BZw)-sXmnDD$atFXcxf9?}xe9Pt_5#d!1hcXMa0D2@(I#Ua92*3}<6z?x^AhQG z(zQ5e)9jS>K0PdmIMX8Yvliz22^JE8o(GF#6rV#Y=hDw)Gmy{q6d0Gh$$Lgd&0DtM zamVoynX{h7H>je^x2`O&T)BAT>dN|tc!z>hWY*l``V052sBoI6O9f0}q&&^FP@sh3 zk;a(WQQatKruB&I8}eDS-Q3h&va3d6x4ExpkMvqOq!kNgtho;)END&^ocuHv=)2<} zfDYG~#(;HrN2D?NF_WWo7)97eJ=_7)m9A8aHO02M0fan`x-|x>G%D;3!@_| LYetjeJY0aEn{Q}w delta 935 zcmZ8fxo#9e5Ur}|={b6Kc4nEicWuvl4|^}$d-+=K(_oA_d_GGZx>H z_(=oiANr2gH*x6LU;JWl;O|OzE4!xlv*aCQUwWxurLwm*In>NEOSp!%A1h`#CwBb$z7#BgucO@|{FIjrIr;l-2d*7cB zOgxxm^M@K9#SANLLADL69gf#XryJ5uXVQ?(=7-RbM#IQYwUbY{x;IK6Y41=l>{&a) zMqSYgqvVCxmcx@Sh6Bk5FDD0U9OjbG-a>K6(3+IOKt`V7f-=Hk&rrN73OyQ5I;o-@ zVIylad7P?L(Me#*-(0qs~1-;6yG4 z81cpIC}Yx?&M=uL?wiK7xSH(x&CMgR&}8+jsY^9Y@oO|Js_PW((3c8J)X*nF!8}CN zqdGa}wiX~Sl+o?cyP)m&;=#-u}vCM@pSBXioE zF$ZmGqd*HBAGy%!TsqX%w02g=bLvn>o3)AxLYGI|OMa)j@$^Y5&9k8%DJrAy(u|gN zlZBv)aq>Kf8DBBZChvo~?D0*kUh*~QGHr)bS#AyaUM;gL`S&}SPqGy1v}fTQt;1gp C5Mew3 diff --git a/precompile/binaries/minlib/secp256k1.mv b/precompile/binaries/minlib/secp256k1.mv index eb513aec4c5298dd1050cdd172bbabad4e8a6041..e94c009ea467a3b141469c032e1fbc3dce5414d9 100644 GIT binary patch delta 500 zcmZ`#y-plK4E}7--_Fe5?H+qTek7oXprbooNl2YCWt5~iB&0xrP^46OgGgvafl~1T zNC@76jy}|Mv^)Twg_QBgmOtB{$M$7)5r3V(eMk;KA~<#vS^a1h!5$p=JGz$e+paek z_Kf0NEDYh7>Y~*C+DZ${a76B@BQpQ(@Hab z_P^{O9xsj#zkTm#w0U(*_q6L?kMN$|y3Sf;!#G@4iGbvBUFVutxSABUOe^ip1}7hf6xC?RlY-27C|qrW^W-ss-NjqF!Ha!2VpGW9-Ld1vcH*_y>s`l*osGv%ymo>Efe=WX1QL*t5Xu!K z4v1UCCs-pmaO1!ighMVIxgddrxa5HN0TgcBQ@sun$*5IT^ZM1R>gs;B@?C%YefMvQ zgb)OxgPrU0<9|{8At(A+{VB~e^MvdV#bWkz@w-gEE&J}L^0?Xf?Rb1IzLE&@ek9EO zzXZ{G@hd2O4+ z+b7JUDlqR%nRkOV_sSh%KG?k^%!A7y%!j)*;Ns$$?$Vidp}U^iw5Hur#2&UKJ``Z;v^n`TGhjd$1(kU)Tej|E>H3dTWV}wp?5frTCSi%9Z(MT zh_OS;GkJpDVA18Oa4mhaYPnY0^ee7SEo$c?&75!@Zl$v>NK-_{_tPe0eCt&DRZ5ZT z0}Si6<}$SFTg{5;=!ZB0@qc)}e%hStmAti%X9Z^zaX6gMoMI&)Rn6eW%S!Y0N7<=H zP&5OmNSGe;^JM-}_UV5dKuI%C- z%MQw@g_F<@@KGyQy@n9}CiSS88SJ|xP5c2rge&Z?rg1foLICn83};Bte6awLcn<44 BYaajr delta 920 zcmZXSxo#9e5QeMzn5%kcr)T!?vH0BedVSv>U)T!2?=BkA^-`= zPXNdXDG3omA|oY6fW!kJy4NPLXQY~%ul}m)uI^X$p98b|l^^5)03noEX@lSU#l&a6 zET4!^-2LLdBkynA6TL~k^5A!=u3na(=5zadv)le!0PeN}?w!5t!!B#|mO!lZ`QV=R zC3lE?YUjGK2E~SFN6}&l=^mL*?~3tGv-t-n7=eRh|d;cDexW z&LV<)H_bfO(^X9RE3%@gNDbk)`<=sLQp`-s?xo4)?zW|DgMCXJM|rM&$utQ(mq{mU z@^Tzme#l5E>3HtJL;gq{{Bd76yY&YFC@(Snkl@4^o3S!Drv79r$Q&2I*VudtX)FizX z@k6#@npD$RC2})OR1zv(Qw2(>5JQ+#po#=k90ii(kgEqmWmw6ulCBf6K@^CM>|?%$ zg{-Md+2hE|UgtYZ8q-6}oZZQ=j5@0&6CPosV6v0e#&x8Gsfr}hG8;QtwJ&EYjq^qjChY>IV}evE3^DD(^b diff --git a/precompile/binaries/stdlib/collection.mv b/precompile/binaries/stdlib/collection.mv index 1f8024b34a8210b4f932c76a6d047835dcf8b724..7ddcbb9c37b09bf7a3b2573d49a9000f21c4c2d5 100644 GIT binary patch delta 458 zcmXv~J!lj`6n^i`&FtLH%Pl9&zI(W7Aq^Uy zrnZ%~Q`;F&TiL#h7p$3Y_tVORx4*}T;SB_m>9#$EDM;l+y z?roHTIWzQWLVLX5)SmJhbJaaHMh8jk=|}}CS{(9IRRMmQXUfoY9d^>a8QT*5e{>5c9YN~nMqz~UoZEHd6``KziaA8R3O|?jbq@pNp z-IqbDE(HGoSApPC5W2Km!G$hd6vUNF1)b|_7H7WicfNBL=X3sjVfLl^Vlx165e(wN zdD47@2expPz98R;^~~C^_(zdrKSkDiF5l7AR&d?f2>a6gksp#z3Tv}h8JS~%FRZQi z7s)X@59D}(0-Pvq12~zKQ=QE`9w87m?JIG?AxokP7m8nou$wF30V-(^!q`tDQ!>+lo8&PTaB= z|C-BjE3v!7ACb#rI*n7&}P+Yvty diff --git a/precompile/binaries/stdlib/dex.mv b/precompile/binaries/stdlib/dex.mv index 6f232c4cd9928abe1264328bea832d9e4011af9a..6ce43899215cba53842cb304edbee9b6fd396486 100644 GIT binary patch delta 14 VcmX?=dMb5;jRK?EW?O|`ApkD$1wjA+ delta 14 VcmX?=dMb5;jRK>}W?O|`ApkDq1wQ}) diff --git a/precompile/binaries/stdlib/initia_nft.mv b/precompile/binaries/stdlib/initia_nft.mv index 6479ba0a0df9a02de1529b9320b9e1f71e7a62a4..74e59982a4cf2a8a7b862c50935de8723be62bf9 100644 GIT binary patch delta 567 zcmY*WJ#Q015Z&32ySJF(;VE3u_5B3a?nK@>FUBOzLlC}^pmNB0Ym zXb=(txu8H)=~GkF(jZaNuxEr2Q|yl3d-Hal_G{$JFejgR_Rdgx*SLq&oN?&5|<>O)k_r>WzGIc{{pw-Q6$hGF*~%p*2ITBZn$c1@GTf zQ+>w<+El{#(H#OseiVyL&M^W_u`(FNGNTw{xwIaKCZ~C#PECsvRB~mZk!e#+-7_7k zVGWZS3j<=5+6B@PL*n*(0LJB2(2RHfZp+6Y0jA|e*n^^Mg@&#nPo{iUY5&2U@=cg` scP(|qk1Hzos!U0Pq_qc><~pDM@6;*xqwPlJw%|4w0JppX0AYjv21U9*SO5S3 delta 541 zcmY+AJ#Q015Qca5>-KKOd%MnAHgSA8EZd3W_zSRfMTEqM6X*~^lxg!bph2{euA@Uj zq6N95F9?Yc zk^HpmQOmZ<8-xi?K@QbehgO^L{0MOdWE1N|Cf8`eP^va)oy;i3RGtOTS-DS6-7Wj{ z+@X@&fKJN;^6H`-kVlWM&1^!9+Q_gyXBhLaaez}F?$9(SQTZd%R{PxQGN&jsAEDuM zkN}IyrDHg(9@~-%$*RH-xviU)lLw@^zr@VTW?ssdq QJ3NdEz=N&;K!n-<0NzkJqW}N^ diff --git a/precompile/binaries/stdlib/nft.mv b/precompile/binaries/stdlib/nft.mv index 1ad111a9bcb4eda38f1bab54ecb839f8c759174a..3f72b4258c1d9e0d6968b559c86fda6c2801e39c 100644 GIT binary patch delta 1732 zcma)7O>7%Q6n^vOXLo02$DYlGBz5fA`E&E1rhiLm3lsu{N{xgnao`Yz)TDvpNU1Bh znH%E3p{&Fm35g?za_7XkQjriBD((mgAt5e3@Wx5iP!x%gJ@&l$-uJ$F^WN_F_0Jm{ zUyc7}6aZKTIp$~h)h|&z!iIc|KVtMi{;J|{ZBsq4t?D24Q>FjTF4^yf?&fHZJ#hT?rQRhKg7W6LA8psTr_VutrnH08Cp1oUsO?Sz5Ao>VTiD zAc#4iF%a`ZIRm*ci~s{Fwm1@8mxAl^7y?^4A%Lyc5acP5fm{^`e2x6!bUFrob|$R> zjHx<{<=HU9xgdp~Z;)3Z6d9gRo4_ul;jbUa92(5oV18kW)Ll#>h%e{J5MHD;yfiBS zUye!gl_294b&`Bvog>EedD?l+ENoGcyl)qS@iUoa`MlPjB##vaY*f7fv z2?(#5GD-QE4WDpCXlVNg??E70p2IbcQr+LhZ`ieHd%v^Q+uIl2tq(hu!J@O-eXqB9 zr_;IVkF59JeZRBalY1X^JNvS;zrVL%x!c{k+q>22_I9_odYzl)Wie8|BkJYXVzzuJ zz9>Hyi{(xES^1~@g+G+Z{fA26c}-OlhF-tEabt7i?d$7rP2ITFX>M=bzP;PsY2Ml0 zp$N_0JI(H1uSxXHLCEr__~-t%2G2ArfWFKsryTdfOB=b!#s+O-xYb5jW2Ci&K|}bK zsLM$cGxa30kJ?WA*d;zu5FekkQiP6Sp+@%(5)qtrkac2rHHcf1OY6@X9<4xu(a*K#fgjMPi zKAIV7M#l$@T=*dQ$#nr~0}bVW4rOimFKXd4nxclMR&+Fbu!Y#`+); z$JS0jJuMxEi~l=seR?k}1$oK=ogAs-mQj#lxuz+R>XV>=#*Cn+96h-VV@e#DLXS|T zt>hL{jCkQvqzYZh3+fR(6*wv9a~#)#nAQ4Web|53{Xs|itgO2RJ6&FM^K7BqaSLp% HEZyQiz)GdM delta 1640 zcma)6J#1V>5T2R+dGGD++q-@D{FnQ&&-VHJbN(fe3n2kweu9KZNrj9P9|eR&GM1n_ z+JGqF(a}IfjUozYpr%4XLP&_hH4>CaRLpy}r34DIcbdDMo%!~g*>Crw&UfvbU$lO= z8UP$ZPWeS~<3o}^QA_(-CmkIHzZ(bWiTWP>fgaETK0nNn;MDs}kxL_=ZMJEA!(qk-^Y6P+$G6cEY$Pv`a z1Ocpii7~ECv5f|tCMpN4&k*3o1y0^<5~wYif!dS=Vw+K5$IJjeW|{!w8Q;cOT`Vm; z?mKLsFmr&H3~vdcax^NNRd%?*953q*#8+}cf~R;6SC=HvHO0iAE*U&iMLz478R7a0 zQ`lYQ_2=R>NM2YcF7!oa12^V30bbhQ-hm{wbyu(DA#LV9Z{^Lj)2($E7gy4afz~dT z<;Nx%o0zM#fZCxV9ZUYAA=1temj4A?i;h$|i6gWc6Da=1UN18gTV8w=OF0hYpg5{b zahY}yAKaxcaiezoaI}9k+PnM4(cU|w(LwukelWT-I@-T=hgbIB9v$r6yY=Si_EGV= zoYHD^czExycvrTIm(`&7R4&C)mBr3)b{a?R)P9A@tKMr9BWBMEBR%-Zy=yY zF9JDj!RjDfBaZWfQZgc=ViiHS1+pE!h}#bmE1e`#vqh z_=L*QlLNI~29yde2M3rqM6N#ei3_%7Lx_jqGLyN6v5tH_AOkln*;+6*v&LGRX2RDU zxSVO`>%zMs%GYe{CQ>&fH)$vAWJuK>?_(qK2q>mA%NNGuP&hKQB4omSXBAW<)ccmk zYoZSMP!ek!MQhqZqsj;kiy<$w!UUI0VN$Ujh=xEX&84ayfHFS~XvznmZLtx*WfxYrCFnkD%{tHHDU`wZ`Qdgltl8?GS%Nke3Zn`km z{fJ5!&#YKQGBDq+Y^sh<&Q!77r&iU7bG36K9Y?GGyKiHDyVpv3+5=Zkfx>{wXKABp zxRLt(QxZL?Rm!i8Mbu>l<=`vBN?T`5rWA=`ni4B?Js+l)XtTuRp05bnD#>iMPxWCQ d)&0r+4E5qIekZq!5Bw@F7hn4|+$xU!;2$AYk5T{t diff --git a/precompile/binaries/stdlib/object.mv b/precompile/binaries/stdlib/object.mv index 57356452cb90dae86ef2dcb7c2f0dea3efb21598..f9942a005ccf885cf272a37624af86d25ea04773 100644 GIT binary patch delta 1006 zcmZ8fJ8u&~5T2dg+dJQ`&v)i*@^Bo-2EURxxi}9K-jMJNk0gMU3L*pr5+I@U6d*cK zqHQQ>Ku{skga%PSMUfvs0UZqz2#Fs+!EAUcS9~}7&3xa??cAsGlgiTL!dE2$AYhQk z{1`v>48=FhJ9qeJ=6(_PeD8Zu3Rg4FrT;p&s2=1|ujZfX^viNNcwZKo4`o5>92B7f zRj5M~Mj(MUOsy?Fx(%&v4elsacw;?+L24WvU=xl5sY&PszrzJX*j3a3)BR+#yCi_^ zp)~BZw)-sXmnDD$atFXcxf9?}xe9Pt_5#d!1hcXMa0D2@(I#Ua92*3}<6z?x^AhQG z(zQ5e)9jS>K0PdmIMX8Yvliz22^JE8o(GF#6rV#Y=hDw)Gmy{q6d0Gh$$Lgd&0DtM zamVoynX{h7H>je^x2`O&T)BAT>dN|tc!z>hWY*l``V052sBoI6O9f0}q&&^FP@sh3 zk;a(WQQatKruB&I8}eDS-Q3h&va3d6x4ExpkMvqOq!kNgtho;)END&^ocuHv=)2<} zfDYG~#(;HrN2D?NF_WWo7)97eJ=_7)m9A8aHO02M0fan`x-|x>G%D;3!@_| LYetjeJY0aEn{Q}w delta 935 zcmZ8fxo#9e5Ur}|={b6Kc4nEicWuvl4|^}$d-+=K(_oA_d_GGZx>H z_(=oiANr2gH*x6LU;JWl;O|OzE4!xlv*aCQUwWxurLwm*In>NEOSp!%A1h`#CwBb$z7#BgucO@|{FIjrIr;l-2d*7cB zOgxxm^M@K9#SANLLADL69gf#XryJ5uXVQ?(=7-RbM#IQYwUbY{x;IK6Y41=l>{&a) zMqSYgqvVCxmcx@Sh6Bk5FDD0U9OjbG-a>K6(3+IOKt`V7f-=Hk&rrN73OyQ5I;o-@ zVIylad7P?L(Me#*-(0qs~1-;6yG4 z81cpIC}Yx?&M=uL?wiK7xSH(x&CMgR&}8+jsY^9Y@oO|Js_PW((3c8J)X*nF!8}CN zqdGa}wiX~Sl+o?cyP)m&;=#-u}vCM@pSBXioE zF$ZmGqd*HBAGy%!TsqX%w02g=bLvn>o3)AxLYGI|OMa)j@$^Y5&9k8%DJrAy(u|gN zlZBv)aq>Kf8DBBZChvo~?D0*kUh*~QGHr)bS#AyaUM;gL`S&}SPqGy1v}fTQt;1gp C5Mew3 diff --git a/precompile/binaries/stdlib/secp256k1.mv b/precompile/binaries/stdlib/secp256k1.mv index eb513aec4c5298dd1050cdd172bbabad4e8a6041..e94c009ea467a3b141469c032e1fbc3dce5414d9 100644 GIT binary patch delta 500 zcmZ`#y-plK4E}7--_Fe5?H+qTek7oXprbooNl2YCWt5~iB&0xrP^46OgGgvafl~1T zNC@76jy}|Mv^)Twg_QBgmOtB{$M$7)5r3V(eMk;KA~<#vS^a1h!5$p=JGz$e+paek z_Kf0NEDYh7>Y~*C+DZ${a76B@BQpQ(@Hab z_P^{O9xsj#zkTm#w0U(*_q6L?kMN$|y3Sf;!#G@4iGbvBUFVutxSABUOe^ip1}7hf6xC?RlY-27C|qrW^W-ss-NjqF!Ha!2VpGW9-Ld1vcH*_y>s`l*osGv%ymo>Efe=WX1QL*t5Xu!K z4v1UCCs-pmaO1!ighMVIxgddrxa5HN0TgcBQ@sun$*5IT^ZM1R>gs;B@?C%YefMvQ zgb)OxgPrU0<9|{8At(A+{VB~e^MvdV#bWkz@w-gEE&J}L^0?Xf?Rb1IzLE&@ek9EO zzXZ{G@hd2O4+ z+b7JUDlqR%nRkOV_sSh%KG?k^%!A7y%!j)*;Ns$$?$Vidp}U^iw5Hur#2&UKJ``Z;v^n`TGhjd$1(kU)Tej|E>H3dTWV}wp?5frTCSi%9Z(MT zh_OS;GkJpDVA18Oa4mhaYPnY0^ee7SEo$c?&75!@Zl$v>NK-_{_tPe0eCt&DRZ5ZT z0}Si6<}$SFTg{5;=!ZB0@qc)}e%hStmAti%X9Z^zaX6gMoMI&)Rn6eW%S!Y0N7<=H zP&5OmNSGe;^JM-}_UV5dKuI%C- z%MQw@g_F<@@KGyQy@n9}CiSS88SJ|xP5c2rge&Z?rg1foLICn83};Bte6awLcn<44 BYaajr delta 920 zcmZXSxo#9e5QeMzn5%kcr)T!?vH0BedVSv>U)T!2?=BkA^-`= zPXNdXDG3omA|oY6fW!kJy4NPLXQY~%ul}m)uI^X$p98b|l^^5)03noEX@lSU#l&a6 zET4!^-2LLdBkynA6TL~k^5A!=u3na(=5zadv)le!0PeN}?w!5t!!B#|mO!lZ`QV=R zC3lE?YUjGK2E~SFN6}&l=^mL*?~3tGv-t-n7=eRh|d;cDexW z&LV<)H_bfO(^X9RE3%@gNDbk)`<=sLQp`-s?xo4)?zW|DgMCXJM|rM&$utQ(mq{mU z@^Tzme#l5E>3HtJL;gq{{Bd76yY&YFC@(Snkl@4^o3S!Drv79r$Q&2I*VudtX)FizX z@k6#@npD$RC2})OR1zv(Qw2(>5JQ+#po#=k90ii(kgEqmWmw6ulCBf6K@^CM>|?%$ zg{-Md+2hE|UgtYZ8q-6}oZZQ=j5@0&6CPosV6v0e#&x8Gsfr}hG8;QtwJ&EYjq^qjChY>IV}evE3^DD(^b diff --git a/precompile/modules/initia_stdlib/sources/block.move b/precompile/modules/initia_stdlib/sources/block.move index 17261937..f7475c76 100644 --- a/precompile/modules/initia_stdlib/sources/block.move +++ b/precompile/modules/initia_stdlib/sources/block.move @@ -45,7 +45,7 @@ module initia_std::block { vm: &signer, _fake_block_hash: address ) { if (!exists(signer::address_of(vm))) { - move_to(vm, HasGenesisBlock{}); + move_to(vm, HasGenesisBlock {}); return }; diff --git a/precompile/modules/initia_stdlib/sources/crypto/secp256k1.move b/precompile/modules/initia_stdlib/sources/crypto/secp256k1.move index aa0ccdfd..3ac13338 100644 --- a/precompile/modules/initia_stdlib/sources/crypto/secp256k1.move +++ b/precompile/modules/initia_stdlib/sources/crypto/secp256k1.move @@ -91,6 +91,24 @@ module initia_std::secp256k1 { sig.bytes } + /// Returns `true` if the signature can verify the public key on the message + public fun verify( + message: vector, + public_key: &ECDSACompressedPublicKey, + signature: &ECDSASignature + ): bool { + assert!( + std::vector::length(&message) == MESSAGE_SIZE, + std::error::invalid_argument(E_DESERIALIZE) + ); + + return verify_internal( + message, + public_key.bytes, + signature.bytes + ) + } + /// Recovers the signer's raw (64-byte) public key from a secp256k1 ECDSA `signature` given the `recovery_id` and the signed /// `message` (32 byte digest). /// @@ -151,6 +169,18 @@ module initia_std::secp256k1 { // Native functions // + /// Returns `true` if `signature` verifies on `public_key` and `message` + /// and returns `false` otherwise. + /// + /// - `message`: A 32-byte hashed message. + /// - `public_key`: A compressed public key in bytes. + /// - `signature`: A 64-byte ECDSA signature. + native fun verify_internal( + message: vector, + public_key: vector, + signature: vector + ): bool; + /// Returns `(public_key, true)` if `signature` verifies on `message` under the recovered `public_key` /// and returns `([], false)` otherwise. native fun recover_public_key_internal( @@ -172,6 +202,29 @@ module initia_std::secp256k1 { // Tests // + #[test] + fun test_secp256k1_sign_verify() { + use std::hash; + + let (sk, vk) = generate_keys(true); + let pk = ecdsa_compressed_public_key_from_bytes(vk); + + let msg: vector = hash::sha2_256(b"test initia secp256k1"); + let (_rid, sig_bytes) = sign(msg, sk); + let sig = ecdsa_signature_from_bytes(sig_bytes); + assert!(verify(msg, &pk, &sig), 1); + + // Test with an incorrect message + let wrong_msg: vector = hash::sha2_256(b"wrong message"); + assert!(!verify(wrong_msg, &pk, &sig), 2); + + // Test with an incorrect signature + let invalid_sig_bytes = sig_bytes; + *std::vector::borrow_mut(&mut invalid_sig_bytes, 0) = 0xFF; // Corrupt the signature + let invalid_sig = ecdsa_signature_from_bytes(invalid_sig_bytes); + assert!(!verify(msg, &pk, &invalid_sig), 3); + } + #[test] fun test_gen_sign_recover() { use std::hash; diff --git a/precompile/modules/initia_stdlib/sources/dex.move b/precompile/modules/initia_stdlib/sources/dex.move index 03cf483f..2910bed3 100644 --- a/precompile/modules/initia_stdlib/sources/dex.move +++ b/precompile/modules/initia_stdlib/sources/dex.move @@ -253,7 +253,7 @@ module initia_std::dex { let base_addr = object::object_address(&base_coin); assert!( base_addr == pair_key.coin_a || base_addr == pair_key.coin_b, - error::invalid_argument(ECOIN_TYPE), + error::invalid_argument(ECOIN_TYPE) ); let is_base_a = base_addr == pair_key.coin_a; let (base_pool, quote_pool, base_weight, quote_weight) = @@ -265,7 +265,7 @@ module initia_std::dex { bigdecimal::div( bigdecimal::mul_by_u64(base_weight, quote_pool), - bigdecimal::mul_by_u64(quote_weight, base_pool), + bigdecimal::mul_by_u64(quote_weight, base_pool) ) } @@ -287,7 +287,7 @@ module initia_std::dex { let offer_address = object::object_address(&offer_metadata); assert!( offer_address == pair_key.coin_a || offer_address == pair_key.coin_b, - error::invalid_argument(ECOIN_TYPE), + error::invalid_argument(ECOIN_TYPE) ); let is_offer_a = offer_address == pair_key.coin_a; let (pool_a, pool_b, weight_a, weight_b, swap_fee_rate) = pool_info(pair, true); @@ -304,7 +304,7 @@ module initia_std::dex { offer_weight, return_weight, offer_amount, - swap_fee_rate, + swap_fee_rate ); return_amount @@ -319,7 +319,7 @@ module initia_std::dex { get_swap_simulation( object::convert(pair_metadata), offer_metadata, - offer_amount, + offer_amount ) } @@ -332,7 +332,7 @@ module initia_std::dex { let offer_address = object::object_address(&offer_metadata); assert!( offer_address == pair_key.coin_a || offer_address == pair_key.coin_b, - error::invalid_argument(ECOIN_TYPE), + error::invalid_argument(ECOIN_TYPE) ); let is_offer_a = offer_address == pair_key.coin_a; let (pool_a, pool_b, weight_a, weight_b, swap_fee_rate) = pool_info(pair, true); @@ -349,7 +349,7 @@ module initia_std::dex { offer_weight, return_weight, return_amount, - swap_fee_rate, + swap_fee_rate ); offer_amount @@ -364,7 +364,7 @@ module initia_std::dex { get_swap_simulation_given_out( object::convert(pair_metadata), offer_metadata, - return_amount, + return_amount ) } @@ -372,7 +372,7 @@ module initia_std::dex { public fun get_provide_simulation( pair: Object, coin_a_amount_in: u64, - coin_b_amount_in: u64, + coin_b_amount_in: u64 ): u64 acquires Pool { let pool_addr = object::object_address(&pair); let pool = borrow_global(pool_addr); @@ -441,7 +441,9 @@ module initia_std::dex { } #[view] - public fun get_current_weight_by_denom(pair_denom: String): CurrentWeightResponse acquires Config { + public fun get_current_weight_by_denom( + pair_denom: String + ): CurrentWeightResponse acquires Config { let pair_metadata = coin::denom_to_metadata(pair_denom); get_current_weight(object::convert(pair_metadata)) } @@ -460,10 +462,11 @@ module initia_std::dex { }; assert!( - option::is_some(&coin_a_start_after) == option::is_some(&coin_b_start_after) + option::is_some(&coin_a_start_after) + == option::is_some(&coin_b_start_after) && option::is_some(&coin_b_start_after) == option::is_some(&liquidity_token_start_after), - ESTART_AFTER, + ESTART_AFTER ); let module_store = borrow_global(@initia_std); @@ -475,7 +478,7 @@ module initia_std::dex { coin_a: option::extract(&mut coin_a_start_after), coin_b: option::extract(&mut coin_b_start_after), liquidity_token: option::extract(&mut liquidity_token_start_after) - }, + } ) } else { option::some( @@ -488,11 +491,11 @@ module initia_std::dex { &module_store.pairs, start_after, option::none(), - 1, + 1 ); while (vector::length(&res) < (limit as u64) - && table::prepare(pairs_iter)) { + && table::prepare(pairs_iter)) { let (key, value) = table::next(pairs_iter); if (&key != option::borrow(&start_after)) { vector::push_back(&mut res, *value) @@ -516,10 +519,11 @@ module initia_std::dex { }; assert!( - option::is_some(&coin_a_start_after) == option::is_some(&coin_b_start_after) + option::is_some(&coin_a_start_after) + == option::is_some(&coin_b_start_after) && option::is_some(&coin_b_start_after) == option::is_some(&liquidity_token_start_after), - ESTART_AFTER, + ESTART_AFTER ); let module_store = borrow_global(@initia_std); @@ -541,7 +545,7 @@ module initia_std::dex { liquidity_token: object::object_address( &liquidity_token_start_after ) - }, + } ) } else { option::some( @@ -554,11 +558,11 @@ module initia_std::dex { &module_store.pairs, start_after, option::none(), - 1, + 1 ); while (vector::length(&res) < (limit as u64) - && table::prepare(pairs_iter)) { + && table::prepare(pairs_iter)) { let (key, value) = table::next(pairs_iter); if (&key != option::borrow(&start_after)) { vector::push_back( @@ -575,7 +579,7 @@ module initia_std::dex { ), weights: value.weights, swap_fee_rate: value.swap_fee_rate - }, + } ) } }; @@ -605,7 +609,7 @@ module initia_std::dex { coin_a, coin_b, liquidity_token: option::extract(&mut start_after) - }, + } ) } else { option::some(PairKey { coin_a, coin_b, liquidity_token: @0x0 }) @@ -616,14 +620,14 @@ module initia_std::dex { &module_store.pairs, start_after, option::none(), - 1, + 1 ); while (vector::length(&res) < (limit as u64) - && table::prepare(pairs_iter)) { + && table::prepare(pairs_iter)) { let (key, value) = table::next(pairs_iter); if (coin_a != key.coin_a || coin_b != key.coin_b) - break; + break; if (&key != option::borrow(&start_after)) { vector::push_back(&mut res, *value) } @@ -680,10 +684,9 @@ module initia_std::dex { weight.timestamp } - public fun unpack_pair_response(pair_response: &PairResponse) - : ( - address, address, address, Weights, BigDecimal - ) { + public fun unpack_pair_response( + pair_response: &PairResponse + ): (address, address, address, Weights, BigDecimal) { ( pair_response.coin_a, pair_response.coin_b, @@ -703,14 +706,17 @@ module initia_std::dex { fun check_chain_permission(chain: &signer) { assert!( signer::address_of(chain) == @initia_std, - error::permission_denied(EUNAUTHORIZED), + error::permission_denied(EUNAUTHORIZED) ); } fun init_module(chain: &signer) { move_to( chain, - ModuleStore { pairs: table::new(), pair_count: 0 }, + ModuleStore { + pairs: table::new(), + pair_count: 0 + } ); } @@ -735,12 +741,12 @@ module initia_std::dex { let coin_a = coin::withdraw( creator, coin_a_metadata, - coin_a_amount, + coin_a_amount ); let coin_b = coin::withdraw( creator, coin_b_metadata, - coin_b_amount, + coin_b_amount ); let liquidity_token = @@ -751,7 +757,7 @@ module initia_std::dex { swap_fee_rate, coin_a, coin_b, - weights, + weights ); coin::deposit(signer::address_of(creator), liquidity_token); } @@ -778,11 +784,11 @@ module initia_std::dex { let (_, timestamp) = get_block_info(); assert!( start_time > timestamp, - error::invalid_argument(ELBP_START_TIME), + error::invalid_argument(ELBP_START_TIME) ); assert!( end_time > start_time, - error::invalid_argument(EWEIGHTS_TIMESTAMP), + error::invalid_argument(EWEIGHTS_TIMESTAMP) ); let weights = Weights { weights_before: Weight { @@ -800,12 +806,12 @@ module initia_std::dex { let coin_a = coin::withdraw( creator, coin_a_metadata, - coin_a_amount, + coin_a_amount ); let coin_b = coin::withdraw( creator, coin_b_metadata, - coin_b_amount, + coin_b_amount ); let liquidity_token = @@ -816,7 +822,7 @@ module initia_std::dex { swap_fee_rate, coin_a, coin_b, - weights, + weights ); coin::deposit(signer::address_of(creator), liquidity_token); } @@ -834,7 +840,7 @@ module initia_std::dex { let config = borrow_global_mut(object::object_address(&pair)); assert!( bigdecimal::le(swap_fee_rate, max_fee_rate()), - error::invalid_argument(EOUT_OF_SWAP_FEE_RATE_RANGE), + error::invalid_argument(EOUT_OF_SWAP_FEE_RATE_RANGE) ); config.swap_fee_rate = swap_fee_rate; @@ -853,7 +859,7 @@ module initia_std::dex { coin_b: pair_key.coin_b, liquidity_token: pair_key.liquidity_token, swap_fee_rate - }, + } ); } @@ -870,7 +876,7 @@ module initia_std::dex { pair, coin_a_amount_in, coin_b_amount_in, - min_liquidity, + min_liquidity ); } @@ -895,12 +901,12 @@ module initia_std::dex { coin::withdraw( account, fungible_asset::store_metadata(pool.coin_a_store), - coin_a_amount_in, + coin_a_amount_in ), coin::withdraw( account, fungible_asset::store_metadata(pool.coin_b_store), - coin_b_amount_in, + coin_b_amount_in ) ) } else { @@ -922,12 +928,12 @@ module initia_std::dex { coin::withdraw( account, fungible_asset::store_metadata(pool.coin_a_store), - coin_a_amount_in, + coin_a_amount_in ), coin::withdraw( account, fungible_asset::store_metadata(pool.coin_b_store), - coin_b_amount_in, + coin_b_amount_in ) ) }; @@ -950,7 +956,7 @@ module initia_std::dex { ) acquires CoinCapabilities, Config, Pool { assert!( liquidity != 0, - error::invalid_argument(EZERO_LIQUIDITY), + error::invalid_argument(EZERO_LIQUIDITY) ); let addr = signer::address_of(account); @@ -958,13 +964,13 @@ module initia_std::dex { coin::withdraw( account, object::convert(pair), - liquidity, + liquidity ); let (coin_a, coin_b) = withdraw_liquidity( liquidity_token, min_coin_a_amount, - min_coin_b_amount, + min_coin_b_amount ); coin::deposit(addr, coin_a); @@ -982,7 +988,7 @@ module initia_std::dex { let offer_coin = coin::withdraw( account, offer_coin, - offer_coin_amount, + offer_coin_amount ); let return_coin = swap(pair, offer_coin); @@ -991,7 +997,7 @@ module initia_std::dex { || *option::borrow(&min_return) <= fungible_asset::amount( &return_coin ), - error::invalid_state(EMIN_RETURN), + error::invalid_state(EMIN_RETURN) ); coin::deposit(signer::address_of(account), return_coin); @@ -1027,7 +1033,7 @@ module initia_std::dex { option::extract( &mut fungible_asset::supply( fungible_asset::metadata_from_asset(&lp_token) - ), + ) ); let coin_a_amount = fungible_asset::balance(pool.coin_a_store); let given_token_amount = fungible_asset::amount(&lp_token); @@ -1043,19 +1049,19 @@ module initia_std::dex { assert!( option::is_none(&min_coin_a_amount) || *option::borrow(&min_coin_a_amount) <= coin_a_amount_out, - error::invalid_state(EMIN_WITHDRAW), + error::invalid_state(EMIN_WITHDRAW) ); assert!( option::is_none(&min_coin_b_amount) || *option::borrow(&min_coin_b_amount) <= coin_b_amount_out, - error::invalid_state(EMIN_WITHDRAW), + error::invalid_state(EMIN_WITHDRAW) ); // burn liquidity token let liquidity_token_capabilities = borrow_global(pair_addr); coin::burn( &liquidity_token_capabilities.burn_cap, - lp_token, + lp_token ); // emit events @@ -1068,7 +1074,7 @@ module initia_std::dex { coin_a_amount: coin_a_amount_out, coin_b_amount: coin_b_amount_out, liquidity: given_token_amount - }, + } ); let pool = borrow_global_mut(pair_addr); @@ -1078,12 +1084,12 @@ module initia_std::dex { fungible_asset::withdraw( pair_signer, pool.coin_a_store, - coin_a_amount_out, + coin_a_amount_out ), fungible_asset::withdraw( pair_signer, pool.coin_b_store, - coin_b_amount_out, + coin_b_amount_out ) ) } @@ -1120,7 +1126,7 @@ module initia_std::dex { assert!( option::is_none(&min_liquidity_amount) || *option::borrow(&min_liquidity_amount) <= liquidity, - error::invalid_state(EMIN_LIQUIDITY), + error::invalid_state(EMIN_LIQUIDITY) ); // emit events @@ -1133,26 +1139,28 @@ module initia_std::dex { provide_amount, fee_amount, liquidity - }, + } ); // mint liquidity tokens to provider let liquidity_token_capabilities = borrow_global(pair_addr); coin::mint( &liquidity_token_capabilities.mint_cap, - liquidity, + liquidity ) } /// Swap directly - public fun swap(pair: Object, offer_coin: FungibleAsset): FungibleAsset acquires Config, Pool { + public fun swap( + pair: Object, offer_coin: FungibleAsset + ): FungibleAsset acquires Config, Pool { let offer_amount = fungible_asset::amount(&offer_coin); let offer_metadata = fungible_asset::metadata_from_asset(&offer_coin); let offer_address = object::object_address(&offer_metadata); let pair_key = generate_pair_key(pair); assert!( offer_address == pair_key.coin_a || offer_address == pair_key.coin_b, - error::invalid_argument(ECOIN_TYPE), + error::invalid_argument(ECOIN_TYPE) ); let is_offer_a = offer_address == pair_key.coin_a; @@ -1177,7 +1185,7 @@ module initia_std::dex { offer_weight, return_weight, fungible_asset::amount(&offer_coin), - swap_fee_rate, + swap_fee_rate ); // apply swap result to pool @@ -1191,14 +1199,14 @@ module initia_std::dex { fungible_asset::withdraw( pair_signer, pool.coin_b_store, - return_amount, + return_amount ) } else { fungible_asset::deposit(pool.coin_b_store, offer_coin); fungible_asset::withdraw( pair_signer, pool.coin_a_store, - return_amount, + return_amount ) }; @@ -1211,7 +1219,7 @@ module initia_std::dex { fee_amount, offer_amount, return_amount - }, + } ); return_coin @@ -1224,20 +1232,20 @@ module initia_std::dex { bigdecimal::one(), bigdecimal::add( weights.weights_before.coin_a_weight, - weights.weights_before.coin_b_weight, - ), + weights.weights_before.coin_b_weight + ) ), - EINVALID_WEIGHTS, + EINVALID_WEIGHTS ); assert!( bigdecimal::eq( bigdecimal::one(), bigdecimal::add( weights.weights_after.coin_a_weight, - weights.weights_after.coin_b_weight, - ), + weights.weights_after.coin_b_weight + ) ), - EINVALID_WEIGHTS, + EINVALID_WEIGHTS ); } @@ -1258,19 +1266,19 @@ module initia_std::dex { symbol, 6, string::utf8(b""), - string::utf8(b""), + string::utf8(b"") ); assert_weights(weights); assert!( bigdecimal::le(swap_fee_rate, max_fee_rate()), - error::invalid_argument(EOUT_OF_SWAP_FEE_RATE_RANGE), + error::invalid_argument(EOUT_OF_SWAP_FEE_RATE_RANGE) ); assert!( coin_address(&coin_a) != coin_address(&coin_b), - error::invalid_argument(ESAME_COIN_TYPE), + error::invalid_argument(ESAME_COIN_TYPE) ); let pair_signer = &object::generate_signer_for_extending(&extend_ref); @@ -1281,24 +1289,24 @@ module initia_std::dex { let coin_a_store = primary_fungible_store::create_primary_store( pair_address, - fungible_asset::asset_metadata(&coin_a), + fungible_asset::asset_metadata(&coin_a) ); let coin_b_store = primary_fungible_store::create_primary_store( pair_address, - fungible_asset::asset_metadata(&coin_b), + fungible_asset::asset_metadata(&coin_b) ); let coin_a_addr = coin_address(&coin_a); let coin_b_addr = coin_address(&coin_b); move_to( pair_signer, - Pool { coin_a_store, coin_b_store }, + Pool { coin_a_store, coin_b_store } ); move_to( pair_signer, - CoinCapabilities { mint_cap, freeze_cap, burn_cap }, + CoinCapabilities { mint_cap, freeze_cap, burn_cap } ); move_to( @@ -1319,7 +1327,7 @@ module initia_std::dex { } }, swap_fee_rate - }, + } ); let liquidity_token = @@ -1327,7 +1335,7 @@ module initia_std::dex { object::address_to_object(pair_address), coin_a, coin_b, - option::none(), + option::none() ); // update weights @@ -1357,7 +1365,7 @@ module initia_std::dex { liquidity_token: pair_address, weights, swap_fee_rate - }, + } ); // emit create pair event @@ -1368,7 +1376,7 @@ module initia_std::dex { liquidity_token: pair_address, weights, swap_fee_rate - }, + } ); liquidity_token @@ -1392,13 +1400,16 @@ module initia_std::dex { let liquidity = calculate_provide_liquidity_return_amount( - pool, pair, coin_a_amount_in, coin_b_amount_in + pool, + pair, + coin_a_amount_in, + coin_b_amount_in ); assert!( option::is_none(&min_liquidity_amount) || *option::borrow(&min_liquidity_amount) <= liquidity, - error::invalid_state(EMIN_LIQUIDITY), + error::invalid_state(EMIN_LIQUIDITY) ); event::emit( @@ -1409,7 +1420,7 @@ module initia_std::dex { coin_a_amount: coin_a_amount_in, coin_b_amount: coin_b_amount_in, liquidity - }, + } ); fungible_asset::deposit(pool.coin_a_store, coin_a); @@ -1418,7 +1429,7 @@ module initia_std::dex { let liquidity_token_capabilities = borrow_global(pool_addr); coin::mint( &liquidity_token_capabilities.mint_cap, - liquidity, + liquidity ) } @@ -1432,7 +1443,7 @@ module initia_std::dex { assert!( timestamp >= weights.weights_after.timestamp, - error::invalid_state(ELBP_NOT_ENDED), + error::invalid_state(ELBP_NOT_ENDED) ) } @@ -1501,7 +1512,10 @@ module initia_std::dex { } fun calculate_provide_liquidity_return_amount( - pool: &Pool, pair: Object, coin_a_amount_in: u64, coin_b_amount_in: u64 + pool: &Pool, + pair: Object, + coin_a_amount_in: u64, + coin_b_amount_in: u64 ): u64 { let coin_a_amount = fungible_asset::balance(pool.coin_a_store); let coin_b_amount = fungible_asset::balance(pool.coin_b_store); @@ -1539,8 +1553,9 @@ module initia_std::dex { // provide coin type must be one of coin a or coin b coin type assert!( provide_metadata == fungible_asset::store_metadata(pool.coin_a_store) - || provide_metadata == fungible_asset::store_metadata(pool.coin_b_store), - error::invalid_argument(ECOIN_TYPE), + || provide_metadata + == fungible_asset::store_metadata(pool.coin_b_store), + error::invalid_argument(ECOIN_TYPE) ); let is_provide_a = provide_metadata == fungible_asset::store_metadata(pool.coin_a_store); @@ -1548,7 +1563,7 @@ module initia_std::dex { let total_share = option::extract(&mut fungible_asset::supply(pair)); assert!( total_share != 0, - error::invalid_state(EZERO_LIQUIDITY), + error::invalid_state(EZERO_LIQUIDITY) ); // load values for fee and increased liquidity amount calculation @@ -1558,7 +1573,7 @@ module initia_std::dex { let normalized_weight = bigdecimal::div( coin_a_weight, - bigdecimal::add(coin_a_weight, coin_b_weight), + bigdecimal::add(coin_a_weight, coin_b_weight) ); let pool_amount_in = fungible_asset::balance(pool.coin_a_store); (normalized_weight, pool_amount_in) @@ -1566,7 +1581,7 @@ module initia_std::dex { let normalized_weight = bigdecimal::div( coin_b_weight, - bigdecimal::add(coin_a_weight, coin_b_weight), + bigdecimal::add(coin_a_weight, coin_b_weight) ); let pool_amount_in = fungible_asset::balance(pool.coin_b_store); @@ -1576,8 +1591,8 @@ module initia_std::dex { // CONTRACT: cannot provide more than the pool amount to prevent huge price impact assert!( - pool_amount_in > amount_in, - error::invalid_argument(EPRICE_IMPACT), + pool_amount_in >= amount_in, + error::invalid_argument(EPRICE_IMPACT) ); // compute fee amount with the assumption that we will swap (1 - normalized_weight) of amount_in @@ -1585,14 +1600,14 @@ module initia_std::dex { bigdecimal::mul_by_u64_truncate( bigdecimal::sub( bigdecimal::one(), - normalized_weight, + normalized_weight ), - amount_in, + amount_in ); let fee_amount = calculate_fee_with_minimum( config.swap_fee_rate, - adjusted_swap_amount, + adjusted_swap_amount ); // actual amount in after deducting fee amount @@ -1602,7 +1617,7 @@ module initia_std::dex { let base = bigdecimal::from_ratio_u64( adjusted_amount_in + pool_amount_in, - pool_amount_in, + pool_amount_in ); let pool_ratio = pow(base, normalized_weight); let new_total_share = bigdecimal::mul_by_u128_truncate(pool_ratio, total_share); @@ -1610,10 +1625,9 @@ module initia_std::dex { } /// get all pool info at once (a_amount, b_amount, a_weight, b_weight, fee_rate) - public fun pool_info(pair: Object, lbp_assertion: bool) - : ( - u64, u64, BigDecimal, BigDecimal, BigDecimal - ) acquires Config, Pool { + public fun pool_info( + pair: Object, lbp_assertion: bool + ): (u64, u64, BigDecimal, BigDecimal, BigDecimal) acquires Config, Pool { let pair_addr = object::object_address(&pair); let config = borrow_global(pair_addr); if (lbp_assertion) { @@ -1621,7 +1635,7 @@ module initia_std::dex { let (_, timestamp) = get_block_info(); assert!( timestamp >= config.weights.weights_before.timestamp, - error::invalid_state(ELBP_NOT_STARTED), + error::invalid_state(ELBP_NOT_STARTED) ); }; @@ -1662,7 +1676,7 @@ module initia_std::dex { ): (u64, u64) { assert!( amount_in > 0, - error::invalid_argument(EZERO_AMOUNT_IN), + error::invalid_argument(EZERO_AMOUNT_IN) ); let one = bigdecimal::one(); @@ -1673,13 +1687,13 @@ module initia_std::dex { let base = bigdecimal::from_ratio_u64( pool_amount_in, - pool_amount_in + adjusted_amount_in, + pool_amount_in + adjusted_amount_in ); let sub_amount = pow(base, exp); ( bigdecimal::mul_by_u64_truncate( bigdecimal::sub(one, sub_amount), - pool_amount_out, + pool_amount_out ), fee_amount ) @@ -1709,7 +1723,9 @@ module initia_std::dex { (amount_in, fee_amount) } - public fun pool_metadata(pair: Object): (Object, Object) acquires Pool { + public fun pool_metadata( + pair: Object + ): (Object, Object) acquires Pool { let pair_addr = object::object_address(&pair); let pool = borrow_global(pair_addr); ( @@ -1723,7 +1739,7 @@ module initia_std::dex { fun pow(base: BigDecimal, exp: BigDecimal): BigDecimal { assert!( !bigdecimal::is_zero(base) && bigdecimal::lt(base, bigdecimal::from_u64(2)), - error::invalid_argument(EOUT_OF_BASE_RANGE), + error::invalid_argument(EOUT_OF_BASE_RANGE) ); let res = bigdecimal::one(); @@ -1782,7 +1798,7 @@ module initia_std::dex { // comp(new) = comp(old) * a * n / (n + 1) = a ^ (n + 1) / (n + 1) comp = bigdecimal::div_by_u64( bigdecimal::mul_by_u64(bigdecimal::mul(comp, a), index), // comp * a * index - index + 1, + index + 1 ); index = index + 1; @@ -1814,10 +1830,9 @@ module initia_std::dex { } #[test_only] - fun initialized_coin(account: &signer, symbol: String) - : ( - coin::BurnCapability, coin::FreezeCapability, coin::MintCapability - ) { + fun initialized_coin( + account: &signer, symbol: String + ): (coin::BurnCapability, coin::FreezeCapability, coin::MintCapability) { let (mint_cap, burn_cap, freeze_cap, _) = coin::initialize_and_generate_extend_ref( account, @@ -1826,7 +1841,7 @@ module initia_std::dex { symbol, 6, string::utf8(b""), - string::utf8(b""), + string::utf8(b"") ); return (burn_cap, freeze_cap, mint_cap) @@ -1849,12 +1864,12 @@ module initia_std::dex { coin::mint_to( &initia_mint_cap, chain_addr, - 100000000, + 100000000 ); coin::mint_to( &usdc_mint_cap, chain_addr, - 100000000, + 100000000 ); // spot price is 1 @@ -1868,7 +1883,7 @@ module initia_std::dex { coin::metadata(chain_addr, string::utf8(b"INIT")), coin::metadata(chain_addr, string::utf8(b"USDC")), 80000000, - 20000000, + 20000000 ); let lp_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL")); @@ -1876,15 +1891,15 @@ module initia_std::dex { assert!( coin::balance(chain_addr, init_metadata) == 20000000, - 0, + 0 ); assert!( coin::balance(chain_addr, usdc_metadata) == 80000000, - 1, + 1 ); assert!( coin::balance(chain_addr, lp_metadata) == 80000000, - 2, + 2 ); // swap init to usdc @@ -1893,15 +1908,15 @@ module initia_std::dex { pair, init_metadata, 1000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, init_metadata) == 20000000 - 1000, - 3, + 3 ); assert!( coin::balance(chain_addr, usdc_metadata) == 80000000 + 996, - 4, + 4 ); // return 999 commission 3 // swap usdc to init @@ -1910,15 +1925,15 @@ module initia_std::dex { pair, usdc_metadata, 1000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, init_metadata) == 20000000 - 1000 + 997, - 5, + 5 ); // return 1000 commission 3 assert!( coin::balance(chain_addr, usdc_metadata) == 80000000 + 996 - 1000, - 6, + 6 ); // withdraw liquidity @@ -1927,15 +1942,17 @@ module initia_std::dex { pair, 40000000, option::none(), - option::none(), + option::none() ); assert!( - coin::balance(chain_addr, init_metadata) == 20000000 - 1000 + 997 + 40000001, - 7, + coin::balance(chain_addr, init_metadata) == 20000000 - 1000 + 997 + + 40000001, + 7 ); assert!( - coin::balance(chain_addr, usdc_metadata) == 80000000 + 996 - 1000 + 10000002, - 8, + coin::balance(chain_addr, usdc_metadata) == 80000000 + 996 - 1000 + + 10000002, + 8 ); // single asset provide liquidity (coin b) @@ -1945,11 +1962,11 @@ module initia_std::dex { pair, usdc_metadata, 100000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, lp_metadata) == 40000000 + 79491, - 9, + 9 ); // single asset provide liquidity (coin a) @@ -1959,11 +1976,11 @@ module initia_std::dex { pair, init_metadata, 100000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, lp_metadata) == 40000000 + 79491 + 80090, - 10, + 10 ); move_to( @@ -1972,7 +1989,7 @@ module initia_std::dex { burn_cap: initia_burn_cap, freeze_cap: initia_freeze_cap, mint_cap: initia_mint_cap - }, + } ); move_to( @@ -1981,7 +1998,7 @@ module initia_std::dex { burn_cap: usdc_burn_cap, freeze_cap: usdc_freeze_cap, mint_cap: usdc_mint_cap - }, + } ); } @@ -2002,12 +2019,12 @@ module initia_std::dex { coin::mint_to( &initia_mint_cap, chain_addr, - 100000000, + 100000000 ); coin::mint_to( &usdc_mint_cap, chain_addr, - 100000000, + 100000000 ); set_block_info(10, 1000); @@ -2026,21 +2043,21 @@ module initia_std::dex { init_metadata, usdc_metadata, 80000000, - 20000000, + 20000000 ); let lp_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL")); let pair = object::convert(lp_metadata); assert!( get_spot_price(pair, init_metadata) == bigdecimal::from_ratio_u64(2475, 100), - 0, + 0 ); // 0.8 : 0.2 set_block_info(11, 2500); assert!( get_spot_price(pair, init_metadata) == bigdecimal::one(), - 1, + 1 ); // 0.61 : 0.39 @@ -2048,20 +2065,20 @@ module initia_std::dex { assert!( get_spot_price(pair, init_metadata) == bigdecimal::from_ratio_u64(391025641025641025, 1000000000000000000), - 2, + 2 ); assert!( coin::balance(chain_addr, init_metadata) == 20000000, - 0, + 0 ); assert!( coin::balance(chain_addr, usdc_metadata) == 80000000, - 1, + 1 ); assert!( coin::balance(chain_addr, lp_metadata) == 80000000, - 3, + 3 ); // swap test during LBP (0.8: 0.2) @@ -2073,15 +2090,15 @@ module initia_std::dex { pair, init_metadata, 1000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, init_metadata) == 20000000 - 1000, - 4, + 4 ); assert!( coin::balance(chain_addr, usdc_metadata) == 80000000 + 996, - 5, + 5 ); // return 999 commission 3 // swap usdc to init @@ -2090,15 +2107,15 @@ module initia_std::dex { pair, usdc_metadata, 1000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, init_metadata) == 20000000 - 1000 + 997, - 6, + 6 ); // return 1000 commission 3 assert!( coin::balance(chain_addr, usdc_metadata) == 80000000 + 996 - 1000, - 7, + 7 ); move_to( @@ -2107,7 +2124,7 @@ module initia_std::dex { burn_cap: initia_burn_cap, freeze_cap: initia_freeze_cap, mint_cap: initia_mint_cap - }, + } ); move_to( @@ -2116,7 +2133,7 @@ module initia_std::dex { burn_cap: usdc_burn_cap, freeze_cap: usdc_freeze_cap, mint_cap: usdc_mint_cap - }, + } ); } @@ -2140,7 +2157,7 @@ module initia_std::dex { assert!( coin_a_weight == bigdecimal::from_ratio_u64(2, 10) && coin_b_weight == bigdecimal::from_ratio_u64(8, 10), - 0, + 0 ); set_block_info(15, 1500); @@ -2148,7 +2165,7 @@ module initia_std::dex { assert!( coin_a_weight == bigdecimal::from_ratio_u64(5, 10) && coin_b_weight == bigdecimal::from_ratio_u64(5, 10), - 1, + 1 ); set_block_info(20, 2000); @@ -2156,7 +2173,7 @@ module initia_std::dex { assert!( coin_a_weight == bigdecimal::from_ratio_u64(8, 10) && coin_b_weight == bigdecimal::from_ratio_u64(2, 10), - 2, + 2 ); set_block_info(30, 3000); @@ -2164,7 +2181,7 @@ module initia_std::dex { assert!( coin_a_weight == bigdecimal::from_ratio_u64(8, 10) && coin_b_weight == bigdecimal::from_ratio_u64(2, 10), - 3, + 3 ); } @@ -2189,17 +2206,17 @@ module initia_std::dex { coin::mint_to( &coin_a_mint_cap, chain_addr, - 100000000, + 100000000 ); coin::mint_to( &coin_b_mint_cap, chain_addr, - 100000000, + 100000000 ); coin::mint_to( &coin_c_mint_cap, chain_addr, - 100000000, + 100000000 ); create_pair_script( @@ -2212,7 +2229,7 @@ module initia_std::dex { a_metadata, b_metadata, 1, - 1, + 1 ); let lp_1_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL1")); let pair_1 = object::convert(lp_1_metadata); @@ -2228,7 +2245,7 @@ module initia_std::dex { a_metadata, b_metadata, 1, - 1, + 1 ); let lp_2_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL2")); let pair_2 = object::convert(lp_2_metadata); @@ -2244,7 +2261,7 @@ module initia_std::dex { a_metadata, c_metadata, 1, - 1, + 1 ); let lp_3_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL3")); let pair_3 = object::convert(lp_3_metadata); @@ -2260,7 +2277,7 @@ module initia_std::dex { a_metadata, c_metadata, 1, - 1, + 1 ); let lp_4_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL4")); let pair_4 = object::convert(lp_4_metadata); @@ -2286,7 +2303,7 @@ module initia_std::dex { option::none(), option::none(), option::none(), - 10, + 10 ); assert!( res @@ -2318,8 +2335,9 @@ module initia_std::dex { liquidity_token: pair_4_addr, weights, swap_fee_rate - }], - 0, + } + ], + 0 ); let res = @@ -2327,7 +2345,7 @@ module initia_std::dex { option::some(a_addr), option::some(b_addr), option::some(pair_1_addr), - 10, + 10 ); assert!( res @@ -2352,8 +2370,9 @@ module initia_std::dex { liquidity_token: pair_4_addr, weights, swap_fee_rate - }], - 1, + } + ], + 1 ); let res = @@ -2361,7 +2380,7 @@ module initia_std::dex { option::some(a_addr), option::some(a_addr), option::some(pair_1_addr), - 10, + 10 ); assert!( res @@ -2393,15 +2412,16 @@ module initia_std::dex { liquidity_token: pair_4_addr, weights, swap_fee_rate - }], - 2, + } + ], + 2 ); let res = get_pairs( a_addr, b_addr, option::none(), - 10, + 10 ); assert!( res @@ -2419,15 +2439,16 @@ module initia_std::dex { liquidity_token: pair_2_addr, weights, swap_fee_rate - }], - 3, + } + ], + 3 ); let res = get_pairs( a_addr, b_addr, option::some(pair_1_addr), - 10, + 10 ); assert!( res @@ -2438,8 +2459,9 @@ module initia_std::dex { liquidity_token: pair_2_addr, weights, swap_fee_rate - }], - 3, + } + ], + 3 ); } } diff --git a/precompile/modules/initia_stdlib/sources/fa/fungible_asset.move b/precompile/modules/initia_stdlib/sources/fa/fungible_asset.move index 7f00e688..3618fed6 100644 --- a/precompile/modules/initia_stdlib/sources/fa/fungible_asset.move +++ b/precompile/modules/initia_stdlib/sources/fa/fungible_asset.move @@ -483,7 +483,8 @@ module initia_std::fungible_asset { /// Get the symbol of the fungible asset from the `metadata` object. public fun symbol(metadata: Object): String acquires Metadata { let md = borrow_fungible_metadata(&metadata); - if (object::is_owner(metadata, @initia_std) && md.symbol == string::utf8(b"uinit")) { + if (object::is_owner(metadata, @initia_std) + && md.symbol == string::utf8(b"uinit")) { return string::utf8(b"INIT") }; @@ -512,7 +513,8 @@ module initia_std::fungible_asset { /// Get the decimals from the `metadata` object. public fun decimals(metadata: Object): u8 acquires Metadata { let md = borrow_fungible_metadata(&metadata); - if (object::is_owner(metadata, @initia_std) && md.symbol == string::utf8(b"uinit")) { + if (object::is_owner(metadata, @initia_std) + && md.symbol == string::utf8(b"uinit")) { return 6 }; diff --git a/precompile/modules/initia_stdlib/sources/object.move b/precompile/modules/initia_stdlib/sources/object.move index 835646e2..779e2429 100644 --- a/precompile/modules/initia_stdlib/sources/object.move +++ b/precompile/modules/initia_stdlib/sources/object.move @@ -28,6 +28,7 @@ module initia_std::object { use initia_std::event; friend initia_std::primary_fungible_store; + friend initia_std::nft; /// An object already exists at this address const EOBJECT_EXISTS: u64 = 1; @@ -243,6 +244,16 @@ module initia_std::object { create_object_internal(creator_address, obj_addr, true) } + /// Create a new object to represent an NFT and return the ConstructorRef. + /// Nft objects can be queried globally by knowing the user generated seed used to create them + /// and the creator's address. NFT objects can be deleted. + public(friend) fun create_nft_object( + owner: address, creator: address, seed: vector + ): ConstructorRef acquires Tombstone { + let obj_addr = create_object_address(&creator, seed); + create_object_internal(owner, obj_addr, true) + } + /// Create a new object whose address is derived based on the creator account address and another object. /// Derivde objects, similar to named objects, cannot be deleted. public(friend) fun create_user_derived_object( diff --git a/precompile/modules/initia_stdlib/sources/token/collection.move b/precompile/modules/initia_stdlib/sources/token/collection.move index 9d307e2d..0803ad11 100644 --- a/precompile/modules/initia_stdlib/sources/token/collection.move +++ b/precompile/modules/initia_stdlib/sources/token/collection.move @@ -271,9 +271,6 @@ module initia_std::collection { ) }; - let transfer_ref = object::generate_transfer_ref(&constructor_ref); - object::disable_ungated_transfer(&transfer_ref); - constructor_ref } @@ -537,15 +534,8 @@ module initia_std::collection { assert!(count(collection) == option::some(0), 0); } - #[test(creator = @0x123)] - #[expected_failure(abort_code = 0x10007, location = Self)] - fun test_create_collection_with_invalid_name(creator: &signer) { - create_collection_helper(creator, string::utf8(b"collection::hello")); - } - - #[test(creator = @0x123, trader = @0x456)] - #[expected_failure(abort_code = 0x50003, location = initia_std::object)] - entry fun test_create_and_transfer(creator: &signer, trader: &signer) { + #[test(creator = @0x123, receipient = @0x456)] + entry fun test_create_and_transfer(creator: &signer, receipient: &signer) { let creator_address = signer::address_of(creator); let collection_name = string::utf8(b"collection name"); create_collection_helper(creator, collection_name); @@ -561,10 +551,16 @@ module initia_std::collection { object::transfer( creator, collection, - signer::address_of(trader) + signer::address_of(receipient) ); } + #[test(creator = @0x123)] + #[expected_failure(abort_code = 0x10007, location = Self)] + fun test_create_collection_with_invalid_name(creator: &signer) { + create_collection_helper(creator, string::utf8(b"collection::hello")); + } + #[test(creator = @0x123)] #[expected_failure(abort_code = 0x80001, location = initia_std::object)] entry fun test_duplicate_collection(creator: &signer) { diff --git a/precompile/modules/initia_stdlib/sources/token/initia_nft.move b/precompile/modules/initia_stdlib/sources/token/initia_nft.move index d55fb43e..eefe1ea0 100644 --- a/precompile/modules/initia_stdlib/sources/token/initia_nft.move +++ b/precompile/modules/initia_stdlib/sources/token/initia_nft.move @@ -13,7 +13,7 @@ module initia_std::initia_nft { use std::string::String; use std::signer; use initia_std::object::{Self, ConstructorRef, ExtendRef, Object}; - use initia_std::collection; + use initia_std::collection::{Self, Collection}; use initia_std::royalty; use initia_std::nft; use initia_std::bigdecimal::BigDecimal; @@ -205,16 +205,17 @@ module initia_std::initia_nft { fun mint_internal( creator: &signer, - collection: String, + collection_name: String, description: String, token_id: String, uri: String, can_burn: bool ): ConstructorRef acquires InitiaNftCollection { + let collection_obj = collection_object(creator, &collection_name); let constructor_ref = nft::create( creator, - collection, + object::convert(collection_obj), description, token_id, option::none(), @@ -222,8 +223,6 @@ module initia_std::initia_nft { ); let object_signer = object::generate_signer(&constructor_ref); - - let collection_obj = collection_object(creator, &collection); let collection = borrow_collection(collection_obj); let mutator_ref = diff --git a/precompile/modules/initia_stdlib/sources/token/nft.move b/precompile/modules/initia_stdlib/sources/token/nft.move index 54872b3e..bff65663 100644 --- a/precompile/modules/initia_stdlib/sources/token/nft.move +++ b/precompile/modules/initia_stdlib/sources/token/nft.move @@ -31,6 +31,8 @@ module initia_std::nft { const EQUERY_LENGTH_TOO_LONG: u64 = 8; /// The provided token id is invalid const EINVALID_TOKEN_ID: u64 = 9; + /// The calling signer is not the owner + const ENOT_OWNER: u64 = 10; const MAX_NFT_TOKEN_ID_LENGTH: u64 = 128; const MAX_URI_LENGTH: u64 = 512; @@ -94,14 +96,19 @@ module initia_std::nft { } inline fun create_common( + owner: &signer, constructor_ref: &ConstructorRef, - creator_address: address, - collection_name: String, + collection: Object, description: String, token_id: String, royalty: Option, uri: String ) { + // only the collection owner can create nfts + assert!( + object::owner(collection) == signer::address_of(owner), + error::unauthenticated(ENOT_OWNER) + ); assert_token_id(&token_id); assert!( string::length(&description) <= MAX_DESCRIPTION_LENGTH, @@ -113,10 +120,6 @@ module initia_std::nft { ); let object_signer = object::generate_signer(constructor_ref); - - let collection_addr = - collection::create_collection_address(creator_address, &collection_name); - let collection = object::address_to_object(collection_addr); collection::increment_supply( collection, token_id, @@ -137,21 +140,24 @@ module initia_std::nft { /// Creates a new nft object from a nft name and returns the ConstructorRef for /// additional specialization. public fun create( - creator: &signer, - collection_name: String, + owner: &signer, + collection: Object, description: String, token_id: String, royalty: Option, uri: String ): ConstructorRef { - let creator_address = signer::address_of(creator); + let owner_address = signer::address_of(owner); + let creator_address = collection::creator(collection); + let collection_name = collection::name(collection); let seed = create_nft_seed(&collection_name, &token_id); - let constructor_ref = object::create_deletable_named_object(creator, seed); + let constructor_ref = + object::create_nft_object(owner_address, creator_address, seed); create_common( + owner, &constructor_ref, - creator_address, - collection_name, + collection, description, token_id, royalty, @@ -354,13 +360,53 @@ module initia_std::nft { #[test_only] use initia_std::bigdecimal; + #[test_only] + fun generate_collection_object( + creator: &signer, collection_name: &String + ): Object { + let creator_address = signer::address_of(creator); + let collection_address = + collection::create_collection_address(creator_address, collection_name); + object::address_to_object(collection_address) + } + + #[test(creator = @0x123, owner = @0x456, trader = @0x789)] + fun test_create_after_collection_transfer( + creator: &signer, owner: &signer + ) { + let collection_name = string::utf8(b"collection name"); + let token_id = string::utf8(b"nft token_id"); + + create_collection_helper(creator, collection_name, 1); + + // transfer collection to owner + let owner_address = signer::address_of(owner); + object::transfer( + creator, + generate_collection_object(creator, &collection_name), + owner_address + ); + + // create nft + create_nft_helper(owner, creator, collection_name, token_id); + + let creator_address = signer::address_of(creator); + let nft_addr = create_nft_address( + creator_address, + &collection_name, + &token_id + ); + let nft = object::address_to_object(nft_addr); + assert!(object::owner(nft) == owner_address, 1); + } + #[test(creator = @0x123, trader = @0x456)] fun test_create_and_transfer(creator: &signer, trader: &signer) acquires Nft { let collection_name = string::utf8(b"collection name"); let token_id = string::utf8(b"nft token_id"); create_collection_helper(creator, collection_name, 1); - create_nft_helper(creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id); let creator_address = signer::address_of(creator); let nft_addr = create_nft_address( @@ -413,7 +459,7 @@ module initia_std::nft { create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -447,7 +493,7 @@ module initia_std::nft { create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -470,7 +516,7 @@ module initia_std::nft { let collection_name = string::utf8(b"collection name"); let token_id = string::utf8(b"nft token_id::hello"); create_collection_helper(creator, collection_name, 1); - create_nft_helper(creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id); } #[test(creator = @0x123)] @@ -481,8 +527,8 @@ module initia_std::nft { let token_id2 = string::utf8(b"nft token_id2"); create_collection_helper(creator, collection_name, 1); - create_nft_helper(creator, collection_name, token_id); - create_nft_helper(creator, collection_name, token_id2); + create_nft_helper(creator, creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id2); } #[test(creator = @0x123)] @@ -492,8 +538,8 @@ module initia_std::nft { let token_id = string::utf8(b"nft token_id"); create_collection_helper(creator, collection_name, 2); - create_nft_helper(creator, collection_name, token_id); - create_nft_helper(creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id); } #[test(creator = @0x123)] @@ -549,7 +595,7 @@ module initia_std::nft { let constructor_ref = create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -573,7 +619,7 @@ module initia_std::nft { let constructor_ref = create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::some( @@ -603,7 +649,7 @@ module initia_std::nft { let constructor_ref = create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -617,7 +663,7 @@ module initia_std::nft { // mint again create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -642,11 +688,14 @@ module initia_std::nft { #[test_only] fun create_nft_helper( - creator: &signer, collection_name: String, token_id: String + owner: &signer, + creator: &signer, + collection_name: String, + token_id: String ): ConstructorRef { create( - creator, - collection_name, + owner, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::some( @@ -663,7 +712,9 @@ module initia_std::nft { fun create_nft_with_mutation_ref( creator: &signer, collection_name: String, token_id: String ): MutatorRef { - let constructor_ref = create_nft_helper(creator, collection_name, token_id); + let constructor_ref = create_nft_helper( + creator, creator, collection_name, token_id + ); generate_mutator_ref(&constructor_ref) } } diff --git a/precompile/modules/initia_stdlib/sources/token/soul_bound_token.move b/precompile/modules/initia_stdlib/sources/token/soul_bound_token.move index be1c51a4..68c957df 100644 --- a/precompile/modules/initia_stdlib/sources/token/soul_bound_token.move +++ b/precompile/modules/initia_stdlib/sources/token/soul_bound_token.move @@ -6,7 +6,7 @@ module initia_std::soul_bound_token { use std::string::String; use std::signer; use initia_std::object::{Self, ConstructorRef, Object}; - use initia_std::collection; + use initia_std::collection::{Self, Collection}; use initia_std::property_map; use initia_std::royalty; use initia_std::nft; @@ -203,7 +203,7 @@ module initia_std::soul_bound_token { fun mint_internal( creator: &signer, - collection: String, + collection_name: String, description: String, name: String, uri: String, @@ -211,10 +211,11 @@ module initia_std::soul_bound_token { property_types: vector, property_values: vector> ): ConstructorRef acquires SoulBoundTokenCollection { + let collection_obj = collection_object(creator, &collection_name); let constructor_ref = nft::create( creator, - collection, + object::convert(collection_obj), description, name, option::none(), @@ -224,7 +225,6 @@ module initia_std::soul_bound_token { let object_signer = object::generate_signer(&constructor_ref); - let collection_obj = collection_object(creator, &collection); let collection = borrow_collection(collection_obj); let mutator_ref = diff --git a/precompile/modules/minitia_stdlib/sources/block.move b/precompile/modules/minitia_stdlib/sources/block.move index c1ee6d18..118f187c 100644 --- a/precompile/modules/minitia_stdlib/sources/block.move +++ b/precompile/modules/minitia_stdlib/sources/block.move @@ -45,7 +45,7 @@ module minitia_std::block { vm: &signer, _fake_block_hash: address ) { if (!exists(signer::address_of(vm))) { - move_to(vm, HasGenesisBlock{}); + move_to(vm, HasGenesisBlock {}); return }; diff --git a/precompile/modules/minitia_stdlib/sources/crypto/secp256k1.move b/precompile/modules/minitia_stdlib/sources/crypto/secp256k1.move index a5e5b9f8..c493245d 100644 --- a/precompile/modules/minitia_stdlib/sources/crypto/secp256k1.move +++ b/precompile/modules/minitia_stdlib/sources/crypto/secp256k1.move @@ -91,6 +91,24 @@ module minitia_std::secp256k1 { sig.bytes } + /// Returns `true` if the signature can verify the public key on the message + public fun verify( + message: vector, + public_key: &ECDSACompressedPublicKey, + signature: &ECDSASignature + ): bool { + assert!( + std::vector::length(&message) == MESSAGE_SIZE, + std::error::invalid_argument(E_DESERIALIZE) + ); + + return verify_internal( + message, + public_key.bytes, + signature.bytes + ) + } + /// Recovers the signer's raw (64-byte) public key from a secp256k1 ECDSA `signature` given the `recovery_id` and the signed /// `message` (32 byte digest). /// @@ -151,6 +169,18 @@ module minitia_std::secp256k1 { // Native functions // + /// Returns `true` if `signature` verifies on `public_key` and `message` + /// and returns `false` otherwise. + /// + /// - `message`: A 32-byte hashed message. + /// - `public_key`: A compressed public key in bytes. + /// - `signature`: A 64-byte ECDSA signature. + native fun verify_internal( + message: vector, + public_key: vector, + signature: vector + ): bool; + /// Returns `(public_key, true)` if `signature` verifies on `message` under the recovered `public_key` /// and returns `([], false)` otherwise. native fun recover_public_key_internal( @@ -172,6 +202,29 @@ module minitia_std::secp256k1 { // Tests // + #[test] + fun test_secp256k1_sign_verify() { + use std::hash; + + let (sk, vk) = generate_keys(true); + let pk = ecdsa_compressed_public_key_from_bytes(vk); + + let msg: vector = hash::sha2_256(b"test initia secp256k1"); + let (_rid, sig_bytes) = sign(msg, sk); + let sig = ecdsa_signature_from_bytes(sig_bytes); + assert!(verify(msg, &pk, &sig), 1); + + // Test with an incorrect message + let wrong_msg: vector = hash::sha2_256(b"wrong message"); + assert!(!verify(wrong_msg, &pk, &sig), 2); + + // Test with an incorrect signature + let invalid_sig_bytes = sig_bytes; + *std::vector::borrow_mut(&mut invalid_sig_bytes, 0) = 0xFF; // Corrupt the signature + let invalid_sig = ecdsa_signature_from_bytes(invalid_sig_bytes); + assert!(!verify(msg, &pk, &invalid_sig), 3); + } + #[test] fun test_gen_sign_recover() { use std::hash; diff --git a/precompile/modules/minitia_stdlib/sources/dex.move b/precompile/modules/minitia_stdlib/sources/dex.move index b8e07a69..1e90d093 100644 --- a/precompile/modules/minitia_stdlib/sources/dex.move +++ b/precompile/modules/minitia_stdlib/sources/dex.move @@ -253,7 +253,7 @@ module minitia_std::dex { let base_addr = object::object_address(&base_coin); assert!( base_addr == pair_key.coin_a || base_addr == pair_key.coin_b, - error::invalid_argument(ECOIN_TYPE), + error::invalid_argument(ECOIN_TYPE) ); let is_base_a = base_addr == pair_key.coin_a; let (base_pool, quote_pool, base_weight, quote_weight) = @@ -265,7 +265,7 @@ module minitia_std::dex { bigdecimal::div( bigdecimal::mul_by_u64(base_weight, quote_pool), - bigdecimal::mul_by_u64(quote_weight, base_pool), + bigdecimal::mul_by_u64(quote_weight, base_pool) ) } @@ -287,7 +287,7 @@ module minitia_std::dex { let offer_address = object::object_address(&offer_metadata); assert!( offer_address == pair_key.coin_a || offer_address == pair_key.coin_b, - error::invalid_argument(ECOIN_TYPE), + error::invalid_argument(ECOIN_TYPE) ); let is_offer_a = offer_address == pair_key.coin_a; let (pool_a, pool_b, weight_a, weight_b, swap_fee_rate) = pool_info(pair, true); @@ -304,7 +304,7 @@ module minitia_std::dex { offer_weight, return_weight, offer_amount, - swap_fee_rate, + swap_fee_rate ); return_amount @@ -319,7 +319,7 @@ module minitia_std::dex { get_swap_simulation( object::convert(pair_metadata), offer_metadata, - offer_amount, + offer_amount ) } @@ -332,7 +332,7 @@ module minitia_std::dex { let offer_address = object::object_address(&offer_metadata); assert!( offer_address == pair_key.coin_a || offer_address == pair_key.coin_b, - error::invalid_argument(ECOIN_TYPE), + error::invalid_argument(ECOIN_TYPE) ); let is_offer_a = offer_address == pair_key.coin_a; let (pool_a, pool_b, weight_a, weight_b, swap_fee_rate) = pool_info(pair, true); @@ -349,7 +349,7 @@ module minitia_std::dex { offer_weight, return_weight, return_amount, - swap_fee_rate, + swap_fee_rate ); offer_amount @@ -364,7 +364,7 @@ module minitia_std::dex { get_swap_simulation_given_out( object::convert(pair_metadata), offer_metadata, - return_amount, + return_amount ) } @@ -372,7 +372,7 @@ module minitia_std::dex { public fun get_provide_simulation( pair: Object, coin_a_amount_in: u64, - coin_b_amount_in: u64, + coin_b_amount_in: u64 ): u64 acquires Pool { let pool_addr = object::object_address(&pair); let pool = borrow_global(pool_addr); @@ -441,7 +441,9 @@ module minitia_std::dex { } #[view] - public fun get_current_weight_by_denom(pair_denom: String): CurrentWeightResponse acquires Config { + public fun get_current_weight_by_denom( + pair_denom: String + ): CurrentWeightResponse acquires Config { let pair_metadata = coin::denom_to_metadata(pair_denom); get_current_weight(object::convert(pair_metadata)) } @@ -460,10 +462,11 @@ module minitia_std::dex { }; assert!( - option::is_some(&coin_a_start_after) == option::is_some(&coin_b_start_after) + option::is_some(&coin_a_start_after) + == option::is_some(&coin_b_start_after) && option::is_some(&coin_b_start_after) == option::is_some(&liquidity_token_start_after), - ESTART_AFTER, + ESTART_AFTER ); let module_store = borrow_global(@minitia_std); @@ -475,7 +478,7 @@ module minitia_std::dex { coin_a: option::extract(&mut coin_a_start_after), coin_b: option::extract(&mut coin_b_start_after), liquidity_token: option::extract(&mut liquidity_token_start_after) - }, + } ) } else { option::some( @@ -488,11 +491,11 @@ module minitia_std::dex { &module_store.pairs, start_after, option::none(), - 1, + 1 ); while (vector::length(&res) < (limit as u64) - && table::prepare(pairs_iter)) { + && table::prepare(pairs_iter)) { let (key, value) = table::next(pairs_iter); if (&key != option::borrow(&start_after)) { vector::push_back(&mut res, *value) @@ -516,10 +519,11 @@ module minitia_std::dex { }; assert!( - option::is_some(&coin_a_start_after) == option::is_some(&coin_b_start_after) + option::is_some(&coin_a_start_after) + == option::is_some(&coin_b_start_after) && option::is_some(&coin_b_start_after) == option::is_some(&liquidity_token_start_after), - ESTART_AFTER, + ESTART_AFTER ); let module_store = borrow_global(@minitia_std); @@ -541,7 +545,7 @@ module minitia_std::dex { liquidity_token: object::object_address( &liquidity_token_start_after ) - }, + } ) } else { option::some( @@ -554,11 +558,11 @@ module minitia_std::dex { &module_store.pairs, start_after, option::none(), - 1, + 1 ); while (vector::length(&res) < (limit as u64) - && table::prepare(pairs_iter)) { + && table::prepare(pairs_iter)) { let (key, value) = table::next(pairs_iter); if (&key != option::borrow(&start_after)) { vector::push_back( @@ -575,7 +579,7 @@ module minitia_std::dex { ), weights: value.weights, swap_fee_rate: value.swap_fee_rate - }, + } ) } }; @@ -605,7 +609,7 @@ module minitia_std::dex { coin_a, coin_b, liquidity_token: option::extract(&mut start_after) - }, + } ) } else { option::some(PairKey { coin_a, coin_b, liquidity_token: @0x0 }) @@ -616,14 +620,14 @@ module minitia_std::dex { &module_store.pairs, start_after, option::none(), - 1, + 1 ); while (vector::length(&res) < (limit as u64) - && table::prepare(pairs_iter)) { + && table::prepare(pairs_iter)) { let (key, value) = table::next(pairs_iter); if (coin_a != key.coin_a || coin_b != key.coin_b) - break; + break; if (&key != option::borrow(&start_after)) { vector::push_back(&mut res, *value) } @@ -680,10 +684,9 @@ module minitia_std::dex { weight.timestamp } - public fun unpack_pair_response(pair_response: &PairResponse) - : ( - address, address, address, Weights, BigDecimal - ) { + public fun unpack_pair_response( + pair_response: &PairResponse + ): (address, address, address, Weights, BigDecimal) { ( pair_response.coin_a, pair_response.coin_b, @@ -703,14 +706,17 @@ module minitia_std::dex { fun check_chain_permission(chain: &signer) { assert!( signer::address_of(chain) == @minitia_std, - error::permission_denied(EUNAUTHORIZED), + error::permission_denied(EUNAUTHORIZED) ); } fun init_module(chain: &signer) { move_to( chain, - ModuleStore { pairs: table::new(), pair_count: 0 }, + ModuleStore { + pairs: table::new(), + pair_count: 0 + } ); } @@ -735,12 +741,12 @@ module minitia_std::dex { let coin_a = coin::withdraw( creator, coin_a_metadata, - coin_a_amount, + coin_a_amount ); let coin_b = coin::withdraw( creator, coin_b_metadata, - coin_b_amount, + coin_b_amount ); let liquidity_token = @@ -751,7 +757,7 @@ module minitia_std::dex { swap_fee_rate, coin_a, coin_b, - weights, + weights ); coin::deposit(signer::address_of(creator), liquidity_token); } @@ -778,11 +784,11 @@ module minitia_std::dex { let (_, timestamp) = get_block_info(); assert!( start_time > timestamp, - error::invalid_argument(ELBP_START_TIME), + error::invalid_argument(ELBP_START_TIME) ); assert!( end_time > start_time, - error::invalid_argument(EWEIGHTS_TIMESTAMP), + error::invalid_argument(EWEIGHTS_TIMESTAMP) ); let weights = Weights { weights_before: Weight { @@ -800,12 +806,12 @@ module minitia_std::dex { let coin_a = coin::withdraw( creator, coin_a_metadata, - coin_a_amount, + coin_a_amount ); let coin_b = coin::withdraw( creator, coin_b_metadata, - coin_b_amount, + coin_b_amount ); let liquidity_token = @@ -816,7 +822,7 @@ module minitia_std::dex { swap_fee_rate, coin_a, coin_b, - weights, + weights ); coin::deposit(signer::address_of(creator), liquidity_token); } @@ -834,7 +840,7 @@ module minitia_std::dex { let config = borrow_global_mut(object::object_address(&pair)); assert!( bigdecimal::le(swap_fee_rate, max_fee_rate()), - error::invalid_argument(EOUT_OF_SWAP_FEE_RATE_RANGE), + error::invalid_argument(EOUT_OF_SWAP_FEE_RATE_RANGE) ); config.swap_fee_rate = swap_fee_rate; @@ -853,7 +859,7 @@ module minitia_std::dex { coin_b: pair_key.coin_b, liquidity_token: pair_key.liquidity_token, swap_fee_rate - }, + } ); } @@ -870,7 +876,7 @@ module minitia_std::dex { pair, coin_a_amount_in, coin_b_amount_in, - min_liquidity, + min_liquidity ); } @@ -895,12 +901,12 @@ module minitia_std::dex { coin::withdraw( account, fungible_asset::store_metadata(pool.coin_a_store), - coin_a_amount_in, + coin_a_amount_in ), coin::withdraw( account, fungible_asset::store_metadata(pool.coin_b_store), - coin_b_amount_in, + coin_b_amount_in ) ) } else { @@ -922,12 +928,12 @@ module minitia_std::dex { coin::withdraw( account, fungible_asset::store_metadata(pool.coin_a_store), - coin_a_amount_in, + coin_a_amount_in ), coin::withdraw( account, fungible_asset::store_metadata(pool.coin_b_store), - coin_b_amount_in, + coin_b_amount_in ) ) }; @@ -950,7 +956,7 @@ module minitia_std::dex { ) acquires CoinCapabilities, Config, Pool { assert!( liquidity != 0, - error::invalid_argument(EZERO_LIQUIDITY), + error::invalid_argument(EZERO_LIQUIDITY) ); let addr = signer::address_of(account); @@ -958,13 +964,13 @@ module minitia_std::dex { coin::withdraw( account, object::convert(pair), - liquidity, + liquidity ); let (coin_a, coin_b) = withdraw_liquidity( liquidity_token, min_coin_a_amount, - min_coin_b_amount, + min_coin_b_amount ); coin::deposit(addr, coin_a); @@ -982,7 +988,7 @@ module minitia_std::dex { let offer_coin = coin::withdraw( account, offer_coin, - offer_coin_amount, + offer_coin_amount ); let return_coin = swap(pair, offer_coin); @@ -991,7 +997,7 @@ module minitia_std::dex { || *option::borrow(&min_return) <= fungible_asset::amount( &return_coin ), - error::invalid_state(EMIN_RETURN), + error::invalid_state(EMIN_RETURN) ); coin::deposit(signer::address_of(account), return_coin); @@ -1027,7 +1033,7 @@ module minitia_std::dex { option::extract( &mut fungible_asset::supply( fungible_asset::metadata_from_asset(&lp_token) - ), + ) ); let coin_a_amount = fungible_asset::balance(pool.coin_a_store); let given_token_amount = fungible_asset::amount(&lp_token); @@ -1043,19 +1049,19 @@ module minitia_std::dex { assert!( option::is_none(&min_coin_a_amount) || *option::borrow(&min_coin_a_amount) <= coin_a_amount_out, - error::invalid_state(EMIN_WITHDRAW), + error::invalid_state(EMIN_WITHDRAW) ); assert!( option::is_none(&min_coin_b_amount) || *option::borrow(&min_coin_b_amount) <= coin_b_amount_out, - error::invalid_state(EMIN_WITHDRAW), + error::invalid_state(EMIN_WITHDRAW) ); // burn liquidity token let liquidity_token_capabilities = borrow_global(pair_addr); coin::burn( &liquidity_token_capabilities.burn_cap, - lp_token, + lp_token ); // emit events @@ -1068,7 +1074,7 @@ module minitia_std::dex { coin_a_amount: coin_a_amount_out, coin_b_amount: coin_b_amount_out, liquidity: given_token_amount - }, + } ); let pool = borrow_global_mut(pair_addr); @@ -1078,12 +1084,12 @@ module minitia_std::dex { fungible_asset::withdraw( pair_signer, pool.coin_a_store, - coin_a_amount_out, + coin_a_amount_out ), fungible_asset::withdraw( pair_signer, pool.coin_b_store, - coin_b_amount_out, + coin_b_amount_out ) ) } @@ -1120,7 +1126,7 @@ module minitia_std::dex { assert!( option::is_none(&min_liquidity_amount) || *option::borrow(&min_liquidity_amount) <= liquidity, - error::invalid_state(EMIN_LIQUIDITY), + error::invalid_state(EMIN_LIQUIDITY) ); // emit events @@ -1133,26 +1139,28 @@ module minitia_std::dex { provide_amount, fee_amount, liquidity - }, + } ); // mint liquidity tokens to provider let liquidity_token_capabilities = borrow_global(pair_addr); coin::mint( &liquidity_token_capabilities.mint_cap, - liquidity, + liquidity ) } /// Swap directly - public fun swap(pair: Object, offer_coin: FungibleAsset): FungibleAsset acquires Config, Pool { + public fun swap( + pair: Object, offer_coin: FungibleAsset + ): FungibleAsset acquires Config, Pool { let offer_amount = fungible_asset::amount(&offer_coin); let offer_metadata = fungible_asset::metadata_from_asset(&offer_coin); let offer_address = object::object_address(&offer_metadata); let pair_key = generate_pair_key(pair); assert!( offer_address == pair_key.coin_a || offer_address == pair_key.coin_b, - error::invalid_argument(ECOIN_TYPE), + error::invalid_argument(ECOIN_TYPE) ); let is_offer_a = offer_address == pair_key.coin_a; @@ -1177,7 +1185,7 @@ module minitia_std::dex { offer_weight, return_weight, fungible_asset::amount(&offer_coin), - swap_fee_rate, + swap_fee_rate ); // apply swap result to pool @@ -1191,14 +1199,14 @@ module minitia_std::dex { fungible_asset::withdraw( pair_signer, pool.coin_b_store, - return_amount, + return_amount ) } else { fungible_asset::deposit(pool.coin_b_store, offer_coin); fungible_asset::withdraw( pair_signer, pool.coin_a_store, - return_amount, + return_amount ) }; @@ -1211,7 +1219,7 @@ module minitia_std::dex { fee_amount, offer_amount, return_amount - }, + } ); return_coin @@ -1224,20 +1232,20 @@ module minitia_std::dex { bigdecimal::one(), bigdecimal::add( weights.weights_before.coin_a_weight, - weights.weights_before.coin_b_weight, - ), + weights.weights_before.coin_b_weight + ) ), - EINVALID_WEIGHTS, + EINVALID_WEIGHTS ); assert!( bigdecimal::eq( bigdecimal::one(), bigdecimal::add( weights.weights_after.coin_a_weight, - weights.weights_after.coin_b_weight, - ), + weights.weights_after.coin_b_weight + ) ), - EINVALID_WEIGHTS, + EINVALID_WEIGHTS ); } @@ -1258,19 +1266,19 @@ module minitia_std::dex { symbol, 6, string::utf8(b""), - string::utf8(b""), + string::utf8(b"") ); assert_weights(weights); assert!( bigdecimal::le(swap_fee_rate, max_fee_rate()), - error::invalid_argument(EOUT_OF_SWAP_FEE_RATE_RANGE), + error::invalid_argument(EOUT_OF_SWAP_FEE_RATE_RANGE) ); assert!( coin_address(&coin_a) != coin_address(&coin_b), - error::invalid_argument(ESAME_COIN_TYPE), + error::invalid_argument(ESAME_COIN_TYPE) ); let pair_signer = &object::generate_signer_for_extending(&extend_ref); @@ -1281,24 +1289,24 @@ module minitia_std::dex { let coin_a_store = primary_fungible_store::create_primary_store( pair_address, - fungible_asset::asset_metadata(&coin_a), + fungible_asset::asset_metadata(&coin_a) ); let coin_b_store = primary_fungible_store::create_primary_store( pair_address, - fungible_asset::asset_metadata(&coin_b), + fungible_asset::asset_metadata(&coin_b) ); let coin_a_addr = coin_address(&coin_a); let coin_b_addr = coin_address(&coin_b); move_to( pair_signer, - Pool { coin_a_store, coin_b_store }, + Pool { coin_a_store, coin_b_store } ); move_to( pair_signer, - CoinCapabilities { mint_cap, freeze_cap, burn_cap }, + CoinCapabilities { mint_cap, freeze_cap, burn_cap } ); move_to( @@ -1319,7 +1327,7 @@ module minitia_std::dex { } }, swap_fee_rate - }, + } ); let liquidity_token = @@ -1327,7 +1335,7 @@ module minitia_std::dex { object::address_to_object(pair_address), coin_a, coin_b, - option::none(), + option::none() ); // update weights @@ -1357,7 +1365,7 @@ module minitia_std::dex { liquidity_token: pair_address, weights, swap_fee_rate - }, + } ); // emit create pair event @@ -1368,7 +1376,7 @@ module minitia_std::dex { liquidity_token: pair_address, weights, swap_fee_rate - }, + } ); liquidity_token @@ -1392,13 +1400,16 @@ module minitia_std::dex { let liquidity = calculate_provide_liquidity_return_amount( - pool, pair, coin_a_amount_in, coin_b_amount_in + pool, + pair, + coin_a_amount_in, + coin_b_amount_in ); assert!( option::is_none(&min_liquidity_amount) || *option::borrow(&min_liquidity_amount) <= liquidity, - error::invalid_state(EMIN_LIQUIDITY), + error::invalid_state(EMIN_LIQUIDITY) ); event::emit( @@ -1409,7 +1420,7 @@ module minitia_std::dex { coin_a_amount: coin_a_amount_in, coin_b_amount: coin_b_amount_in, liquidity - }, + } ); fungible_asset::deposit(pool.coin_a_store, coin_a); @@ -1418,7 +1429,7 @@ module minitia_std::dex { let liquidity_token_capabilities = borrow_global(pool_addr); coin::mint( &liquidity_token_capabilities.mint_cap, - liquidity, + liquidity ) } @@ -1432,7 +1443,7 @@ module minitia_std::dex { assert!( timestamp >= weights.weights_after.timestamp, - error::invalid_state(ELBP_NOT_ENDED), + error::invalid_state(ELBP_NOT_ENDED) ) } @@ -1501,7 +1512,10 @@ module minitia_std::dex { } fun calculate_provide_liquidity_return_amount( - pool: &Pool, pair: Object, coin_a_amount_in: u64, coin_b_amount_in: u64 + pool: &Pool, + pair: Object, + coin_a_amount_in: u64, + coin_b_amount_in: u64 ): u64 { let coin_a_amount = fungible_asset::balance(pool.coin_a_store); let coin_b_amount = fungible_asset::balance(pool.coin_b_store); @@ -1539,8 +1553,9 @@ module minitia_std::dex { // provide coin type must be one of coin a or coin b coin type assert!( provide_metadata == fungible_asset::store_metadata(pool.coin_a_store) - || provide_metadata == fungible_asset::store_metadata(pool.coin_b_store), - error::invalid_argument(ECOIN_TYPE), + || provide_metadata + == fungible_asset::store_metadata(pool.coin_b_store), + error::invalid_argument(ECOIN_TYPE) ); let is_provide_a = provide_metadata == fungible_asset::store_metadata(pool.coin_a_store); @@ -1548,7 +1563,7 @@ module minitia_std::dex { let total_share = option::extract(&mut fungible_asset::supply(pair)); assert!( total_share != 0, - error::invalid_state(EZERO_LIQUIDITY), + error::invalid_state(EZERO_LIQUIDITY) ); // load values for fee and increased liquidity amount calculation @@ -1558,7 +1573,7 @@ module minitia_std::dex { let normalized_weight = bigdecimal::div( coin_a_weight, - bigdecimal::add(coin_a_weight, coin_b_weight), + bigdecimal::add(coin_a_weight, coin_b_weight) ); let pool_amount_in = fungible_asset::balance(pool.coin_a_store); (normalized_weight, pool_amount_in) @@ -1566,7 +1581,7 @@ module minitia_std::dex { let normalized_weight = bigdecimal::div( coin_b_weight, - bigdecimal::add(coin_a_weight, coin_b_weight), + bigdecimal::add(coin_a_weight, coin_b_weight) ); let pool_amount_in = fungible_asset::balance(pool.coin_b_store); @@ -1576,8 +1591,8 @@ module minitia_std::dex { // CONTRACT: cannot provide more than the pool amount to prevent huge price impact assert!( - pool_amount_in > amount_in, - error::invalid_argument(EPRICE_IMPACT), + pool_amount_in >= amount_in, + error::invalid_argument(EPRICE_IMPACT) ); // compute fee amount with the assumption that we will swap (1 - normalized_weight) of amount_in @@ -1585,14 +1600,14 @@ module minitia_std::dex { bigdecimal::mul_by_u64_truncate( bigdecimal::sub( bigdecimal::one(), - normalized_weight, + normalized_weight ), - amount_in, + amount_in ); let fee_amount = calculate_fee_with_minimum( config.swap_fee_rate, - adjusted_swap_amount, + adjusted_swap_amount ); // actual amount in after deducting fee amount @@ -1602,7 +1617,7 @@ module minitia_std::dex { let base = bigdecimal::from_ratio_u64( adjusted_amount_in + pool_amount_in, - pool_amount_in, + pool_amount_in ); let pool_ratio = pow(base, normalized_weight); let new_total_share = bigdecimal::mul_by_u128_truncate(pool_ratio, total_share); @@ -1610,10 +1625,9 @@ module minitia_std::dex { } /// get all pool info at once (a_amount, b_amount, a_weight, b_weight, fee_rate) - public fun pool_info(pair: Object, lbp_assertion: bool) - : ( - u64, u64, BigDecimal, BigDecimal, BigDecimal - ) acquires Config, Pool { + public fun pool_info( + pair: Object, lbp_assertion: bool + ): (u64, u64, BigDecimal, BigDecimal, BigDecimal) acquires Config, Pool { let pair_addr = object::object_address(&pair); let config = borrow_global(pair_addr); if (lbp_assertion) { @@ -1621,7 +1635,7 @@ module minitia_std::dex { let (_, timestamp) = get_block_info(); assert!( timestamp >= config.weights.weights_before.timestamp, - error::invalid_state(ELBP_NOT_STARTED), + error::invalid_state(ELBP_NOT_STARTED) ); }; @@ -1662,7 +1676,7 @@ module minitia_std::dex { ): (u64, u64) { assert!( amount_in > 0, - error::invalid_argument(EZERO_AMOUNT_IN), + error::invalid_argument(EZERO_AMOUNT_IN) ); let one = bigdecimal::one(); @@ -1673,13 +1687,13 @@ module minitia_std::dex { let base = bigdecimal::from_ratio_u64( pool_amount_in, - pool_amount_in + adjusted_amount_in, + pool_amount_in + adjusted_amount_in ); let sub_amount = pow(base, exp); ( bigdecimal::mul_by_u64_truncate( bigdecimal::sub(one, sub_amount), - pool_amount_out, + pool_amount_out ), fee_amount ) @@ -1709,7 +1723,9 @@ module minitia_std::dex { (amount_in, fee_amount) } - public fun pool_metadata(pair: Object): (Object, Object) acquires Pool { + public fun pool_metadata( + pair: Object + ): (Object, Object) acquires Pool { let pair_addr = object::object_address(&pair); let pool = borrow_global(pair_addr); ( @@ -1723,7 +1739,7 @@ module minitia_std::dex { fun pow(base: BigDecimal, exp: BigDecimal): BigDecimal { assert!( !bigdecimal::is_zero(base) && bigdecimal::lt(base, bigdecimal::from_u64(2)), - error::invalid_argument(EOUT_OF_BASE_RANGE), + error::invalid_argument(EOUT_OF_BASE_RANGE) ); let res = bigdecimal::one(); @@ -1782,7 +1798,7 @@ module minitia_std::dex { // comp(new) = comp(old) * a * n / (n + 1) = a ^ (n + 1) / (n + 1) comp = bigdecimal::div_by_u64( bigdecimal::mul_by_u64(bigdecimal::mul(comp, a), index), // comp * a * index - index + 1, + index + 1 ); index = index + 1; @@ -1814,10 +1830,9 @@ module minitia_std::dex { } #[test_only] - fun initialized_coin(account: &signer, symbol: String) - : ( - coin::BurnCapability, coin::FreezeCapability, coin::MintCapability - ) { + fun initialized_coin( + account: &signer, symbol: String + ): (coin::BurnCapability, coin::FreezeCapability, coin::MintCapability) { let (mint_cap, burn_cap, freeze_cap, _) = coin::initialize_and_generate_extend_ref( account, @@ -1826,7 +1841,7 @@ module minitia_std::dex { symbol, 6, string::utf8(b""), - string::utf8(b""), + string::utf8(b"") ); return (burn_cap, freeze_cap, mint_cap) @@ -1849,12 +1864,12 @@ module minitia_std::dex { coin::mint_to( &initia_mint_cap, chain_addr, - 100000000, + 100000000 ); coin::mint_to( &usdc_mint_cap, chain_addr, - 100000000, + 100000000 ); // spot price is 1 @@ -1868,7 +1883,7 @@ module minitia_std::dex { coin::metadata(chain_addr, string::utf8(b"INIT")), coin::metadata(chain_addr, string::utf8(b"USDC")), 80000000, - 20000000, + 20000000 ); let lp_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL")); @@ -1876,15 +1891,15 @@ module minitia_std::dex { assert!( coin::balance(chain_addr, init_metadata) == 20000000, - 0, + 0 ); assert!( coin::balance(chain_addr, usdc_metadata) == 80000000, - 1, + 1 ); assert!( coin::balance(chain_addr, lp_metadata) == 80000000, - 2, + 2 ); // swap init to usdc @@ -1893,15 +1908,15 @@ module minitia_std::dex { pair, init_metadata, 1000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, init_metadata) == 20000000 - 1000, - 3, + 3 ); assert!( coin::balance(chain_addr, usdc_metadata) == 80000000 + 996, - 4, + 4 ); // return 999 commission 3 // swap usdc to init @@ -1910,15 +1925,15 @@ module minitia_std::dex { pair, usdc_metadata, 1000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, init_metadata) == 20000000 - 1000 + 997, - 5, + 5 ); // return 1000 commission 3 assert!( coin::balance(chain_addr, usdc_metadata) == 80000000 + 996 - 1000, - 6, + 6 ); // withdraw liquidity @@ -1927,15 +1942,17 @@ module minitia_std::dex { pair, 40000000, option::none(), - option::none(), + option::none() ); assert!( - coin::balance(chain_addr, init_metadata) == 20000000 - 1000 + 997 + 40000001, - 7, + coin::balance(chain_addr, init_metadata) == 20000000 - 1000 + 997 + + 40000001, + 7 ); assert!( - coin::balance(chain_addr, usdc_metadata) == 80000000 + 996 - 1000 + 10000002, - 8, + coin::balance(chain_addr, usdc_metadata) == 80000000 + 996 - 1000 + + 10000002, + 8 ); // single asset provide liquidity (coin b) @@ -1945,11 +1962,11 @@ module minitia_std::dex { pair, usdc_metadata, 100000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, lp_metadata) == 40000000 + 79491, - 9, + 9 ); // single asset provide liquidity (coin a) @@ -1959,11 +1976,11 @@ module minitia_std::dex { pair, init_metadata, 100000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, lp_metadata) == 40000000 + 79491 + 80090, - 10, + 10 ); move_to( @@ -1972,7 +1989,7 @@ module minitia_std::dex { burn_cap: initia_burn_cap, freeze_cap: initia_freeze_cap, mint_cap: initia_mint_cap - }, + } ); move_to( @@ -1981,7 +1998,7 @@ module minitia_std::dex { burn_cap: usdc_burn_cap, freeze_cap: usdc_freeze_cap, mint_cap: usdc_mint_cap - }, + } ); } @@ -2002,12 +2019,12 @@ module minitia_std::dex { coin::mint_to( &initia_mint_cap, chain_addr, - 100000000, + 100000000 ); coin::mint_to( &usdc_mint_cap, chain_addr, - 100000000, + 100000000 ); set_block_info(10, 1000); @@ -2026,21 +2043,21 @@ module minitia_std::dex { init_metadata, usdc_metadata, 80000000, - 20000000, + 20000000 ); let lp_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL")); let pair = object::convert(lp_metadata); assert!( get_spot_price(pair, init_metadata) == bigdecimal::from_ratio_u64(2475, 100), - 0, + 0 ); // 0.8 : 0.2 set_block_info(11, 2500); assert!( get_spot_price(pair, init_metadata) == bigdecimal::one(), - 1, + 1 ); // 0.61 : 0.39 @@ -2048,20 +2065,20 @@ module minitia_std::dex { assert!( get_spot_price(pair, init_metadata) == bigdecimal::from_ratio_u64(391025641025641025, 1000000000000000000), - 2, + 2 ); assert!( coin::balance(chain_addr, init_metadata) == 20000000, - 0, + 0 ); assert!( coin::balance(chain_addr, usdc_metadata) == 80000000, - 1, + 1 ); assert!( coin::balance(chain_addr, lp_metadata) == 80000000, - 3, + 3 ); // swap test during LBP (0.8: 0.2) @@ -2073,15 +2090,15 @@ module minitia_std::dex { pair, init_metadata, 1000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, init_metadata) == 20000000 - 1000, - 4, + 4 ); assert!( coin::balance(chain_addr, usdc_metadata) == 80000000 + 996, - 5, + 5 ); // return 999 commission 3 // swap usdc to init @@ -2090,15 +2107,15 @@ module minitia_std::dex { pair, usdc_metadata, 1000, - option::none(), + option::none() ); assert!( coin::balance(chain_addr, init_metadata) == 20000000 - 1000 + 997, - 6, + 6 ); // return 1000 commission 3 assert!( coin::balance(chain_addr, usdc_metadata) == 80000000 + 996 - 1000, - 7, + 7 ); move_to( @@ -2107,7 +2124,7 @@ module minitia_std::dex { burn_cap: initia_burn_cap, freeze_cap: initia_freeze_cap, mint_cap: initia_mint_cap - }, + } ); move_to( @@ -2116,7 +2133,7 @@ module minitia_std::dex { burn_cap: usdc_burn_cap, freeze_cap: usdc_freeze_cap, mint_cap: usdc_mint_cap - }, + } ); } @@ -2140,7 +2157,7 @@ module minitia_std::dex { assert!( coin_a_weight == bigdecimal::from_ratio_u64(2, 10) && coin_b_weight == bigdecimal::from_ratio_u64(8, 10), - 0, + 0 ); set_block_info(15, 1500); @@ -2148,7 +2165,7 @@ module minitia_std::dex { assert!( coin_a_weight == bigdecimal::from_ratio_u64(5, 10) && coin_b_weight == bigdecimal::from_ratio_u64(5, 10), - 1, + 1 ); set_block_info(20, 2000); @@ -2156,7 +2173,7 @@ module minitia_std::dex { assert!( coin_a_weight == bigdecimal::from_ratio_u64(8, 10) && coin_b_weight == bigdecimal::from_ratio_u64(2, 10), - 2, + 2 ); set_block_info(30, 3000); @@ -2164,7 +2181,7 @@ module minitia_std::dex { assert!( coin_a_weight == bigdecimal::from_ratio_u64(8, 10) && coin_b_weight == bigdecimal::from_ratio_u64(2, 10), - 3, + 3 ); } @@ -2189,17 +2206,17 @@ module minitia_std::dex { coin::mint_to( &coin_a_mint_cap, chain_addr, - 100000000, + 100000000 ); coin::mint_to( &coin_b_mint_cap, chain_addr, - 100000000, + 100000000 ); coin::mint_to( &coin_c_mint_cap, chain_addr, - 100000000, + 100000000 ); create_pair_script( @@ -2212,7 +2229,7 @@ module minitia_std::dex { a_metadata, b_metadata, 1, - 1, + 1 ); let lp_1_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL1")); let pair_1 = object::convert(lp_1_metadata); @@ -2228,7 +2245,7 @@ module minitia_std::dex { a_metadata, b_metadata, 1, - 1, + 1 ); let lp_2_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL2")); let pair_2 = object::convert(lp_2_metadata); @@ -2244,7 +2261,7 @@ module minitia_std::dex { a_metadata, c_metadata, 1, - 1, + 1 ); let lp_3_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL3")); let pair_3 = object::convert(lp_3_metadata); @@ -2260,7 +2277,7 @@ module minitia_std::dex { a_metadata, c_metadata, 1, - 1, + 1 ); let lp_4_metadata = coin::metadata(chain_addr, string::utf8(b"SYMBOL4")); let pair_4 = object::convert(lp_4_metadata); @@ -2286,7 +2303,7 @@ module minitia_std::dex { option::none(), option::none(), option::none(), - 10, + 10 ); assert!( res @@ -2318,8 +2335,9 @@ module minitia_std::dex { liquidity_token: pair_4_addr, weights, swap_fee_rate - }], - 0, + } + ], + 0 ); let res = @@ -2327,7 +2345,7 @@ module minitia_std::dex { option::some(a_addr), option::some(b_addr), option::some(pair_1_addr), - 10, + 10 ); assert!( res @@ -2352,8 +2370,9 @@ module minitia_std::dex { liquidity_token: pair_4_addr, weights, swap_fee_rate - }], - 1, + } + ], + 1 ); let res = @@ -2361,7 +2380,7 @@ module minitia_std::dex { option::some(a_addr), option::some(a_addr), option::some(pair_1_addr), - 10, + 10 ); assert!( res @@ -2393,15 +2412,16 @@ module minitia_std::dex { liquidity_token: pair_4_addr, weights, swap_fee_rate - }], - 2, + } + ], + 2 ); let res = get_pairs( a_addr, b_addr, option::none(), - 10, + 10 ); assert!( res @@ -2419,15 +2439,16 @@ module minitia_std::dex { liquidity_token: pair_2_addr, weights, swap_fee_rate - }], - 3, + } + ], + 3 ); let res = get_pairs( a_addr, b_addr, option::some(pair_1_addr), - 10, + 10 ); assert!( res @@ -2438,8 +2459,9 @@ module minitia_std::dex { liquidity_token: pair_2_addr, weights, swap_fee_rate - }], - 3, + } + ], + 3 ); } } diff --git a/precompile/modules/minitia_stdlib/sources/object.move b/precompile/modules/minitia_stdlib/sources/object.move index 70cd864c..8e90c065 100644 --- a/precompile/modules/minitia_stdlib/sources/object.move +++ b/precompile/modules/minitia_stdlib/sources/object.move @@ -28,6 +28,7 @@ module minitia_std::object { use minitia_std::event; friend minitia_std::primary_fungible_store; + friend minitia_std::nft; /// An object already exists at this address const EOBJECT_EXISTS: u64 = 1; @@ -243,6 +244,16 @@ module minitia_std::object { create_object_internal(creator_address, obj_addr, true) } + /// Create a new object to represent an NFT and return the ConstructorRef. + /// Nft objects can be queried globally by knowing the user generated seed used to create them + /// and the creator's address. NFT objects can be deleted. + public(friend) fun create_nft_object( + owner: address, creator: address, seed: vector + ): ConstructorRef acquires Tombstone { + let obj_addr = create_object_address(&creator, seed); + create_object_internal(owner, obj_addr, true) + } + /// Create a new object whose address is derived based on the creator account address and another object. /// Derivde objects, similar to named objects, cannot be deleted. public(friend) fun create_user_derived_object( diff --git a/precompile/modules/minitia_stdlib/sources/token/collection.move b/precompile/modules/minitia_stdlib/sources/token/collection.move index 301d2683..e151d53e 100644 --- a/precompile/modules/minitia_stdlib/sources/token/collection.move +++ b/precompile/modules/minitia_stdlib/sources/token/collection.move @@ -271,9 +271,6 @@ module minitia_std::collection { ) }; - let transfer_ref = object::generate_transfer_ref(&constructor_ref); - object::disable_ungated_transfer(&transfer_ref); - constructor_ref } @@ -537,15 +534,8 @@ module minitia_std::collection { assert!(count(collection) == option::some(0), 0); } - #[test(creator = @0x123)] - #[expected_failure(abort_code = 0x10007, location = Self)] - fun test_create_collection_with_invalid_name(creator: &signer) { - create_collection_helper(creator, string::utf8(b"collection::hello")); - } - - #[test(creator = @0x123, trader = @0x456)] - #[expected_failure(abort_code = 0x50003, location = minitia_std::object)] - entry fun test_create_and_transfer(creator: &signer, trader: &signer) { + #[test(creator = @0x123, receipient = @0x456)] + entry fun test_create_and_transfer(creator: &signer, receipient: &signer) { let creator_address = signer::address_of(creator); let collection_name = string::utf8(b"collection name"); create_collection_helper(creator, collection_name); @@ -561,10 +551,17 @@ module minitia_std::collection { object::transfer( creator, collection, - signer::address_of(trader) + signer::address_of(receipient) ); } + #[test(creator = @0x123)] + #[expected_failure(abort_code = 0x10007, location = Self)] + fun test_create_collection_with_invalid_name(creator: &signer) { + create_collection_helper(creator, string::utf8(b"collection::hello")); + } + + #[test(creator = @0x123)] #[expected_failure(abort_code = 0x80001, location = minitia_std::object)] entry fun test_duplicate_collection(creator: &signer) { diff --git a/precompile/modules/minitia_stdlib/sources/token/initia_nft.move b/precompile/modules/minitia_stdlib/sources/token/initia_nft.move index 2e2be06f..54d7dc5d 100644 --- a/precompile/modules/minitia_stdlib/sources/token/initia_nft.move +++ b/precompile/modules/minitia_stdlib/sources/token/initia_nft.move @@ -13,7 +13,7 @@ module minitia_std::initia_nft { use std::string::String; use std::signer; use minitia_std::object::{Self, ConstructorRef, ExtendRef, Object}; - use minitia_std::collection; + use minitia_std::collection::{Self, Collection}; use minitia_std::royalty; use minitia_std::nft; use minitia_std::bigdecimal::BigDecimal; @@ -205,16 +205,17 @@ module minitia_std::initia_nft { fun mint_internal( creator: &signer, - collection: String, + collection_name: String, description: String, token_id: String, uri: String, can_burn: bool ): ConstructorRef acquires InitiaNftCollection { + let collection_obj = collection_object(creator, &collection_name); let constructor_ref = nft::create( creator, - collection, + object::convert(collection_obj), description, token_id, option::none(), @@ -222,8 +223,6 @@ module minitia_std::initia_nft { ); let object_signer = object::generate_signer(&constructor_ref); - - let collection_obj = collection_object(creator, &collection); let collection = borrow_collection(collection_obj); let mutator_ref = diff --git a/precompile/modules/minitia_stdlib/sources/token/nft.move b/precompile/modules/minitia_stdlib/sources/token/nft.move index b0a6e1e5..a786f606 100644 --- a/precompile/modules/minitia_stdlib/sources/token/nft.move +++ b/precompile/modules/minitia_stdlib/sources/token/nft.move @@ -31,6 +31,8 @@ module minitia_std::nft { const EQUERY_LENGTH_TOO_LONG: u64 = 8; /// The provided token id is invalid const EINVALID_TOKEN_ID: u64 = 9; + /// The calling signer is not the owner + const ENOT_OWNER: u64 = 10; const MAX_NFT_TOKEN_ID_LENGTH: u64 = 128; const MAX_URI_LENGTH: u64 = 512; @@ -94,14 +96,19 @@ module minitia_std::nft { } inline fun create_common( + owner: &signer, constructor_ref: &ConstructorRef, - creator_address: address, - collection_name: String, + collection: Object, description: String, token_id: String, royalty: Option, uri: String ) { + // only the collection owner can create nfts + assert!( + object::owner(collection) == signer::address_of(owner), + error::unauthenticated(ENOT_OWNER) + ); assert_token_id(&token_id); assert!( string::length(&description) <= MAX_DESCRIPTION_LENGTH, @@ -113,10 +120,6 @@ module minitia_std::nft { ); let object_signer = object::generate_signer(constructor_ref); - - let collection_addr = - collection::create_collection_address(creator_address, &collection_name); - let collection = object::address_to_object(collection_addr); collection::increment_supply( collection, token_id, @@ -137,21 +140,24 @@ module minitia_std::nft { /// Creates a new nft object from a nft name and returns the ConstructorRef for /// additional specialization. public fun create( - creator: &signer, - collection_name: String, + owner: &signer, + collection: Object, description: String, token_id: String, royalty: Option, uri: String ): ConstructorRef { - let creator_address = signer::address_of(creator); + let owner_address = signer::address_of(owner); + let creator_address = collection::creator(collection); + let collection_name = collection::name(collection); let seed = create_nft_seed(&collection_name, &token_id); - let constructor_ref = object::create_deletable_named_object(creator, seed); + let constructor_ref = + object::create_nft_object(owner_address, creator_address, seed); create_common( + owner, &constructor_ref, - creator_address, - collection_name, + collection, description, token_id, royalty, @@ -354,13 +360,53 @@ module minitia_std::nft { #[test_only] use minitia_std::bigdecimal; + #[test_only] + fun generate_collection_object( + creator: &signer, collection_name: &String + ): Object { + let creator_address = signer::address_of(creator); + let collection_address = + collection::create_collection_address(creator_address, collection_name); + object::address_to_object(collection_address) + } + + #[test(creator = @0x123, owner = @0x456, trader = @0x789)] + fun test_create_after_collection_transfer( + creator: &signer, owner: &signer + ) { + let collection_name = string::utf8(b"collection name"); + let token_id = string::utf8(b"nft token_id"); + + create_collection_helper(creator, collection_name, 1); + + // transfer collection to owner + let owner_address = signer::address_of(owner); + object::transfer( + creator, + generate_collection_object(creator, &collection_name), + owner_address + ); + + // create nft + create_nft_helper(owner, creator, collection_name, token_id); + + let creator_address = signer::address_of(creator); + let nft_addr = create_nft_address( + creator_address, + &collection_name, + &token_id + ); + let nft = object::address_to_object(nft_addr); + assert!(object::owner(nft) == owner_address, 1); + } + #[test(creator = @0x123, trader = @0x456)] fun test_create_and_transfer(creator: &signer, trader: &signer) acquires Nft { let collection_name = string::utf8(b"collection name"); let token_id = string::utf8(b"nft token_id"); create_collection_helper(creator, collection_name, 1); - create_nft_helper(creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id); let creator_address = signer::address_of(creator); let nft_addr = create_nft_address( @@ -413,7 +459,7 @@ module minitia_std::nft { create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -447,7 +493,7 @@ module minitia_std::nft { create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -470,7 +516,7 @@ module minitia_std::nft { let collection_name = string::utf8(b"collection name"); let token_id = string::utf8(b"nft token_id::hello"); create_collection_helper(creator, collection_name, 1); - create_nft_helper(creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id); } #[test(creator = @0x123)] @@ -481,8 +527,8 @@ module minitia_std::nft { let token_id2 = string::utf8(b"nft token_id2"); create_collection_helper(creator, collection_name, 1); - create_nft_helper(creator, collection_name, token_id); - create_nft_helper(creator, collection_name, token_id2); + create_nft_helper(creator, creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id2); } #[test(creator = @0x123)] @@ -492,8 +538,8 @@ module minitia_std::nft { let token_id = string::utf8(b"nft token_id"); create_collection_helper(creator, collection_name, 2); - create_nft_helper(creator, collection_name, token_id); - create_nft_helper(creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id); + create_nft_helper(creator, creator, collection_name, token_id); } #[test(creator = @0x123)] @@ -549,7 +595,7 @@ module minitia_std::nft { let constructor_ref = create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -573,7 +619,7 @@ module minitia_std::nft { let constructor_ref = create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::some( @@ -603,7 +649,7 @@ module minitia_std::nft { let constructor_ref = create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -617,7 +663,7 @@ module minitia_std::nft { // mint again create( creator, - collection_name, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::none(), @@ -642,11 +688,14 @@ module minitia_std::nft { #[test_only] fun create_nft_helper( - creator: &signer, collection_name: String, token_id: String + owner: &signer, + creator: &signer, + collection_name: String, + token_id: String ): ConstructorRef { create( - creator, - collection_name, + owner, + generate_collection_object(creator, &collection_name), string::utf8(b"nft description"), token_id, option::some( @@ -663,7 +712,9 @@ module minitia_std::nft { fun create_nft_with_mutation_ref( creator: &signer, collection_name: String, token_id: String ): MutatorRef { - let constructor_ref = create_nft_helper(creator, collection_name, token_id); + let constructor_ref = create_nft_helper( + creator, creator, collection_name, token_id + ); generate_mutator_ref(&constructor_ref) } } diff --git a/precompile/modules/minitia_stdlib/sources/token/soul_bound_token.move b/precompile/modules/minitia_stdlib/sources/token/soul_bound_token.move index c56023b3..727f65a9 100644 --- a/precompile/modules/minitia_stdlib/sources/token/soul_bound_token.move +++ b/precompile/modules/minitia_stdlib/sources/token/soul_bound_token.move @@ -6,7 +6,7 @@ module minitia_std::soul_bound_token { use std::string::String; use std::signer; use minitia_std::object::{Self, ConstructorRef, Object}; - use minitia_std::collection; + use minitia_std::collection::{Self, Collection}; use minitia_std::property_map; use minitia_std::royalty; use minitia_std::nft; @@ -203,7 +203,7 @@ module minitia_std::soul_bound_token { fun mint_internal( creator: &signer, - collection: String, + collection_name: String, description: String, name: String, uri: String, @@ -211,10 +211,11 @@ module minitia_std::soul_bound_token { property_types: vector, property_values: vector> ): ConstructorRef acquires SoulBoundTokenCollection { + let collection_obj = collection_object(creator, &collection_name); let constructor_ref = nft::create( creator, - collection, + object::convert(collection_obj), description, name, option::none(), @@ -224,7 +225,6 @@ module minitia_std::soul_bound_token { let object_signer = object::generate_signer(&constructor_ref); - let collection_obj = collection_object(creator, &collection); let collection = borrow_collection(collection_obj); let mutator_ref =