@@ -327,6 +327,34 @@ where
327327 }
328328 }
329329
330+ /// unified logic for UpdateAdmin and ClearAdmin messages
331+ fn update_admin (
332+ & self ,
333+ api : & dyn Api ,
334+ storage : & mut dyn Storage ,
335+ sender : Addr ,
336+ contract_addr : & str ,
337+ new_admin : Option < String > ,
338+ ) -> AnyResult < AppResponse > {
339+ let contract_addr = api. addr_validate ( contract_addr) ?;
340+ let admin = new_admin. map ( |a| api. addr_validate ( & a) ) . transpose ( ) ?;
341+
342+ // check admin status
343+ let mut data = self . load_contract ( storage, & contract_addr) ?;
344+ if data. admin != Some ( sender) {
345+ bail ! ( "Only admin can update the contract admin: {:?}" , data. admin) ;
346+ }
347+ // update admin field
348+ data. admin = admin;
349+ self . save_contract ( storage, & contract_addr, & data) ?;
350+
351+ // no custom event here
352+ Ok ( AppResponse {
353+ data : None ,
354+ events : vec ! [ ] ,
355+ } )
356+ }
357+
330358 // this returns the contract address as well, so we can properly resend the data
331359 fn execute_wasm (
332360 & self ,
@@ -474,6 +502,13 @@ where
474502 res. data = execute_response ( res. data ) ;
475503 Ok ( res)
476504 }
505+ WasmMsg :: UpdateAdmin {
506+ contract_addr,
507+ admin,
508+ } => self . update_admin ( api, storage, sender, & contract_addr, Some ( admin) ) ,
509+ WasmMsg :: ClearAdmin { contract_addr } => {
510+ self . update_admin ( api, storage, sender, & contract_addr, None )
511+ }
477512 msg => bail ! ( Error :: UnsupportedWasmMsg ( msg) ) ,
478513 }
479514 }
@@ -906,7 +941,8 @@ mod test {
906941 use crate :: app:: Router ;
907942 use crate :: bank:: BankKeeper ;
908943 use crate :: module:: FailingModule ;
909- use crate :: test_helpers:: contracts:: { error, payout} ;
944+ use crate :: test_helpers:: contracts:: { caller, error, payout} ;
945+ use crate :: test_helpers:: EmptyMsg ;
910946 use crate :: transactions:: StorageTransaction ;
911947
912948 use super :: * ;
@@ -1356,4 +1392,131 @@ mod test {
13561392 assert_payout ( & keeper, & mut wasm_storage, & contract2, & payout2) ;
13571393 assert_payout ( & keeper, & mut wasm_storage, & contract3, & payout3) ;
13581394 }
1395+
1396+ fn assert_admin (
1397+ storage : & dyn Storage ,
1398+ keeper : & WasmKeeper < Empty , Empty > ,
1399+ contract_addr : & impl ToString ,
1400+ admin : Option < Addr > ,
1401+ ) {
1402+ let api = MockApi :: default ( ) ;
1403+ let querier: MockQuerier < Empty > = MockQuerier :: new ( & [ ] ) ;
1404+ // query
1405+ let data = keeper
1406+ . query (
1407+ & api,
1408+ storage,
1409+ & querier,
1410+ & mock_env ( ) . block ,
1411+ WasmQuery :: ContractInfo {
1412+ contract_addr : contract_addr. to_string ( ) ,
1413+ } ,
1414+ )
1415+ . unwrap ( ) ;
1416+ let res: ContractInfoResponse = from_slice ( & data) . unwrap ( ) ;
1417+ assert_eq ! ( res. admin, admin. as_ref( ) . map( Addr :: to_string) ) ;
1418+ }
1419+
1420+ #[ test]
1421+ fn update_clear_admin_works ( ) {
1422+ let api = MockApi :: default ( ) ;
1423+ let mut keeper = WasmKeeper :: new ( ) ;
1424+ let block = mock_env ( ) . block ;
1425+ let code_id = keeper. store_code ( caller:: contract ( ) ) ;
1426+
1427+ let mut wasm_storage = MockStorage :: new ( ) ;
1428+
1429+ let admin: Addr = Addr :: unchecked ( "admin" ) ;
1430+ let new_admin: Addr = Addr :: unchecked ( "new_admin" ) ;
1431+ let normal_user: Addr = Addr :: unchecked ( "normal_user" ) ;
1432+
1433+ let contract_addr = keeper
1434+ . register_contract (
1435+ & mut wasm_storage,
1436+ code_id,
1437+ Addr :: unchecked ( "creator" ) ,
1438+ admin. clone ( ) ,
1439+ "label" . to_owned ( ) ,
1440+ 1000 ,
1441+ )
1442+ . unwrap ( ) ;
1443+
1444+ // init the contract
1445+ let info = mock_info ( "admin" , & [ ] ) ;
1446+ let init_msg = to_vec ( & EmptyMsg { } ) . unwrap ( ) ;
1447+ let res = keeper
1448+ . call_instantiate (
1449+ contract_addr. clone ( ) ,
1450+ & api,
1451+ & mut wasm_storage,
1452+ & mock_router ( ) ,
1453+ & block,
1454+ info,
1455+ init_msg,
1456+ )
1457+ . unwrap ( ) ;
1458+ assert_eq ! ( 0 , res. messages. len( ) ) ;
1459+
1460+ assert_admin ( & wasm_storage, & keeper, & contract_addr, Some ( admin. clone ( ) ) ) ;
1461+
1462+ // non-admin should not be allowed to become admin on their own
1463+ keeper
1464+ . execute_wasm (
1465+ & api,
1466+ & mut wasm_storage,
1467+ & mock_router ( ) ,
1468+ & block,
1469+ normal_user. clone ( ) ,
1470+ WasmMsg :: UpdateAdmin {
1471+ contract_addr : contract_addr. to_string ( ) ,
1472+ admin : normal_user. to_string ( ) ,
1473+ } ,
1474+ )
1475+ . unwrap_err ( ) ;
1476+
1477+ // should still be admin
1478+ assert_admin ( & wasm_storage, & keeper, & contract_addr, Some ( admin. clone ( ) ) ) ;
1479+
1480+ // admin should be allowed to transfers adminship
1481+ let res = keeper
1482+ . execute_wasm (
1483+ & api,
1484+ & mut wasm_storage,
1485+ & mock_router ( ) ,
1486+ & block,
1487+ admin,
1488+ WasmMsg :: UpdateAdmin {
1489+ contract_addr : contract_addr. to_string ( ) ,
1490+ admin : new_admin. to_string ( ) ,
1491+ } ,
1492+ )
1493+ . unwrap ( ) ;
1494+ assert_eq ! ( res. events. len( ) , 0 ) ;
1495+
1496+ // new_admin should now be admin
1497+ assert_admin (
1498+ & wasm_storage,
1499+ & keeper,
1500+ & contract_addr,
1501+ Some ( new_admin. clone ( ) ) ,
1502+ ) ;
1503+
1504+ // new_admin should now be able to clear to admin
1505+ let res = keeper
1506+ . execute_wasm (
1507+ & api,
1508+ & mut wasm_storage,
1509+ & mock_router ( ) ,
1510+ & block,
1511+ new_admin,
1512+ WasmMsg :: ClearAdmin {
1513+ contract_addr : contract_addr. to_string ( ) ,
1514+ } ,
1515+ )
1516+ . unwrap ( ) ;
1517+ assert_eq ! ( res. events. len( ) , 0 ) ;
1518+
1519+ // should have no admin now
1520+ assert_admin ( & wasm_storage, & keeper, & contract_addr, None ) ;
1521+ }
13591522}
0 commit comments