From 8c31f8e6f56ebed5909c0e448e2758ce988aadbe Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 28 Jan 2025 14:33:24 -0500 Subject: [PATCH] fix(interchain-token): unimplemented notice for SAC methods (#225) Co-authored-by: ahramy --- .../src/tests/testdata/interchain_token.wasm | Bin 11825 -> 0 bytes .../src/contract.rs | 62 +++-- .../src/error.rs | 7 +- .../src/testdata/interchain_token.wasm | Bin 9593 -> 0 bytes .../stellar_interchain_token.optimized.wasm | Bin 0 -> 9564 bytes .../stellar_token_manager.optimized.wasm | Bin 0 -> 2330 bytes .../src/testdata/token_manager.wasm | Bin 2328 -> 0 bytes .../src/tests/deploy_interchain_token.rs | 55 ++++- .../src/tests/execute.rs | 6 +- .../src/testutils.rs | 6 +- .../stellar-interchain-token/src/contract.rs | 60 +++-- .../src/tests/test.rs | 223 ++++++++++++------ ...r.golden => remove_minter_succeeds.golden} | 0 .../tests/testdata/set_admin_succeeds.golden | 11 + .../transfer_ownership_succeeds.golden | 11 + 15 files changed, 288 insertions(+), 153 deletions(-) delete mode 100755 contracts/stellar-example/src/tests/testdata/interchain_token.wasm delete mode 100755 contracts/stellar-interchain-token-service/src/testdata/interchain_token.wasm create mode 100644 contracts/stellar-interchain-token-service/src/testdata/stellar_interchain_token.optimized.wasm create mode 100644 contracts/stellar-interchain-token-service/src/testdata/stellar_token_manager.optimized.wasm delete mode 100644 contracts/stellar-interchain-token-service/src/testdata/token_manager.wasm rename contracts/stellar-interchain-token/src/tests/testdata/{remove_minter.golden => remove_minter_succeeds.golden} (100%) create mode 100644 contracts/stellar-interchain-token/src/tests/testdata/set_admin_succeeds.golden create mode 100644 contracts/stellar-interchain-token/src/tests/testdata/transfer_ownership_succeeds.golden diff --git a/contracts/stellar-example/src/tests/testdata/interchain_token.wasm b/contracts/stellar-example/src/tests/testdata/interchain_token.wasm deleted file mode 100755 index 20da80083061b02381f621abf3424ea8a4d6f9f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11825 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 Result, ContractError> { caller.require_auth(); - ensure!(initial_supply >= 0, ContractError::InvalidSupply); + ensure!(initial_supply >= 0, ContractError::InvalidInitialSupply); let token_id = Self::interchain_token_id(env, caller.clone(), salt); token_metadata.validate()?; - Self::deploy_token( - env, - token_id.clone(), - token_metadata, - minter, - Some((initial_supply, caller)), - ); + let token_address = Self::deploy_token(env, token_id.clone(), token_metadata, minter)?; + + if initial_supply > 0 { + StellarAssetClient::new(env, &token_address).mint(&caller, &initial_supply); + } Ok(token_id) } @@ -272,14 +270,9 @@ impl InterchainTokenServiceInterface for InterchainTokenService { ) -> Result, ContractError> { let token_id = Self::canonical_interchain_token_id(env, token_address.clone()); - ensure!( - !env.storage() - .persistent() - .has(&DataKey::TokenIdConfig(token_id.clone())), - ContractError::TokenAlreadyRegistered - ); + Self::ensure_token_not_registered(env, token_id.clone())?; - let _ = Self::deploy_token_manager( + let _: Address = Self::deploy_token_manager( env, token_id.clone(), token_address, @@ -696,17 +689,12 @@ impl InterchainTokenService { minter, }: DeployInterchainToken, ) -> Result<(), ContractError> { - ensure!( - Self::token_id_config(env, token_id.clone()).is_err(), - ContractError::TokenAlreadyDeployed - ); - let token_metadata = TokenMetadata::new(name, symbol, decimals as u32)?; // Note: attempt to convert a byte string which doesn't represent a valid Soroban address fails at the Host level let minter = minter.map(|m| Address::from_string_bytes(&m)); - Self::deploy_token(env, token_id, token_metadata, minter, None); + let _: Address = Self::deploy_token(env, token_id, token_metadata, minter)?; Ok(()) } @@ -744,14 +732,14 @@ impl InterchainTokenService { /// * `token_id` - The token ID for the interchain token being deployed. /// * `token_metadata` - The metadata for the interchain token being deployed. /// * `minter` - An optional address of an additional minter for the interchain token being deployed. - /// * `initial_mint` - The initial mint amount and recipient for the interchain token being deployed. fn deploy_token( env: &Env, token_id: BytesN<32>, token_metadata: TokenMetadata, minter: Option
, - initial_mint: Option<(i128, Address)>, - ) { + ) -> Result { + Self::ensure_token_not_registered(env, token_id.clone())?; + let token_address = deployer::deploy_interchain_token( env, Self::interchain_token_wasm_hash(env), @@ -764,20 +752,24 @@ impl InterchainTokenService { let token_manager = Self::deploy_token_manager( env, token_id, - token_address, + token_address.clone(), TokenManagerType::NativeInterchainToken, ); - match initial_mint { - Some((initial_supply, recipient)) if initial_supply > 0 => { - StellarAssetClient::new(env, &interchain_token_client.address) - .mint(&recipient, &initial_supply); - } - _ => {} - } - - // Transfer minter role to the token manager + // Give minter role to the token manager interchain_token_client.add_minter(&token_manager); - interchain_token_client.remove_minter(&env.current_contract_address()); + + Ok(token_address) + } + + fn ensure_token_not_registered(env: &Env, token_id: BytesN<32>) -> Result<(), ContractError> { + ensure!( + !env.storage() + .persistent() + .has(&DataKey::TokenIdConfig(token_id)), + ContractError::TokenAlreadyRegistered + ); + + Ok(()) } } diff --git a/contracts/stellar-interchain-token-service/src/error.rs b/contracts/stellar-interchain-token-service/src/error.rs index 9b2186ce..4bffb77e 100644 --- a/contracts/stellar-interchain-token-service/src/error.rs +++ b/contracts/stellar-interchain-token-service/src/error.rs @@ -23,7 +23,7 @@ pub enum ContractError { NotHubAddress = 15, InvalidTokenMetaData = 16, InvalidTokenId = 17, - TokenAlreadyDeployed = 18, + TokenAlreadyRegistered = 18, InvalidFlowLimit = 19, FlowLimitExceeded = 20, FlowAmountOverflow = 21, @@ -33,9 +33,8 @@ pub enum ContractError { InvalidTokenName = 25, InvalidTokenSymbol = 26, InvalidTokenDecimals = 27, - TokenAlreadyRegistered = 28, - ContractPaused = 29, - InvalidSupply = 30, + ContractPaused = 28, + InvalidInitialSupply = 29, } impl_not_approved_error!(ContractError); diff --git a/contracts/stellar-interchain-token-service/src/testdata/interchain_token.wasm b/contracts/stellar-interchain-token-service/src/testdata/interchain_token.wasm deleted file mode 100755 index 5fb4601b7ae21d20e368529cc2ad8c7ef831c2d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9593 zcmcIqTWnm%d7e4v?1elcHL@R-_O@{+fJv##>*P>obx+0)N3eK9s&Kyp44+ z<85ph5HTZ7WkbyP8|xS#kg!w4j6iK;eFKABa)XU^G=dF$m|nmT2L*--+yr&(3OF|8 zhPSa-F3Snwh7`+I+jUBAyIp*V;|-nLZY_>zB%nWNu-lzT6R?4Vv|pL_A&WTwj@OEsLpgx3!cs zYYPn#^(}O2D+|po7CqU=ik*GBzusuLfoBKW!0>9~+Mny|B;8uQ0dVp8f-TPun!dH} zl~#MIj#yWU8|^QDyq)Vhgy0V02~(7M(Hnr9!%Xg3|5D+{aQ^Zm72qu#C8l4iq~VXby0 zsjt>%>z%~+`v30GsZR(k)IKe>cQ4kj$H8swrxhiJ!}lPp4R8H15xpyxD)ON4*1akF zpBAH@h~(0sFFnXQ`Xfj&>Qe_sJS-*-`D()doUN57{3+DD=r?`^AQ+COmLgAmLTN1z z`GOM;`Q;sch!M3I{TUZ3sRLZ3Y+;mz8QS3yMlVKxMVR6y7;RxjcSyoi7o+c`>!=Z~ zU$w)Nq4w?uQ-Uz>ip7H>ye8!a=+l>MacB|rO!#0a2tiw=?N}CK5psao9~3)75r%-$QtgW6#qhh*^DLWz zhIdeaUw$<7Y%m3;l^3|yRCu6hRSdNB&vO}2S$3s;d!W%q$+{N;Gh}O0rf)t97?Bk6aEld0Z_;O(Z5rM zo(`xN(3n_YA+*TD@FW=s-HD%m<4!E5QMqZqsn_BU(oekDh6Dkm!U6!wugBqS$N>T~ z6osRpSo>f!6hb%y$SgrROBf6lHjCk^{&q0B`OT-*-tb${dXVFh@qK4OsT}dO7zrr4 zV9MKwqz(=XQSqYhi)RMJFnCg3iawBEiu%tRxqe&N=CEzPmo^_cJ5T`ZXO^hzUUY1U z`s_txOW{2VN*Chd>$gV=&;_}K9fL?Njs#Q*jDWns+dFfVWCZL8k6JZXOBElyhnA2F zzNR$6MjzZg(&G!-iY%^Xlc1DjqX7Xr<;6&Dn&8<9WK-mKqqpG7sr%s`@=ZJ+OvSe# zoF52dE-GFE+i(aCP|qZ zHMVlM5}d6R_KyHH@COyzL&hh|+~Bwes^Ijj;eVQtL8^1wi6}FG%n_@d!(rRlj5cmp z%e&McXw-Lvbq(RObVA^~F#@|8l>7}yDHg_8sy^t5RQ)3oWR}iw^HfATnljgQySDyQzS2F|H6KVLBphIt_2P6prC&!%DO<01<~n1?T3)O zDR3${cR%P#(ZOTRXr*)pR29L`K$qYt(6<+~82$Mdqd&0Kf^`AcU35xa3Pn!#t4|sa zQh_DCVw?vAhF$!ZfmZS2e%fE27PK)5ba=06K)psuzIg979gKQI>QI{jO0m51I$mMgn$e2+32AQh%(|C3gj8 zR?~>_jVu^DgW;w$7%+Yan?gv~mbQzXgLG%3-E45@yn0uS7ct5_LieY=JMqRHl;FgT z3Vtr#W9N+<74Ob1*f695U2^Uk4jOC18}}4jgkQ_zn8T1{3-xKD#b8s^@)gX`q?*!s znXI+~(Sj5BA$_UmlRcGX5>YcrtOqAGJhRvDWBfB|rkh*a+cC2oLcDn<>4OYf*Ri5$UlpdY2;nG1tM{mGw z20+hi0mB!pZ%*~k5y3EM$(e4$MAcENdDbROGC}1-rx5`-g{DH&>Da>}zRf85jenB1 z^7c0(J$M@q~SDD;4Q)pQKT@2KP0&4@kCAq>i^wM_8Wbq=J}r#n zaRL>TlQ071DAKVb03(y(GcC9kXb&(vnNLJ}++}V8F*2E?B>^L3vp^B6KzkrTCeLV^ zoalxl2@VnZ_|Z}_=0X|4Qp)f}jB92=MqR8Amz0wX{F6G2omu`OkRh*FKapywW$4*V zBbnZ95n24^kKg~zKm6Ul`8O~V86Gxs(!++#0I{i+5V;iodL|s}+CPF4_+6*%M!BF2 zzEwO+6!)QnMKmH&h*VTJAL=GS1dL1l0%|M|(-Wa2d2n*3egb%st(LsYIo={2%43AR zjcujQ-R@17&%Lo4_l8g4-qNT#gYsd+NYDEMU3mm*DbD?0;MIPW_rtpby^vG(6+FhL7te>~L877`UXow|r-+IqK zwkIkzV2%{4x9rIxn7;R+^e)^BqMZ&}qSdK;;o&SQNIDkUy746p*Al*gry@1vtoEhM z@_$B~r5(~*o$;z?R3()UJ=~JT0`AS8xBTJAGd6GvT(L$#PvGCbm?VWI%)VfEko{rA7}_uria z=f<%w7~-Yj!(@^&J>&Ei0H*?>#=`wnxBr&65O*_Tm}fNi9R+k-3<(Hugqv>tHil z5asbA0%qWj+UD@WaiiT4t_hG!AjU%9F_XsO^=1`ZlEjsn#n_7locRi14s@vlhh{0E zL-UxMM=JUUOU);9o*()I^8<$J051Fxabb-%K!)G*Jz?Y%O5|~0z4^8XzvG*WUcJ!J z%~n^>x7L~sR<0*qy|A*noUA0c`E69k#>d7ZclR9I>*jOjQuq=tiqACaE123%UR_{lNo)rzPyH8U2`p--fi<@^v&rj2R{}Ux~Gq=QgWv$cIvx&wvwQj7n7n+wf?w@g$JlAE#UPNQ}>`uAnu9@3O zBfXJsoFUHh^|_=|JI~ALT>G54s>ZU|Q3%ic3gM%a_+!aFmhO+dVZD%+#FcIGie))1 z`@}o@1|6~yARUr6=La)mH^&on!0^YaUH3((z6X*ps!#&UNaCe>&5JD#wfz9z70KJsICT4%A{ zD+Bv8zBQ9;HKQ!--8`4=++z-#nMMTW!KMdaa^?aU?$7l@{JAfC2to%nhTq4nL71Yo zSyr?4c5P$99Cz!o&M$j%Lfz5p;kAh0b~yGPrmW@?ZYz#nx5e&^$J^m&0PwTrb<1=+ z9Jlg|ga4Pk?916l%jbT0=APROj?O+F1jqZW+k3h*%NiLt`VGD2J+C*WY}Nd_M?Tj@ z`Q^uZZ+{j7*`nR`GqFpeKv=W+1k^nrN&2!H=< zRuZLmQEunw?645|2TS%jytdoh&Drv-KfUJ1^XqVbM=N3M3vF-DkCPAQxUcuD+nZmo56K3^ z(OIhXoKoJ$5opVMu$kv{KJTmDS~pzf9P;btkYMG>Ot&`~P734i86ziFDr!PgPqD#??u6J}@FD2JmSwKSA(e0#@w67u) zn{TzX;g@#&8q07#1@4_quCq<~IGX#m#A5Ut{;I6MH_^>0uhK{e|x`pm|u508r-^DW6xIKpI>KS8(nigvXrK@MB3@S z{dXHZeariH>R^8Df?3;;ocWCsvTMk+!(L|}TlwJVAWW$zqD^ZfpX%kks|n5kHp_gF zxyTiJbIm{C==q5I?YS#A??N3~Y^42QbNA%t_VV&acex}wX?C0?BkDUWl8p? z!9u%(f0|+-MDfVj6V>A<8skTg c%pW;2KUl_*D1WL+$~vHP%Gau2QL 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 literal 0 HcmV?d00001 diff --git a/contracts/stellar-interchain-token-service/src/testdata/stellar_token_manager.optimized.wasm b/contracts/stellar-interchain-token-service/src/testdata/stellar_token_manager.optimized.wasm new file mode 100644 index 0000000000000000000000000000000000000000..dfdc91715bcfe1daa70353e4127ef34307514cf1 GIT binary patch literal 2330 zcma)7Pj4GV6n`_b{*!E+jO!##3@vLnLgIk2LlXzMWEYwUMG#VfIBqufI*FaE(_JTN zFSezGd2v;KaSWH@j;)P^7HvnfL$A`_0T7aDqby0IW2& z&AP0+&AT)L{zX@jyw^j9b%9hsM!jtS{@EmRSQ#Db%m%>@*a|Rqf;49jSq14GlmN_~-2j-8_ zd`WYvt0xcL!?yp3L&|;V){Z+aOlEAm*7kyqe_ZRdeUORvsptA|<3`7KyrALwwh{z; z`$v#Vow$Cm-}YcOb$qnzJ9QW4QiuCUVW?NItlQ(ZU3WT;?Rs^=blcu@og;h430%R) zAE$DkGuGp)23RlHne_|Y&w#RZ!)hIrfnPQw}E5iV74A5s`m|9yUk<40LPlGg& z%$kNrN0Y<4fzSMLk2`hbJO`cEbyk}zeHvx*={1w(RczAIOTXe?F@l930_YkuAko_%U=QlDM+P>GC z;B*LC`!JHc)U8vf%i^M0hg=lj@z)W;%aE(+83K5lo+gM;>|TPIblCwkDzkQOAs zFCAw@I;SL_D1Johy0$HZOrC4SJKiNTNJOGSmlple4ul4dC8DL!!ao)hdTJMkAz zh9ZZb8Bj;|c`Rqx~Va%M;TK5>qN z@bq5KcY4Hkc$;o#0*Cx>pwQTK^zouvWVgUfbBneYRAx|IbaE2Wji5?#aaOZ!1k zZ-wf&7Ex1LzEiH3W&WDqAx@#xx z#g3GaKY~-mg;P1eKTw2(5EWAX1H_REIB_rU&F>0nD|I*DS&)cqxf8A3UQXr3hWrFrA_9@H z2&(Oxc+nV6C^GhwP3g=== ze?phQNLa~>b39$b#Il8p`ofw#pTa}@pZy-wIneHWMLD1l8-k_c_!hfV%!k)JS&ytha zUeLSV369+QG4?{x@E?Df_af~FzK`ukeJoN=M`7CD$L)@JaL_(+8>EW$L=PGn(t-r| zrQ?i9Pb&%LY<9gm(%G1I{G9+@o<}^!6u*ab>JsOdtt&Exf3-{{j@J~mPbG_U$9|(a zl9A7-&C6PDY)A8$=bnV!n7FIH#1FbNG5Aq@si<%m^z2AS(hS8s!RO81b%I@GC;r08 zP~>nJba@oTwN8eeAKwGj^T}BJ8M&Yh;TiM)i)kE-+!CL*Tdo%d;8C2E(`fi^fLk@J zH|ZR#>UF$c&g_WaCr&pA zPw(}7XGVO7x9N5^KH9E#eCT!@b)Tr#IU1lnxNKLH2|vJxTUkJ|TwKAM9Wpqk{UB(x zLiL-KO0iU2zFVr_t2XLQr@T@vEx+BU+$%TB<>q#2rBZ#X)LdS!ZZ9plE2Rqj11^ND A`Tzg` diff --git a/contracts/stellar-interchain-token-service/src/tests/deploy_interchain_token.rs b/contracts/stellar-interchain-token-service/src/tests/deploy_interchain_token.rs index 7bffb7ee..c866f940 100644 --- a/contracts/stellar-interchain-token-service/src/tests/deploy_interchain_token.rs +++ b/contracts/stellar-interchain-token-service/src/tests/deploy_interchain_token.rs @@ -9,6 +9,10 @@ use crate::error::ContractError; use crate::event::{InterchainTokenDeployedEvent, TokenManagerDeployedEvent}; use crate::types::TokenManagerType; +const INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX: i32 = -4; +const INTERCHAIN_TOKEN_DEPLOYED_NO_SUPPLY_EVENT_IDX: i32 = INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX + 1; +const TOKEN_MANAGER_DEPLOYED_EVENT_IDX: i32 = INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX + 1; + fn dummy_token_params(env: &Env) -> (Address, BytesN<32>, TokenMetadata) { let sender = Address::generate(env); let salt = BytesN::<32>::from_array(env, &[1; 32]); @@ -29,10 +33,13 @@ fn deploy_interchain_token_succeeds() { &sender, client.deploy_interchain_token(&sender, &salt, &token_metadata, &initial_supply, &minter) ); - let interchain_token_deployed_event = - events::fmt_emitted_event_at_idx::(&env, -5); - let token_manager_deployed_event = - events::fmt_emitted_event_at_idx::(&env, -4); + let interchain_token_deployed_event = events::fmt_emitted_event_at_idx::< + InterchainTokenDeployedEvent, + >(&env, INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX); + let token_manager_deployed_event = events::fmt_emitted_event_at_idx::( + &env, + TOKEN_MANAGER_DEPLOYED_EVENT_IDX, + ); goldie::assert!([ interchain_token_deployed_event, @@ -59,6 +66,34 @@ fn deploy_interchain_token_fails_when_paused() { ); } +#[test] +fn deploy_interchain_token_fails_when_already_deployed() { + let (env, client, _, _, _) = setup_env(); + + let (sender, salt, token_metadata) = dummy_token_params(&env); + let minter: Option
= None; + let initial_supply = 100; + + client.mock_all_auths().deploy_interchain_token( + &sender, + &salt, + &token_metadata, + &initial_supply, + &minter, + ); + + assert_contract_err!( + client.mock_all_auths().try_deploy_interchain_token( + &sender, + &salt, + &token_metadata, + &initial_supply, + &minter + ), + ContractError::TokenAlreadyRegistered + ); +} + #[test] fn deploy_interchain_token_with_initial_supply_no_minter() { let (env, client, _, _, _) = setup_env(); @@ -74,7 +109,7 @@ fn deploy_interchain_token_with_initial_supply_no_minter() { goldie::assert!(events::fmt_emitted_event_at_idx::< InterchainTokenDeployedEvent, - >(&env, -5)); + >(&env, INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX)); let token_address = client.token_address(&token_id); let token_manager = client.token_manager(&token_id); @@ -108,7 +143,7 @@ fn deploy_interchain_token_with_initial_supply_valid_minter() { goldie::assert!(events::fmt_emitted_event_at_idx::< InterchainTokenDeployedEvent, - >(&env, -5)); + >(&env, INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX)); let token_address = client.token_address(&token_id); let token_manager = client.token_manager(&token_id); @@ -138,7 +173,7 @@ fn deploy_interchain_token_check_token_id_and_token_manager_type() { goldie::assert!(events::fmt_emitted_event_at_idx::< InterchainTokenDeployedEvent, - >(&env, -5)); + >(&env, INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX)); assert_eq!(token_id, expected_token_id); assert_eq!( @@ -168,7 +203,7 @@ fn deploy_interchain_token_zero_initial_supply_and_valid_minter() { goldie::assert!(events::fmt_emitted_event_at_idx::< InterchainTokenDeployedEvent, - >(&env, -4)); + >(&env, INTERCHAIN_TOKEN_DEPLOYED_NO_SUPPLY_EVENT_IDX)); let token_address = client.token_address(&token_id); let token_manager = client.token_manager(&token_id); @@ -199,7 +234,7 @@ fn deploy_interchain_token_fails_with_zero_initial_supply_and_no_minter() { goldie::assert!(events::fmt_emitted_event_at_idx::< InterchainTokenDeployedEvent, - >(&env, -4)); + >(&env, INTERCHAIN_TOKEN_DEPLOYED_NO_SUPPLY_EVENT_IDX)); let token_address = client.token_address(&token_id); let token_manager = client.token_manager(&token_id); @@ -292,6 +327,6 @@ fn deploy_interchain_token_fails_with_negative_supply() { &invalid_supply, &None ), - ContractError::InvalidSupply + ContractError::InvalidInitialSupply ); } diff --git a/contracts/stellar-interchain-token-service/src/tests/execute.rs b/contracts/stellar-interchain-token-service/src/tests/execute.rs index 1c7c1db3..80068da8 100644 --- a/contracts/stellar-interchain-token-service/src/tests/execute.rs +++ b/contracts/stellar-interchain-token-service/src/tests/execute.rs @@ -192,9 +192,9 @@ fn deploy_interchain_token_message_execute_succeeds() { client.execute(&source_chain, &message_id, &source_address, &payload); let interchain_token_deployed_event = - events::fmt_emitted_event_at_idx::(&env, -4); + events::fmt_emitted_event_at_idx::(&env, -3); let token_manager_deployed_event = - events::fmt_emitted_event_at_idx::(&env, -3); + events::fmt_emitted_event_at_idx::(&env, -2); let token = InterchainTokenClient::new(&env, &client.token_address(&token_id)); @@ -690,6 +690,6 @@ fn deploy_interchain_token_message_execute_fails_token_already_deployed() { assert_contract_err!( client.try_execute(&source_chain, &second_message_id, &source_address, &payload), - ContractError::TokenAlreadyDeployed + ContractError::TokenAlreadyRegistered ); } diff --git a/contracts/stellar-interchain-token-service/src/testutils.rs b/contracts/stellar-interchain-token-service/src/testutils.rs index d62897c6..8863447e 100644 --- a/contracts/stellar-interchain-token-service/src/testutils.rs +++ b/contracts/stellar-interchain-token-service/src/testutils.rs @@ -8,8 +8,10 @@ use crate::{InterchainTokenService, InterchainTokenServiceClient}; // Note: On changes to `interchain-token` and `token-manager` crates, recompile via `stellar contract build && ./optimize.sh` // and copy the built `target/wasm32-unknown-unknown/release/stellar_*.optimized.wasm` to ../testdata. -pub const INTERCHAIN_TOKEN_WASM: &[u8] = include_bytes!("./testdata/interchain_token.wasm"); -pub const TOKEN_MANAGER_WASM: &[u8] = include_bytes!("./testdata/token_manager.wasm"); +pub const INTERCHAIN_TOKEN_WASM: &[u8] = + include_bytes!("./testdata/stellar_interchain_token.optimized.wasm"); +pub const TOKEN_MANAGER_WASM: &[u8] = + include_bytes!("./testdata/stellar_token_manager.optimized.wasm"); pub fn setup_its<'a>( env: &Env, diff --git a/contracts/stellar-interchain-token/src/contract.rs b/contracts/stellar-interchain-token/src/contract.rs index 08e1e234..196560a2 100644 --- a/contracts/stellar-interchain-token/src/contract.rs +++ b/contracts/stellar-interchain-token/src/contract.rs @@ -1,8 +1,5 @@ use soroban_sdk::token::{StellarAssetInterface, TokenInterface}; -use soroban_sdk::{ - assert_with_error, contract, contractimpl, panic_with_error, token, Address, BytesN, Env, - String, -}; +use soroban_sdk::{assert_with_error, contract, contractimpl, token, Address, BytesN, Env, String}; use soroban_token_sdk::event::Events as TokenEvents; use soroban_token_sdk::metadata::TokenMetadata; use soroban_token_sdk::TokenUtils; @@ -35,14 +32,31 @@ impl InterchainToken { env.storage().instance().set(&DataKey::TokenId, &token_id); - env.storage().instance().set(&DataKey::Minter(owner), &()); - if let Some(minter) = minter { - env.storage().instance().set(&DataKey::Minter(minter), &()); + env.storage() + .instance() + .set(&DataKey::Minter(minter.clone()), &()); + + MinterAddedEvent { minter }.emit(&env); } } } +#[contractimpl] +impl OwnableInterface for InterchainToken { + fn owner(env: &Env) -> Address { + interfaces::owner(env) + } + + fn transfer_ownership(env: &Env, new_owner: Address) { + interfaces::transfer_ownership::(env, new_owner.clone()); + + // Emit the standard soroban event for setting admin + TokenEvents::new(env).set_admin(Self::owner(env), new_owner); + } +} + +// Note: Some methods below are intentionally unimplemented as they are not supported by this token #[contractimpl] impl StellarAssetInterface for InterchainToken { fn set_admin(env: Env, admin: Address) { @@ -54,21 +68,28 @@ impl StellarAssetInterface for InterchainToken { } fn set_authorized(_env: Env, _id: Address, _authorize: bool) { - todo!() + unimplemented!() } fn authorized(_env: Env, _id: Address) -> bool { - todo!() + unimplemented!() } fn mint(env: Env, to: Address, amount: i128) { - if let Err(err) = Self::mint_from(&env, Self::owner(&env), to, amount) { - panic_with_error!(env, err); - } + let owner = Self::owner(&env); + owner.require_auth(); + + Self::validate_amount(&env, amount); + + Self::receive_balance(&env, to.clone(), amount); + + extend_instance_ttl(&env); + + TokenUtils::new(&env).events().mint(owner, to, amount); } fn clawback(_env: Env, _from: Address, _amount: i128) { - todo!() + unimplemented!() } } @@ -355,16 +376,3 @@ impl InterchainToken { extend_persistent_ttl(env, &key); } } - -#[contractimpl] -impl OwnableInterface for InterchainToken { - fn owner(env: &Env) -> Address { - interfaces::owner(env) - } - - fn transfer_ownership(env: &Env, new_owner: Address) { - interfaces::transfer_ownership::(env, new_owner.clone()); - // adhere to reference implementation for tokens and emit predefined soroban event - TokenEvents::new(env).set_admin(Self::owner(env), new_owner); - } -} diff --git a/contracts/stellar-interchain-token/src/tests/test.rs b/contracts/stellar-interchain-token/src/tests/test.rs index d2fc3754..e1c2616f 100644 --- a/contracts/stellar-interchain-token/src/tests/test.rs +++ b/contracts/stellar-interchain-token/src/tests/test.rs @@ -4,7 +4,8 @@ extern crate std; use soroban_sdk::testutils::{Address as _, BytesN as _, Ledger}; use soroban_sdk::{Address, BytesN, Env, IntoVal as _}; use soroban_token_sdk::metadata::TokenMetadata; -use stellar_axelar_std::events::fmt_last_emitted_event; +use stellar_axelar_std::events::{fmt_emitted_event_at_idx, fmt_last_emitted_event}; +use stellar_axelar_std::interfaces::OwnershipTransferredEvent; use stellar_axelar_std::{assert_auth, assert_auth_err}; use crate::event::{MinterAddedEvent, MinterRemovedEvent}; @@ -18,7 +19,7 @@ fn setup_token_metadata(env: &Env, name: &str, symbol: &str, decimal: u32) -> To } } -fn setup_token<'a>(env: &Env) -> (InterchainTokenClient<'a>, Address, Address) { +fn setup_token<'a>(env: &Env) -> (InterchainTokenClient<'a>, Address) { let owner = Address::generate(env); let minter = Address::generate(env); let token_id: BytesN<32> = BytesN::<32>::random(env); @@ -26,11 +27,11 @@ fn setup_token<'a>(env: &Env) -> (InterchainTokenClient<'a>, Address, Address) { let contract_id = env.register( InterchainToken, - (owner.clone(), minter.clone(), &token_id, token_metadata), + (owner, minter.clone(), &token_id, token_metadata), ); let token = InterchainTokenClient::new(env, &contract_id); - (token, owner, minter) + (token, minter) } #[test] @@ -59,12 +60,12 @@ fn register_interchain_token() { assert_eq!(token.symbol(), token_metadata.symbol); assert_eq!(token.decimals(), token_metadata.decimal); assert_eq!(token.owner(), owner); - assert!(token.is_minter(&owner)); + assert!(!token.is_minter(&owner)); assert!(token.is_minter(&minter)); } #[test] -fn register_interchain_token_without_minter() { +fn register_interchain_token_succeeds_without_minter() { let env = Env::default(); let owner = Address::generate(&env); @@ -80,7 +81,22 @@ fn register_interchain_token_without_minter() { let token = InterchainTokenClient::new(&env, &contract_id); assert_eq!(token.owner(), owner); - assert!(token.is_minter(&owner)); + assert!(!token.is_minter(&owner)); +} + +#[test] +fn transfer_ownership_succeeds() { + let env = Env::default(); + let new_owner = Address::generate(&env); + + let (token, _) = setup_token(&env); + + assert_auth!(token.owner(), token.transfer_ownership(&new_owner)); + goldie::assert!(fmt_emitted_event_at_idx::( + &env, -2 + )); + + assert_eq!(token.owner(), new_owner); } #[test] @@ -90,25 +106,38 @@ fn transfer_ownership_from_non_owner_fails() { let new_owner = Address::generate(&env); let user = Address::generate(&env); - let (token, _owner, _minter) = setup_token(&env); + let (token, _) = setup_token(&env); assert_auth_err!(user, token.transfer_ownership(&new_owner)); } #[test] -fn transfer_ownership() { +fn set_admin_succeeds() { let env = Env::default(); let new_owner = Address::generate(&env); - let (token, owner, _minter) = setup_token(&env); - - assert_eq!(token.owner(), owner); + let (token, _) = setup_token(&env); - assert_auth!(owner, token.transfer_ownership(&new_owner)); + assert_auth!(token.owner(), token.set_admin(&new_owner)); + goldie::assert!(fmt_emitted_event_at_idx::( + &env, -2 + )); assert_eq!(token.owner(), new_owner); } +#[test] +fn set_admin_fails_when_not_owner() { + let env = Env::default(); + + let new_owner = Address::generate(&env); + let user = Address::generate(&env); + + let (token, _) = setup_token(&env); + + assert_auth_err!(user, token.set_admin(&new_owner)); +} + #[test] #[should_panic(expected = "HostError: Error(Contract, #6)")] // NegativeAmount fn transfer_fails_with_negative_amount() { @@ -118,7 +147,7 @@ fn transfer_fails_with_negative_amount() { let user2 = Address::generate(&env); let amount = -1; - let (token, _owner, _minter) = setup_token(&env); + let (token, _) = setup_token(&env); token.mock_all_auths().transfer(&user1, &user2, &amount); } @@ -132,7 +161,7 @@ fn transfer_fails_with_insufficient_balance() { let user2 = Address::generate(&env); let amount = 1000; - let (token, _owner, _minter) = setup_token(&env); + let (token, _) = setup_token(&env); token.mock_all_auths().transfer(&user1, &user2, &amount); } @@ -145,7 +174,7 @@ fn transfer() { let user2 = Address::generate(&env); let amount = 1000; - let (token, _owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); assert_auth!(minter, token.mint_from(&minter, &user1, &amount)); assert_eq!(token.balance(&user1), amount); @@ -165,7 +194,7 @@ fn transfer_from_fails_with_negative_amount() { let user3 = Address::generate(&env); let amount = -1; - let (token, _owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); assert_auth!(minter, token.mint_from(&minter, &user1, &1000_i128)); assert_eq!(token.balance(&user1), 1000_i128); @@ -192,7 +221,7 @@ fn transfer_from_fails_without_approval() { let user2 = Address::generate(&env); let user3 = Address::generate(&env); - let (token, _owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); assert_auth!(minter, token.mint_from(&minter, &user1, &1000_i128)); assert_eq!(token.balance(&user1), 1000_i128); @@ -211,7 +240,7 @@ fn transfer_from_fails_with_insufficient_allowance() { let user2 = Address::generate(&env); let user3 = Address::generate(&env); - let (token, _owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); assert_auth!(minter, token.mint_from(&minter, &user1, &1000_i128)); assert_eq!(token.balance(&user1), 1000_i128); @@ -238,7 +267,7 @@ fn transfer_from_fails_with_expired_allowance() { let user2 = Address::generate(&env); let user3 = Address::generate(&env); - let (token, _owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); token .mock_all_auths() @@ -260,14 +289,14 @@ fn transfer_from_fails_with_expired_allowance() { } #[test] -fn transfer_from() { +fn transfer_from_succeeds() { let env = Env::default(); let user1 = Address::generate(&env); let user2 = Address::generate(&env); let user3 = Address::generate(&env); - let (token, _owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); assert_auth!(minter, token.mint_from(&minter, &user1, &1000_i128)); assert_eq!(token.balance(&user1), 1000_i128); @@ -290,36 +319,55 @@ fn transfer_from() { } #[test] -fn mint_from_invalid_minter_fails() { +fn mint_succeeds() { let env = Env::default(); let amount = 1000; - let user = Address::generate(&env); - let (token, owner, minter) = setup_token(&env); + let (token, _) = setup_token(&env); - assert_auth_err!(owner, token.mint_from(&minter, &user, &amount)); - assert_auth_err!(user, token.mint_from(&minter, &user, &amount)); - assert_auth_err!(user, token.mint(&user, &amount)); + assert_auth!(token.owner(), token.mint(&user, &amount)); + assert_eq!(token.balance(&user), amount); + + token.mock_all_auths().remove_minter(&token.owner()); + + // Owner can mint without being a minter + assert_auth!(token.owner(), token.mint(&user, &amount)); + assert_eq!(token.balance(&user), amount * 2); } #[test] -fn mint_from_minter_succeeds() { +fn mint_from_succeeds() { let env = Env::default(); let amount = 1000; let user = Address::generate(&env); - let (token, owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); assert_auth!(minter, token.mint_from(&minter, &user, &amount)); assert_eq!(token.balance(&user), amount); - assert_auth!(owner, token.mint(&user, &amount)); + assert_auth!(token.owner(), token.mint(&user, &amount)); assert_eq!(token.balance(&user), amount * 2); } +#[test] +fn mint_from_fails_with_invalid_minter() { + let env = Env::default(); + + let amount = 1000; + + let user = Address::generate(&env); + + let (token, minter) = setup_token(&env); + + assert_auth_err!(token.owner(), token.mint_from(&minter, &user, &amount)); + assert_auth_err!(user, token.mint_from(&minter, &user, &amount)); + assert_auth_err!(user, token.mint(&user, &amount)); +} + #[test] fn add_minter_fails_without_owner_auth() { let env = Env::default(); @@ -327,7 +375,7 @@ fn add_minter_fails_without_owner_auth() { let minter2 = Address::generate(&env); let user = Address::generate(&env); - let (token, _owner, _minter1) = setup_token(&env); + let (token, _) = setup_token(&env); assert_auth_err!(user, token.add_minter(&minter2)); } @@ -340,9 +388,9 @@ fn add_minter_succeeds() { let minter2 = Address::generate(&env); let user = Address::generate(&env); - let (token, owner, _minter1) = setup_token(&env); + let (token, _) = setup_token(&env); - assert_auth!(owner, token.add_minter(&minter2)); + assert_auth!(token.owner(), token.add_minter(&minter2)); goldie::assert!(fmt_last_emitted_event::(&env)); @@ -351,32 +399,48 @@ fn add_minter_succeeds() { } #[test] -fn remove_minter_fails_without_owner_auth() { +fn remove_minter_succeeds() { let env = Env::default(); + let amount = 1000; let minter1 = Address::generate(&env); let user = Address::generate(&env); - let (token, _owner, _minter) = setup_token(&env); + let (token, _) = setup_token(&env); - assert_auth_err!(user, token.remove_minter(&minter1)); + assert_auth!(token.owner(), token.remove_minter(&minter1)); + + goldie::assert!(fmt_last_emitted_event::(&env)); + + assert_auth_err!(minter1, token.mint_from(&minter1, &user, &amount)); } #[test] -fn remove_minter() { +fn remove_minter_fails_without_minter_auth() { let env = Env::default(); - let amount = 1000; let minter1 = Address::generate(&env); let user = Address::generate(&env); - let (token, owner, _minter) = setup_token(&env); + let (token, _) = setup_token(&env); - assert_auth!(owner, token.remove_minter(&minter1)); + assert_auth_err!(user, token.remove_minter(&minter1)); +} - goldie::assert!(fmt_last_emitted_event::(&env)); +#[test] +fn burn_succeeds() { + let env = Env::default(); - assert_auth_err!(minter1, token.mint_from(&minter1, &user, &amount)); + let user = Address::generate(&env); + + let (token, minter) = setup_token(&env); + let amount = 1000; + + assert_auth!(minter, token.mint_from(&minter, &user, &amount)); + assert_eq!(token.balance(&user), amount); + + assert_auth!(user, token.burn(&user, &amount)); + assert_eq!(token.balance(&user), 0); } #[test] @@ -386,7 +450,7 @@ fn burn_fails_with_negative_amount() { let user = Address::generate(&env); - let (token, _owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); let amount = 1000; assert_auth!(minter, token.mint_from(&minter, &user, &amount)); @@ -404,7 +468,7 @@ fn burn_fails_with_insufficient_balance() { let user = Address::generate(&env); - let (token, _owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); let amount = 1000; assert_auth!(minter, token.mint_from(&minter, &user, &amount)); @@ -416,19 +480,29 @@ fn burn_fails_with_insufficient_balance() { } #[test] -fn burn_succeeds() { +fn burn_from_succeeds() { let env = Env::default(); - let user = Address::generate(&env); - - let (token, _owner, minter) = setup_token(&env); + let user1 = Address::generate(&env); + let user2 = Address::generate(&env); + let (token, minter) = setup_token(&env); let amount = 1000; - assert_auth!(minter, token.mint_from(&minter, &user, &amount)); - assert_eq!(token.balance(&user), amount); + assert_auth!(minter, token.mint_from(&minter, &user1, &amount)); + assert_eq!(token.balance(&user1), amount); - assert_auth!(user, token.burn(&user, &amount)); - assert_eq!(token.balance(&user), 0); + let expiration_ledger = 200; + let burn_amount = 100; + + assert_auth!( + user1, + token.approve(&user1, &user2, &burn_amount, &expiration_ledger) + ); + assert_eq!(token.allowance(&user1, &user2), burn_amount); + + assert_auth!(user2, token.burn_from(&user2, &user1, &burn_amount)); + assert_eq!(token.allowance(&user1, &user2), 0); + assert_eq!(token.balance(&user1), (amount - burn_amount)); } #[test] @@ -438,7 +512,7 @@ fn burn_from_fails_with_negative_amount() { let user1 = Address::generate(&env); let user2 = Address::generate(&env); - let (token, _owner, _minter) = setup_token(&env); + let (token, _) = setup_token(&env); let burn_amount = -1; @@ -454,7 +528,7 @@ fn burn_from_fails_without_approval() { let user1 = Address::generate(&env); let user2 = Address::generate(&env); - let (token, _owner, minter) = setup_token(&env); + let (token, minter) = setup_token(&env); let amount = 1000; assert_auth!(minter, token.mint_from(&minter, &user1, &amount)); @@ -468,28 +542,31 @@ fn burn_from_fails_without_approval() { } #[test] -fn burn_from_succeeds() { +#[should_panic(expected = "not implemented")] +fn set_authorized_fails() { let env = Env::default(); - let user1 = Address::generate(&env); - let user2 = Address::generate(&env); - let (token, _owner, minter) = setup_token(&env); - let amount = 1000; + let (token, _) = setup_token(&env); - assert_auth!(minter, token.mint_from(&minter, &user1, &amount)); - assert_eq!(token.balance(&user1), amount); + token.set_authorized(&token.owner(), &true); +} - let expiration_ledger = 200; - let burn_amount = 100; +#[test] +#[should_panic(expected = "not implemented")] +fn authorized_fails() { + let env = Env::default(); - assert_auth!( - user1, - token.approve(&user1, &user2, &burn_amount, &expiration_ledger) - ); - assert_eq!(token.allowance(&user1, &user2), burn_amount); + let (token, _) = setup_token(&env); - assert_auth!(user2, token.burn_from(&user2, &user1, &burn_amount)); - assert_eq!(token.allowance(&user1, &user2), 0); - assert_eq!(token.balance(&user1), (amount - burn_amount)); - assert_eq!(token.balance(&user2), 0); + token.authorized(&token.owner()); +} + +#[test] +#[should_panic(expected = "not implemented")] +fn clawback_fails() { + let env = Env::default(); + + let (token, _) = setup_token(&env); + + token.clawback(&token.owner(), &1); } diff --git a/contracts/stellar-interchain-token/src/tests/testdata/remove_minter.golden b/contracts/stellar-interchain-token/src/tests/testdata/remove_minter_succeeds.golden similarity index 100% rename from contracts/stellar-interchain-token/src/tests/testdata/remove_minter.golden rename to contracts/stellar-interchain-token/src/tests/testdata/remove_minter_succeeds.golden diff --git a/contracts/stellar-interchain-token/src/tests/testdata/set_admin_succeeds.golden b/contracts/stellar-interchain-token/src/tests/testdata/set_admin_succeeds.golden new file mode 100644 index 00000000..766f97fe --- /dev/null +++ b/contracts/stellar-interchain-token/src/tests/testdata/set_admin_succeeds.golden @@ -0,0 +1,11 @@ +OwnershipTransferredEvent { + previous_owner: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4), + new_owner: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM), +} + +Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4) + +ownership_transferred { + #[topic] previous_owner: Address, + #[topic] new_owner: Address, +} \ No newline at end of file diff --git a/contracts/stellar-interchain-token/src/tests/testdata/transfer_ownership_succeeds.golden b/contracts/stellar-interchain-token/src/tests/testdata/transfer_ownership_succeeds.golden new file mode 100644 index 00000000..766f97fe --- /dev/null +++ b/contracts/stellar-interchain-token/src/tests/testdata/transfer_ownership_succeeds.golden @@ -0,0 +1,11 @@ +OwnershipTransferredEvent { + previous_owner: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4), + new_owner: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM), +} + +Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4) + +ownership_transferred { + #[topic] previous_owner: Address, + #[topic] new_owner: Address, +} \ No newline at end of file