use super::*; use crate::test_payload::test_contracts::payloader::Payload; use cosmwasm_std::{from_json, Addr, Coin, Empty, Uint128}; use cw_multi_test::{App, Contract, ContractWrapper, Executor, IntoAddr}; const DENOM: &str = "pao"; pub fn payloader_contract() -> Box> { Box::new( ContractWrapper::new_with_empty( test_contracts::payloader::execute, test_contracts::payloader::instantiate, test_contracts::payloader::query, ) .with_reply(test_contracts::payloader::reply), ) } fn coins(amount: u128) -> Vec { vec![Coin { denom: DENOM.to_string(), amount: Uint128::new(amount), }] } fn assert_balance(app: &App, amount: u128, addr: &Addr) { let coins = app.wrap().query_all_balances(addr).unwrap(); assert_eq!(1, coins.len()); assert_eq!(amount, coins[0].amount.u128()); assert_eq!(DENOM, coins[0].denom); } #[test] fn submessage_payload_should_work() { // Prepare addresses for Alice, Bob and Cecil accounts. let alice_addr = "alice".into_addr(); let bob_addr = "bob".into_addr(); let cecil_addr = "cecil".into_addr(); // Initialize the chain with initial balances for Alice, Bob and Cecil. let mut app = App::new(|router, _api, storage| { router .bank .init_balance(storage, &alice_addr, coins(100)) .unwrap(); router .bank .init_balance(storage, &bob_addr, coins(200)) .unwrap(); router .bank .init_balance(storage, &cecil_addr, coins(300)) .unwrap(); }); // Check the balance for Alice. assert_balance(&app, 100, &alice_addr); // Check the balance for Bob. assert_balance(&app, 200, &bob_addr); // Check the balance for Cecil. assert_balance(&app, 300, &cecil_addr); // Alice stores the code of payloader contract on chain. let code_id = app.store_code_with_creator(alice_addr.clone(), payloader_contract()); // Alice instantiates the contract. let contract_addr = app .instantiate_contract( code_id, alice_addr.clone(), &Empty {}, &coins(90), "payloader", None, ) .unwrap(); // Now, Alice should have only 10pao, because 90pao was sent to the instantiated contract. assert_balance(&app, 10, &alice_addr); // Now the contract should have 90pao. assert_balance(&app, 90, &contract_addr); //--------------------------------------------------------------------------------------------- // Alice sends 10pao to Bob using payloader contract. // // 1. Alice executes ExecuteMessage::Send message variant on payloader contract. // 2. Payloader contract returns single submessage that is the Bank::Send variant // with the address of Bob. // 3. Chain processes the bank message (sends 10pao to Bob) and returns to payloader contract // with the response executing the reply entrypoint. // 4. The response from processing the execute and reply is the data returned from reply. // Because there was only one submessage, the result is the data from reply processed // for this single submessage. //--------------------------------------------------------------------------------------------- // Alice sends 10pao to Bob using payloader contract. let msg = test_contracts::payloader::ExecuteMessage::Send( bob_addr.to_string(), 10, DENOM.to_string(), ); let response = app .execute_contract(alice_addr.clone(), contract_addr.clone(), &msg, &[]) .unwrap(); let payload = from_json::(response.data.unwrap()).unwrap(); assert_eq!(1, payload.id); assert_eq!("SEND", payload.action); // Now, Bob should have 10pao more, because Alice sent him 10pao. assert_balance(&app, 210, &bob_addr); // No changes for Cecil. assert_balance(&app, 300, &cecil_addr); // No changes for Alice. assert_balance(&app, 10, &alice_addr); // Now the contract should have 80pao. assert_balance(&app, 80, &contract_addr); //--------------------------------------------------------------------------------------------- // Alice sends 1pao to Bob and 2pao to Cecil using payloader contract. // // 1. Alice executes ExecuteMessage::SendMulti message variant on payloader contract. // 2. Payloader contract returns two submessages that are the Bank::Send variants // one with the address of Bob and second with the address of Cecil. // 3. Chain processes both bank messages (sends 1pao to Bob nad 2pao to Cecil) // and returns to payloader contract with the responses executing the reply // entrypoint twice. // 4. The response from processing the execute and two replies is the data returned // from processing the LAST REPLY. //--------------------------------------------------------------------------------------------- // Alice sends 1pao to Bob and 2pao to Cecil using payloader contract. let msg = test_contracts::payloader::ExecuteMessage::SendMulti( bob_addr.to_string(), 1, cecil_addr.to_string(), 2, DENOM.to_string(), ); let response = app .execute_contract(alice_addr.clone(), contract_addr.clone(), &msg, &[]) .unwrap(); let payload = from_json::(response.data.unwrap()).unwrap(); assert_eq!(3, payload.id); assert_eq!("SEND", payload.action); // Now, Bob should have 1pao more, because Alice sent him 10pao. assert_balance(&app, 211, &bob_addr); // Now, Cecil should have 2pao more, because Alice sent him 10pao. assert_balance(&app, 302, &cecil_addr); // No changes for Alice. assert_balance(&app, 10, &alice_addr); // Now the contract should have 77pao. assert_balance(&app, 77, &contract_addr); //--------------------------------------------------------------------------------------------- // Alice burns 10pao in her contract. // // 1. Alice executes ExecuteMessage::Burn message variant on payloader contract. // 2. Payloader contract returns two submessage that is the Bank::Burn variant. // 3. Chain processes both bank message (burns 10pao for the contract) // and returns to payloader contract with the response executing the reply entrypoint. // 4. The response is from reply entrypoint. //--------------------------------------------------------------------------------------------- let msg = test_contracts::payloader::ExecuteMessage::Burn(10, DENOM.to_string()); let response = app .execute_contract(alice_addr.clone(), contract_addr.clone(), &msg, &[]) .unwrap(); let payload = from_json::(response.data.unwrap()).unwrap(); assert_eq!(4, payload.id); assert_eq!("BURN", payload.action); // No changes for Bob. assert_balance(&app, 211, &bob_addr); // No changes for Cecil. assert_balance(&app, 302, &cecil_addr); // No changes for Alice. assert_balance(&app, 10, &alice_addr); // Now the contract should have 67pao. assert_balance(&app, 67, &contract_addr); //--------------------------------------------------------------------------------------------- // Alice burns 10pao in her contract. // This time without filling the payload field in the submessage. //--------------------------------------------------------------------------------------------- let msg = test_contracts::payloader::ExecuteMessage::BurnNoPayload(10, DENOM.to_string()); let response = app .execute_contract(alice_addr.clone(), contract_addr.clone(), &msg, &[]) .unwrap(); let payload = from_json::(response.data.unwrap()).unwrap(); assert_eq!(5, payload.id); assert_eq!("EMPTY", payload.action); // No changes for Bob. assert_balance(&app, 211, &bob_addr); // No changes for Cecil. assert_balance(&app, 302, &cecil_addr); // No changes for Alice. assert_balance(&app, 10, &alice_addr); // Now the contract should have 57pao. assert_balance(&app, 57, &contract_addr); //--------------------------------------------------------------------------------------------- // Alice executes a variant without reply. // The response is from the execute entrypoint. //--------------------------------------------------------------------------------------------- let msg = test_contracts::payloader::ExecuteMessage::Nop; let response = app .execute_contract(alice_addr.clone(), contract_addr.clone(), &msg, &[]) .unwrap(); let payload = from_json::(response.data.unwrap()).unwrap(); assert_eq!(0, payload.id); assert_eq!("EXECUTE", payload.action); // No changes for Bob. assert_balance(&app, 211, &bob_addr); // No changes for Cecil. assert_balance(&app, 302, &cecil_addr); // No changes for Alice. assert_balance(&app, 10, &alice_addr); // Now changes for the contract. assert_balance(&app, 57, &contract_addr); }