diff --git a/evm-ds/src/continuations.rs b/evm-ds/src/continuations.rs index 382b95448a..c0b64a3565 100644 --- a/evm-ds/src/continuations.rs +++ b/evm-ds/src/continuations.rs @@ -67,15 +67,13 @@ impl Continuations { // Sometimes a contract will change the state of another contract // in this case, we need to find cached state of continuations that // has now been invalidated by this and update it - pub fn update_states(&mut self, addr: H160, key: H256, value: H256, skip: bool) { - + pub fn update_states(&mut self, addr: H160, key: H256, value: H256, skip: bool) { if skip { return; } // Loop over continuations updating the address if it exists for (_, continuation) in self.storage.iter_mut() { - if let Some(value_current) = continuation.storages.get_mut(&(addr, key)) { *value_current = value; } diff --git a/evm-ds/src/evm_server_run.rs b/evm-ds/src/evm_server_run.rs index dc910e6ea7..306e1b8a91 100644 --- a/evm-ds/src/evm_server_run.rs +++ b/evm-ds/src/evm_server_run.rs @@ -233,7 +233,13 @@ fn build_exit_result( // Is this call static? if so, we don't want to modify other continuations' state let storage_proto = storage .into_iter() - .map(|(k, v)| { continuations.lock().unwrap().update_states(address, k, v, is_static); backend.encode_storage(k, v).into()}) + .map(|(k, v)| { + continuations + .lock() + .unwrap() + .update_states(address, k, v, is_static); + backend.encode_storage(k, v).into() + }) .collect(); modify.set_storage(storage_proto); diff --git a/evm-ds/src/precompiles/mod.rs b/evm-ds/src/precompiles/mod.rs index 9a976edf76..fcfc075e48 100644 --- a/evm-ds/src/precompiles/mod.rs +++ b/evm-ds/src/precompiles/mod.rs @@ -56,12 +56,8 @@ pub fn get_precompiles() -> BTreeMap { blake2::blake2 as PrecompileFn, ), ( - H160::from_str("000000000000000000000000000000005a494c51").unwrap(), - scilla_call::scilla_call as PrecompileFn, - ), - ( - H160::from_str("000000000000000000000000000000005a494c52").unwrap(), - scilla_call::scilla_call_keep_origin as PrecompileFn, + H160::from_str("000000000000000000000000000000005a494c53").unwrap(), + scilla_call::scilla_call_2 as PrecompileFn, ), ( H160::from_str("000000000000000000000000000000005a494c92").unwrap(), diff --git a/evm-ds/src/precompiles/scilla_call.rs b/evm-ds/src/precompiles/scilla_call.rs index 720ec82795..ef91ebe0e5 100644 --- a/evm-ds/src/precompiles/scilla_call.rs +++ b/evm-ds/src/precompiles/scilla_call.rs @@ -3,47 +3,47 @@ use evm::executor::stack::{PrecompileFailure, PrecompileOutput, PrecompileOutput use evm::{Context, ExitError}; use std::borrow::Cow; -use crate::precompiles::scilla_common::get_contract_addr_and_name; +use crate::precompiles::scilla_common::parse_invocation_prefix; use crate::precompiles::scilla_common::substitute_scilla_type_with_sol; use ethabi::decode; +use ethabi::ethereum_types::Address; use ethabi::param_type::ParamType; use ethabi::token::Token; use hex::ToHex; +use primitive_types::U256; use serde_json::{json, Value}; // TODO: revisit these consts const BASE_COST: u64 = 15; const PER_BYTE_COST: u64 = 3; -pub(crate) fn scilla_call( +pub(crate) fn scilla_call_2( input: &[u8], gas_limit: Option, _contex: &Context, backend: &dyn Backend, _is_static: bool, ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { - scilla_call_common(input, gas_limit, _contex, backend, _is_static, false) -} + let gas_needed = check_gas(input, gas_limit)?; + let (code_address, passed_transition_name, keep_origin) = parse_invocation_prefix(input)?; -pub(crate) fn scilla_call_keep_origin( - input: &[u8], - gas_limit: Option, - _contex: &Context, - backend: &dyn Backend, - _is_static: bool, -) -> Result<(PrecompileOutput, u64), PrecompileFailure> { - scilla_call_common(input, gas_limit, _contex, backend, _is_static, true) + if keep_origin != U256::from(0) && keep_origin != U256::from(1) { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other(Cow::Borrowed("call mode should be either 0 or 1")), + }); + }; + let keep_origin = keep_origin == U256::from(1); + scilla_call_common( + input, + backend, + code_address, + &passed_transition_name, + keep_origin, + gas_needed, + ) } -// input should be formed of: scilla_contract_addr, transition_name, arg1, arg2, arg3, ..., argn -fn scilla_call_common( - input: &[u8], - gas_limit: Option, - _contex: &Context, - backend: &dyn Backend, - _is_static: bool, - keep_origin: bool, -) -> Result<(PrecompileOutput, u64), PrecompileFailure> { +fn check_gas(input: &[u8], gas_limit: Option) -> Result { let gas_needed = match required_gas(input) { Ok(i) => i, Err(err) => return Err(PrecompileFailure::Error { exit_status: err }), @@ -52,13 +52,24 @@ fn scilla_call_common( if let Some(gas_limit) = gas_limit { if gas_limit < gas_needed { return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, + exit_status: ExitError::Other(Cow::Borrowed( + "Unable to parse scilla contract code", + )), }); } } + Ok(gas_needed) +} - let (code_address, passed_transition_name) = get_contract_addr_and_name(input)?; - +// input should be formed of: scilla_contract_addr, transition_name, keep_origin, arg1, arg2, arg3, ..., argn +fn scilla_call_common( + input: &[u8], + backend: &dyn Backend, + code_address: Address, + passed_transition_name: &str, + keep_origin: bool, + gas_needed: u64, +) -> Result<(PrecompileOutput, u64), PrecompileFailure> { let code = backend.code_as_json(code_address); // If there's no code under given address it doesn't make sense to proceed further - transition call will fail at scilla level later if code.is_empty() { @@ -78,7 +89,7 @@ fn scilla_call_common( exit_status: ExitError::Other(Cow::Borrowed("Unable to get transitions array from contract json")), }); }; - let mut output_json = build_result_json(input, &passed_transition_name, transitions)?; + let mut output_json = build_result_json(input, passed_transition_name, transitions)?; output_json["_address"] = Value::String(code_address.encode_hex()); output_json["keep_origin"] = Value::Bool(keep_origin); @@ -96,7 +107,7 @@ fn build_result_json( expected_transition: &str, transitions: &Vec, ) -> Result { - let mut solidity_args = vec![ParamType::Address, ParamType::String]; + let mut solidity_args = vec![ParamType::Address, ParamType::String, ParamType::Bool]; let mut scilla_args = vec![]; for transition in transitions { @@ -132,7 +143,7 @@ fn build_result_json( result["_tag"] = Value::String(expected_transition.to_string()); let mut result_arguments = Value::Array(vec![]); - for (scilla_arg, solidity_value) in scilla_args.iter().zip(decoded_values.iter().skip(2)) { + for (scilla_arg, solidity_value) in scilla_args.iter().zip(decoded_values.iter().skip(3)) { let json_arg: Value = match solidity_value { Token::Uint(solidity_uint) => { json!({"vname" : scilla_arg.0, "type" : scilla_arg.1, "value": format!("{}", solidity_uint)}) diff --git a/evm-ds/src/precompiles/scilla_common.rs b/evm-ds/src/precompiles/scilla_common.rs index 3b5e90166a..1ea224fa04 100644 --- a/evm-ds/src/precompiles/scilla_common.rs +++ b/evm-ds/src/precompiles/scilla_common.rs @@ -1,5 +1,5 @@ use ethabi::ethereum_types::Address; -use ethabi::{decode, ParamType, Token}; +use ethabi::{decode, ParamType, Token, Uint}; use evm::executor::stack::PrecompileFailure; use evm::ExitError; use std::borrow::Cow; @@ -51,3 +51,31 @@ pub fn get_contract_addr_and_name(input: &[u8]) -> Result<(Address, String), Pre Ok((code_address.to_owned(), name.to_owned())) } + +pub fn parse_invocation_prefix(input: &[u8]) -> Result<(Address, String, Uint), PrecompileFailure> { + let partial_types = vec![ParamType::Address, ParamType::String, ParamType::Uint(8)]; + let partial_tokens = decode(&partial_types, input); + let Ok(partial_tokens) = partial_tokens else { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other(Cow::Borrowed("Incorrect input")), + }); + }; + + if partial_types.len() < 3 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other(Cow::Borrowed("Incorrect input")), + }); + } + + let (Token::Address(code_address), Token::String(name), Token::Uint(preserve_sender)) = (&partial_tokens[0], &partial_tokens[1], &partial_tokens[2]) else { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other(Cow::Borrowed("Incorrect input")), + }); + }; + + Ok(( + code_address.to_owned(), + name.to_owned(), + preserve_sender.to_owned(), + )) +} diff --git a/src/libCps/CpsAccountStoreInterface.h b/src/libCps/CpsAccountStoreInterface.h index bb9bfa89f8..f598be3b73 100644 --- a/src/libCps/CpsAccountStoreInterface.h +++ b/src/libCps/CpsAccountStoreInterface.h @@ -90,6 +90,7 @@ struct CpsAccountStoreInterface { virtual void MarkNewLibraryCreated(const Address& address) = 0; virtual CpsAccountStoreInterface::AccountType GetAccountType( const Address& address) = 0; + virtual bool isAccountEvmContract(const Address& address) const = 0; }; } // namespace libCps diff --git a/src/libCps/CpsRunEvm.cpp b/src/libCps/CpsRunEvm.cpp index d182b7876e..983d55b885 100644 --- a/src/libCps/CpsRunEvm.cpp +++ b/src/libCps/CpsRunEvm.cpp @@ -244,9 +244,9 @@ CpsExecuteResult CpsRunEvm::HandleCallTrap(const evm::EvmResult& result) { LOG_GENERAL(INFO, "Saving storage for Address: " << thisContractAddress); if (!mAccountStore.UpdateStateValue( - thisContractAddress, - DataConversion::StringToCharArray(sit.key()), 0, - DataConversion::StringToCharArray(sit.value()), 0)) { + thisContractAddress, + DataConversion::StringToCharArray(sit.key()), 0, + DataConversion::StringToCharArray(sit.value()), 0)) { } } @@ -399,7 +399,7 @@ CpsExecuteResult CpsRunEvm::HandlePrecompileTrap( const auto sender = (jsonData["keep_origin"].isBool() && jsonData["keep_origin"].asBool() == true) - ? mCpsContext.origSender.hex() + ? ProtoToAddress(mProtoArgs.caller()).hex() : ProtoToAddress(mProtoArgs.address()).hex(); jsonData.removeMember("keep_origin"); @@ -450,14 +450,16 @@ CpsExecuteResult CpsRunEvm::HandlePrecompileTrap( const auto destAddress = jsonData["_address"].asString(); - ScillaArgs scillaArgs = {.from = ProtoToAddress(mProtoArgs.address()), - .dest = Address{destAddress}, - .origin = mCpsContext.origSender, - .value = Amount{}, - .calldata = jsonData, - .edge = 0, - .depth = 0, - .gasLimit = remainingGas}; + ScillaArgs scillaArgs = { + .from = ProtoToAddress(mProtoArgs.address()), + .dest = Address{destAddress}, + .origin = mCpsContext.origSender, + .value = Amount{}, + .calldata = jsonData, + .edge = 0, + .depth = 0, + .gasLimit = remainingGas, + .extras = ScillaArgExtras{.scillaReceiverAddress = Address{}}}; auto nextRun = std::make_shared( std::move(scillaArgs), mExecutor, mCpsContext, CpsRun::TrapScillaCall); @@ -719,14 +721,15 @@ void CpsRunEvm::HandleApply(const evm::EvmResult& result, // Funds is what we want our contract to become/be modified to. // Check that the contract funds plus the current funds in our account // is equal to this value - if(funds != recipientPreFunds + currentContractFunds) { - std::string error = - "Possible zil mint. Funds in destroyed account: " + - currentContractFunds.toWei().convert_to() + - ", requested: " + (funds - recipientPreFunds).toWei().convert_to(); - - LOG_GENERAL(WARNING, "ERROR IN DESTUCT! " << error); - span.SetError(error); + if (funds != recipientPreFunds + currentContractFunds) { + std::string error = + "Possible zil mint. Funds in destroyed account: " + + currentContractFunds.toWei().convert_to() + + ", requested: " + + (funds - recipientPreFunds).toWei().convert_to(); + + LOG_GENERAL(WARNING, "ERROR IN DESTUCT! " << error); + span.SetError(error); } mAccountStore.TransferBalanceAtomic(accountToRemove, fundsRecipient, diff --git a/src/libCps/CpsRunScilla.cpp b/src/libCps/CpsRunScilla.cpp index ab77cd0ee6..a9a3546d5a 100644 --- a/src/libCps/CpsRunScilla.cpp +++ b/src/libCps/CpsRunScilla.cpp @@ -328,7 +328,13 @@ CpsExecuteResult CpsRunScilla::runCall(TransactionReceipt& receipt) { mAccountStore, mArgs, runnerResult.returnVal, receipt, scillaVersion); if (!parseCallResults.success) { + // Revert in case of non-recoverable failures + if (parseCallResults.failureType == + ScillaCallParseResult::NON_RECOVERABLE) { + return {TxnStatus::NOT_PRESENT, false, retScillaVal}; + } // Allow TrapScilla call to fail and let EVM handle errored run accordingly + // (only for recoverable failures) if (GetType() == CpsRun::TrapScillaCall) { return {TxnStatus::NOT_PRESENT, true, retScillaVal}; } diff --git a/src/libCps/CpsRunScilla.h b/src/libCps/CpsRunScilla.h index 428a83700e..028f0a16e6 100644 --- a/src/libCps/CpsRunScilla.h +++ b/src/libCps/CpsRunScilla.h @@ -38,6 +38,11 @@ struct ScillaInvokeResult { std::string returnVal; }; +struct ScillaArgExtras { + using Address = dev::h160; + Address scillaReceiverAddress; +}; + struct ScillaArgs { using Address = dev::h160; struct CodeData { @@ -52,6 +57,7 @@ struct ScillaArgs { uint32_t edge = 0; uint32_t depth = 0; uint64_t gasLimit = 0; + std::optional extras = std::nullopt; }; class CpsRunScilla final : public CpsRun { diff --git a/src/libCps/ScillaHelpersCall.cpp b/src/libCps/ScillaHelpersCall.cpp index c0c4172d10..c890c791c2 100644 --- a/src/libCps/ScillaHelpersCall.cpp +++ b/src/libCps/ScillaHelpersCall.cpp @@ -77,7 +77,7 @@ ScillaCallParseResult ScillaHelpersCall::ParseCallContractOutput( << r_timer_end(tpStart)); } - return {true, false, {}}; + return {true, false}; } /// parse the output from interpreter for calling and update states @@ -195,6 +195,15 @@ ScillaCallParseResult ScillaHelpersCall::ParseCallContractJsonOutput( return {}; } + // At this point we don't support any named calls from Scilla to EVM + for (const auto ¶m : msg["params"]) { + if (param.isMember("vname") && param["vname"] == "_EvmCall") { + receipt.AddError(CALL_CONTRACT_FAILED); + return ScillaCallParseResult{ + .success = false, + .failureType = ScillaCallParseResult::NON_RECOVERABLE}; + } + } const auto recipient = Address(msg["_recipient"].asString()); // Recipient is contract @@ -219,15 +228,32 @@ ScillaCallParseResult ScillaHelpersCall::ParseCallContractJsonOutput( // ZIL-5165: Don't fail if the recipient is a user account. { - const CpsAccountStoreInterface::AccountType accountType = acc_store.GetAccountType(recipient); + const CpsAccountStoreInterface::AccountType accountType = + acc_store.GetAccountType(recipient); LOG_GENERAL(INFO, "Target is accountType " << accountType); if (accountType == CpsAccountStoreInterface::DoesNotExist || accountType == CpsAccountStoreInterface::EOA) { LOG_GENERAL(INFO, "Target is EOA: processing."); - // Message sent to a non-contract account. Add something to results.entries so that if this - // message attempts to transfer funds, it succeeds. - results.entries.emplace_back(ScillaCallParseResult::SingleResult{ - {}, recipient, amount, false}); + // Message sent to a non-contract account. Add something to + // results.entries so that if this message attempts to transfer funds, + // it succeeds. + results.entries.emplace_back( + ScillaCallParseResult::SingleResult{{}, recipient, amount, false}); + continue; + } + } + + if (acc_store.isAccountEvmContract(recipient)) { + // Workaround before we have full interop: treat EVM contracts as EOA + // accounts only if there's receiver_address set to 0x0, otherwise revert + if (!scillaArgs.extras || + scillaArgs.extras->scillaReceiverAddress != Address{}) { + return ScillaCallParseResult{ + .success = false, + .failureType = ScillaCallParseResult::NON_RECOVERABLE}; + } else { + results.entries.emplace_back( + ScillaCallParseResult::SingleResult{{}, recipient, amount, false}); continue; } } @@ -267,7 +293,8 @@ ScillaCallParseResult ScillaHelpersCall::ParseCallContractJsonOutput( std::move(inputMessage), recipient, amount, isNextContract}); } - LOG_GENERAL(INFO, "Returning success " << results.success << " entries " << results.entries.size()); + LOG_GENERAL(INFO, "Returning success " << results.success << " entries " + << results.entries.size()); return results; } diff --git a/src/libCps/ScillaHelpersCall.h b/src/libCps/ScillaHelpersCall.h index cf44807595..2fb417c4d8 100644 --- a/src/libCps/ScillaHelpersCall.h +++ b/src/libCps/ScillaHelpersCall.h @@ -32,13 +32,16 @@ struct ScillaCallParseResult { // if contract accepted sent amount from a call (should be succeeded by a // transfer) bool accepted = false; + enum FailureType { RECOVERABLE = 0, NON_RECOVERABLE }; + FailureType failureType = FailureType::RECOVERABLE; + struct SingleResult { Json::Value nextInputMessage; Address nextAddress; Amount amount; bool isNextContract = false; }; - std::vector entries; + std::vector entries = {}; }; class ScillaHelpersCall final { diff --git a/src/libData/AccountStore/AccountStoreCpsInterface.h b/src/libData/AccountStore/AccountStoreCpsInterface.h index fc58e67ada..148a0d753b 100644 --- a/src/libData/AccountStore/AccountStoreCpsInterface.h +++ b/src/libData/AccountStore/AccountStoreCpsInterface.h @@ -276,6 +276,14 @@ class AccountStoreCpsInterface : public libCps::CpsAccountStoreInterface { mAccountStore.m_newLibrariesCreated.push_back(address); } + virtual bool isAccountEvmContract(const Address& address) const override { + Account* account = mAccountStore.GetAccountAtomic(address); + if (account == nullptr) { + return false; + } + return account->isContract() && EvmUtils::isEvm(account->GetCode()); + } + private: AccountStoreSC& mAccountStore; std::string mScillaRootVersion; diff --git a/tests/EvmAcceptanceTests/artifacts/scilla.cache b/tests/EvmAcceptanceTests/artifacts/scilla.cache index d887c0641b..ea8c3f386d 100644 --- a/tests/EvmAcceptanceTests/artifacts/scilla.cache +++ b/tests/EvmAcceptanceTests/artifacts/scilla.cache @@ -1 +1 @@ -{"contracts/scilla/AdditionLib_Errored.scillib":{"hash":"26d8bd82410d3b62087a37186261576d","path":"contracts/scilla/AdditionLib_Errored.scillib","parsedContract":{"name":"AdditionLibErrored","transitions":[],"fields":[],"constructorParams":[],"ctors":[]}},"contracts/scilla/AdditionLib.scillib":{"hash":"4b36e802c862ddb7a118fe698a7af17a","path":"contracts/scilla/AdditionLib.scillib","parsedContract":{"name":"AdditionLib","transitions":[],"fields":[],"constructorParams":[],"ctors":[]}},"contracts/scilla/BasicInterop.scilla":{"hash":"6d04c1a7522c5b07019b14370bd18e3f","path":"contracts/scilla/BasicInterop.scilla","parsedContract":{"name":"BasicInterop","transitions":[{"type":"CompTrans","name":"setSimpleMap","params":[{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"setNestedMap","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"setString","params":[{"name":"str","typeJSON":"String","type":"String"}]},{"type":"CompTrans","name":"setUint","params":[{"name":"str","typeJSON":"Uint128","type":"Uint128"}]}],"fields":[{"type":"Map","name":"simpleMap"},{"type":"Map","name":"nestedMap"},{"type":"Uint128","name":"uintField"},{"type":"String","name":"strField"}],"constructorParams":null,"ctors":[]}},"contracts/scilla/ByStr.scilla":{"hash":"f62ba8373e78ab29df6bafd20c28f86b","path":"contracts/scilla/ByStr.scilla","parsedContract":{"name":"ByStrFunctionality","transitions":[{"type":"CompTrans","name":"getByStr5Field","params":[]},{"type":"CompTrans","name":"getByStrConcat","params":[]},{"type":"CompTrans","name":"getByStrLen","params":[{"name":"first","typeJSON":"ByStr","type":"ByStr"}]}],"fields":[{"type":"ByStr5","name":"byte5_field"},{"type":"ByStr6","name":"byte6_field"},{"type":"Uint32","name":"bystr_len"}],"constructorParams":null,"ctors":[]}},"contracts/scilla/chainid.scilla":{"hash":"35e1e541fd230bce09763879a37afc86","path":"contracts/scilla/chainid.scilla","parsedContract":{"name":"ChainId","transitions":[{"type":"CompTrans","name":"EventChainID","params":[]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/codehash.scilla":{"hash":"55c8fe558a423f6c8028417d96825006","path":"contracts/scilla/codehash.scilla","parsedContract":{"name":"Codehash","transitions":[{"type":"CompTrans","name":"foo","params":[{"name":"addr","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"foo2","params":[{"name":"addr","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"foo3","params":[{"name":"addr","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"foo4","params":[{"name":"addr","typeJSON":"ByStr20","type":"ByStr20"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/deployment/ImmutableString.scilla":{"hash":"58c9f603c5ac58860e06e29c6140626e","path":"contracts/scilla/deployment/ImmutableString.scilla","parsedContract":{"name":"ImmutableString","transitions":[{"type":"CompTrans","name":"getString","params":[]},{"type":"CompTrans","name":"getContractAddress","params":[]}],"fields":[],"constructorParams":[{"type":"String","name":"immutable_string"}],"ctors":[]}},"contracts/scilla/ecdsa.scilla":{"hash":"6c65dc8fcd0ccc62e973929c6ea86a42","path":"contracts/scilla/ecdsa.scilla","parsedContract":{"name":"Ecdsa","transitions":[{"type":"CompTrans","name":"recover","params":[{"name":"msg","typeJSON":"ByStr","type":"ByStr"},{"name":"sig","typeJSON":"ByStr64","type":"ByStr64"},{"name":"recid","typeJSON":"Uint32","type":"Uint32"}]},{"type":"CompTrans","name":"verify","params":[{"name":"pubk","typeJSON":"ByStr33","type":"ByStr33"},{"name":"msg","typeJSON":"ByStr","type":"ByStr"},{"name":"sig","typeJSON":"ByStr64","type":"ByStr64"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/FungibleToken.scilla":{"hash":"537803d3693d38d0bd9324dc874e3e2a","path":"contracts/scilla/FungibleToken.scilla","parsedContract":{"name":"FungibleToken","transitions":[{"type":"CompProc","name":"ThrowError","params":[{"name":"err","typeJSON":{"ctor":"Error","argtypes":[]},"type":"Error"}]},{"type":"CompProc","name":"IsNotSender","params":[{"name":"address","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompProc","name":"AuthorizedMoveIfSufficientBalance","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"IncreaseAllowance","params":[{"name":"spender","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"DecreaseAllowance","params":[{"name":"spender","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Transfer","params":[{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"TransferFrom","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]}],"fields":[{"type":"Uint128","name":"total_supply"},{"type":"Map","name":"balances"},{"type":"Map","name":"allowances"}],"constructorParams":[{"type":"ByStr20","name":"contract_owner"},{"type":"String","name":"name"},{"type":"String","name":"symbol"},{"type":"Uint32","name":"decimals"},{"type":"Uint128","name":"init_supply"}],"ctors":[{"typename":"Error","ctorname":"CodeIsSender","argtypes":[]},{"typename":"Error","ctorname":"CodeInsufficientFunds","argtypes":[]},{"typename":"Error","ctorname":"CodeInsufficientAllowance","argtypes":[]},{"typename":"Unit","ctorname":"Unit","argtypes":[]}]}},"contracts/scilla/HelloWorld.scilla":{"hash":"d620d417615518045bd7099fe14ccd3c","path":"contracts/scilla/HelloWorld.scilla","parsedContract":{"name":"HelloWorld","transitions":[{"type":"CompTrans","name":"setHello","params":[{"name":"msg","typeJSON":"String","type":"String"}]},{"type":"CompTrans","name":"getHello","params":[]}],"fields":[{"type":"String","name":"welcome_msg"}],"constructorParams":[{"type":"ByStr20","name":"owner"}],"ctors":[]}},"contracts/scilla/MutualLib.scillib":{"hash":"dfe87093a6a15cb484015c1aa0b299ab","path":"contracts/scilla/MutualLib.scillib","parsedContract":{"name":"MutualLib","transitions":[],"fields":[],"constructorParams":[],"ctors":[]}},"contracts/scilla/SendZil.scilla":{"hash":"35a00d1d1f197927e970498a21afbc00","path":"contracts/scilla/SendZil.scilla","parsedContract":{"name":"SendZil","transitions":[{"type":"CompTrans","name":"acceptZil","params":[]},{"type":"CompTrans","name":"updateTestField","params":[{"name":"val","typeJSON":"Uint256","type":"Uint256"}]},{"type":"CompTrans","name":"dontAcceptZil","params":[]},{"type":"CompTrans","name":"fundUserWithTag","params":[{"name":"user","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"fundUser","params":[{"name":"user","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"fundContract","params":[{"name":"contract_address","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"callOtherContract","params":[{"name":"contract_address","typeJSON":"ByStr20","type":"ByStr20"},{"name":"tag","typeJSON":"String","type":"String"},{"name":"value","typeJSON":"Uint256","type":"Uint256"}]}],"fields":[{"type":"Uint256","name":"test_field"}],"constructorParams":null,"ctors":[]}},"contracts/scilla/SetGet.scilla":{"hash":"789c0036812698c9d477cc0d66a1c4ba","path":"contracts/scilla/SetGet.scilla","parsedContract":{"name":"SetGet","transitions":[{"type":"CompTrans","name":"set","params":[{"name":"v","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"emit","params":[]},{"type":"CompTrans","name":"set_string","params":[{"name":"v","typeJSON":"String","type":"String"}]},{"type":"CompTrans","name":"get_string","params":[]},{"type":"CompTrans","name":"set_address","params":[{"name":"v","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"get_address","params":[]}],"fields":[{"type":"Uint128","name":"value"},{"type":"String","name":"string_value"},{"type":"ByStr20","name":"address_value"}],"constructorParams":null,"ctors":[]}},"contracts/scilla/TestContract1.scilla":{"hash":"4c1ae834a226bf6977a2c04b0896b690","path":"contracts/scilla/TestContract1.scilla","parsedContract":{"name":"TestContract1","transitions":[{"type":"CompTrans","name":"addNumWithExtLib","params":[{"name":"a","typeJSON":"Uint128","type":"Uint128"},{"name":"b","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Sending","params":[{"name":"c2","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"Receiving","params":[{"name":"param","typeJSON":{"ctor":"T2","argtypes":[]},"type":"T2"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/TestContract2.scilla":{"hash":"b8bb3b274cc08af3a94359ce324aaaa4","path":"contracts/scilla/TestContract2.scilla","parsedContract":{"name":"TestContract2","transitions":[{"type":"CompTrans","name":"Receiving","params":[{"name":"param","typeJSON":{"ctor":"T1","argtypes":[]},"type":"T1"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/Timestamp.scilla":{"hash":"c5fd976cfbb5a50ee14d05ccee927af5","path":"contracts/scilla/Timestamp.scilla","parsedContract":{"name":"Timestamp","transitions":[{"type":"CompTrans","name":"EventTimestamp","params":[{"name":"bnum","typeJSON":"BNum","type":"BNum"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/ZRC2Interop.scilla":{"hash":"8c1a0e31968ebdce4d6dd61f6a645cf4","path":"contracts/scilla/ZRC2Interop.scilla","parsedContract":{"name":"ZRC2Interop","transitions":[{"type":"CompProc","name":"ThrowError","params":[{"name":"err","typeJSON":{"ctor":"Error","argtypes":[]},"type":"Error"}]},{"type":"CompProc","name":"IsOwner","params":[{"name":"address","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompProc","name":"IsNotSender","params":[{"name":"address","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompProc","name":"AuthorizedMint","params":[{"name":"recipient","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompProc","name":"AuthorizedBurnIfSufficientBalance","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompProc","name":"AuthorizedMoveIfSufficientBalance","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Mint","params":[{"name":"recipient","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Burn","params":[{"name":"burn_account","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"IncreaseAllowance","params":[{"name":"spender","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"DecreaseAllowance","params":[{"name":"spender","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Transfer","params":[{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"TransferFrom","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]}],"fields":[{"type":"Uint128","name":"total_supply"},{"type":"Map","name":"balances"},{"type":"Map","name":"allowances"}],"constructorParams":[{"type":"ByStr20","name":"contract_owner"},{"type":"String","name":"name"},{"type":"String","name":"symbol"},{"type":"Uint32","name":"decimals"},{"type":"Uint128","name":"init_supply"}],"ctors":[{"typename":"Error","ctorname":"CodeIsSender","argtypes":[]},{"typename":"Error","ctorname":"CodeInsufficientFunds","argtypes":[]},{"typename":"Error","ctorname":"CodeInsufficientAllowance","argtypes":[]},{"typename":"Error","ctorname":"CodeNotOwner","argtypes":[]},{"typename":"Unit","ctorname":"Unit","argtypes":[]}]}}} \ No newline at end of file +{"contracts/scilla/AdditionLib_Errored.scillib":{"hash":"26d8bd82410d3b62087a37186261576d","path":"contracts/scilla/AdditionLib_Errored.scillib","parsedContract":{"name":"AdditionLibErrored","transitions":[],"fields":[],"constructorParams":[],"ctors":[]}},"contracts/scilla/AdditionLib.scillib":{"hash":"4b36e802c862ddb7a118fe698a7af17a","path":"contracts/scilla/AdditionLib.scillib","parsedContract":{"name":"AdditionLib","transitions":[],"fields":[],"constructorParams":[],"ctors":[]}},"contracts/scilla/BasicInterop.scilla":{"hash":"6d04c1a7522c5b07019b14370bd18e3f","path":"contracts/scilla/BasicInterop.scilla","parsedContract":{"name":"BasicInterop","transitions":[{"type":"CompTrans","name":"setSimpleMap","params":[{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"setNestedMap","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"setString","params":[{"name":"str","typeJSON":"String","type":"String"}]},{"type":"CompTrans","name":"setUint","params":[{"name":"str","typeJSON":"Uint128","type":"Uint128"}]}],"fields":[{"type":"Map","name":"simpleMap"},{"type":"Map","name":"nestedMap"},{"type":"Uint128","name":"uintField"},{"type":"String","name":"strField"}],"constructorParams":null,"ctors":[]}},"contracts/scilla/ByStr.scilla":{"hash":"f62ba8373e78ab29df6bafd20c28f86b","path":"contracts/scilla/ByStr.scilla","parsedContract":{"name":"ByStrFunctionality","transitions":[{"type":"CompTrans","name":"getByStr5Field","params":[]},{"type":"CompTrans","name":"getByStrConcat","params":[]},{"type":"CompTrans","name":"getByStrLen","params":[{"name":"first","typeJSON":"ByStr","type":"ByStr"}]}],"fields":[{"type":"ByStr5","name":"byte5_field"},{"type":"ByStr6","name":"byte6_field"},{"type":"Uint32","name":"bystr_len"}],"constructorParams":null,"ctors":[]}},"contracts/scilla/chainid.scilla":{"hash":"35e1e541fd230bce09763879a37afc86","path":"contracts/scilla/chainid.scilla","parsedContract":{"name":"ChainId","transitions":[{"type":"CompTrans","name":"EventChainID","params":[]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/codehash.scilla":{"hash":"55c8fe558a423f6c8028417d96825006","path":"contracts/scilla/codehash.scilla","parsedContract":{"name":"Codehash","transitions":[{"type":"CompTrans","name":"foo","params":[{"name":"addr","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"foo2","params":[{"name":"addr","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"foo3","params":[{"name":"addr","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"foo4","params":[{"name":"addr","typeJSON":"ByStr20","type":"ByStr20"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/deployment/ImmutableString.scilla":{"hash":"58c9f603c5ac58860e06e29c6140626e","path":"contracts/scilla/deployment/ImmutableString.scilla","parsedContract":{"name":"ImmutableString","transitions":[{"type":"CompTrans","name":"getString","params":[]},{"type":"CompTrans","name":"getContractAddress","params":[]}],"fields":[],"constructorParams":[{"type":"String","name":"immutable_string"}],"ctors":[]}},"contracts/scilla/ecdsa.scilla":{"hash":"6c65dc8fcd0ccc62e973929c6ea86a42","path":"contracts/scilla/ecdsa.scilla","parsedContract":{"name":"Ecdsa","transitions":[{"type":"CompTrans","name":"recover","params":[{"name":"msg","typeJSON":"ByStr","type":"ByStr"},{"name":"sig","typeJSON":"ByStr64","type":"ByStr64"},{"name":"recid","typeJSON":"Uint32","type":"Uint32"}]},{"type":"CompTrans","name":"verify","params":[{"name":"pubk","typeJSON":"ByStr33","type":"ByStr33"},{"name":"msg","typeJSON":"ByStr","type":"ByStr"},{"name":"sig","typeJSON":"ByStr64","type":"ByStr64"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/FungibleToken.scilla":{"hash":"ee33c91b9d5c83703539800e9fa9474d","path":"contracts/scilla/FungibleToken.scilla","parsedContract":{"name":"FungibleToken","transitions":[{"type":"CompProc","name":"ThrowError","params":[{"name":"err","typeJSON":{"ctor":"Error","argtypes":[]},"type":"Error"}]},{"type":"CompProc","name":"IsNotSender","params":[{"name":"address","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompProc","name":"AuthorizedMoveIfSufficientBalance","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"IncreaseAllowance","params":[{"name":"spender","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"DecreaseAllowance","params":[{"name":"spender","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Transfer","params":[{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"TransferFailed","params":[{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"TransferFrom","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]}],"fields":[{"type":"Uint128","name":"total_supply"},{"type":"Map","name":"balances"},{"type":"Map","name":"allowances"}],"constructorParams":[{"type":"ByStr20","name":"contract_owner"},{"type":"String","name":"name"},{"type":"String","name":"symbol"},{"type":"Uint32","name":"decimals"},{"type":"Uint128","name":"init_supply"}],"ctors":[{"typename":"Error","ctorname":"CodeIsSender","argtypes":[]},{"typename":"Error","ctorname":"CodeInsufficientFunds","argtypes":[]},{"typename":"Error","ctorname":"CodeInsufficientAllowance","argtypes":[]},{"typename":"Unit","ctorname":"Unit","argtypes":[]}]}},"contracts/scilla/HelloWorld.scilla":{"hash":"d620d417615518045bd7099fe14ccd3c","path":"contracts/scilla/HelloWorld.scilla","parsedContract":{"name":"HelloWorld","transitions":[{"type":"CompTrans","name":"setHello","params":[{"name":"msg","typeJSON":"String","type":"String"}]},{"type":"CompTrans","name":"getHello","params":[]}],"fields":[{"type":"String","name":"welcome_msg"}],"constructorParams":[{"type":"ByStr20","name":"owner"}],"ctors":[]}},"contracts/scilla/MutualLib.scillib":{"hash":"dfe87093a6a15cb484015c1aa0b299ab","path":"contracts/scilla/MutualLib.scillib","parsedContract":{"name":"MutualLib","transitions":[],"fields":[],"constructorParams":[],"ctors":[]}},"contracts/scilla/SendZil.scilla":{"hash":"35a00d1d1f197927e970498a21afbc00","path":"contracts/scilla/SendZil.scilla","parsedContract":{"name":"SendZil","transitions":[{"type":"CompTrans","name":"acceptZil","params":[]},{"type":"CompTrans","name":"updateTestField","params":[{"name":"val","typeJSON":"Uint256","type":"Uint256"}]},{"type":"CompTrans","name":"dontAcceptZil","params":[]},{"type":"CompTrans","name":"fundUserWithTag","params":[{"name":"user","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"fundUser","params":[{"name":"user","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"fundContract","params":[{"name":"contract_address","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"callOtherContract","params":[{"name":"contract_address","typeJSON":"ByStr20","type":"ByStr20"},{"name":"tag","typeJSON":"String","type":"String"},{"name":"value","typeJSON":"Uint256","type":"Uint256"}]}],"fields":[{"type":"Uint256","name":"test_field"}],"constructorParams":null,"ctors":[]}},"contracts/scilla/SetGet.scilla":{"hash":"789c0036812698c9d477cc0d66a1c4ba","path":"contracts/scilla/SetGet.scilla","parsedContract":{"name":"SetGet","transitions":[{"type":"CompTrans","name":"set","params":[{"name":"v","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"emit","params":[]},{"type":"CompTrans","name":"set_string","params":[{"name":"v","typeJSON":"String","type":"String"}]},{"type":"CompTrans","name":"get_string","params":[]},{"type":"CompTrans","name":"set_address","params":[{"name":"v","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"get_address","params":[]}],"fields":[{"type":"Uint128","name":"value"},{"type":"String","name":"string_value"},{"type":"ByStr20","name":"address_value"}],"constructorParams":null,"ctors":[]}},"contracts/scilla/TestContract1.scilla":{"hash":"4c1ae834a226bf6977a2c04b0896b690","path":"contracts/scilla/TestContract1.scilla","parsedContract":{"name":"TestContract1","transitions":[{"type":"CompTrans","name":"addNumWithExtLib","params":[{"name":"a","typeJSON":"Uint128","type":"Uint128"},{"name":"b","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Sending","params":[{"name":"c2","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompTrans","name":"Receiving","params":[{"name":"param","typeJSON":{"ctor":"T2","argtypes":[]},"type":"T2"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/TestContract2.scilla":{"hash":"b8bb3b274cc08af3a94359ce324aaaa4","path":"contracts/scilla/TestContract2.scilla","parsedContract":{"name":"TestContract2","transitions":[{"type":"CompTrans","name":"Receiving","params":[{"name":"param","typeJSON":{"ctor":"T1","argtypes":[]},"type":"T1"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/Timestamp.scilla":{"hash":"c5fd976cfbb5a50ee14d05ccee927af5","path":"contracts/scilla/Timestamp.scilla","parsedContract":{"name":"Timestamp","transitions":[{"type":"CompTrans","name":"EventTimestamp","params":[{"name":"bnum","typeJSON":"BNum","type":"BNum"}]}],"fields":[],"constructorParams":null,"ctors":[]}},"contracts/scilla/ZRC2Interop.scilla":{"hash":"8c1a0e31968ebdce4d6dd61f6a645cf4","path":"contracts/scilla/ZRC2Interop.scilla","parsedContract":{"name":"ZRC2Interop","transitions":[{"type":"CompProc","name":"ThrowError","params":[{"name":"err","typeJSON":{"ctor":"Error","argtypes":[]},"type":"Error"}]},{"type":"CompProc","name":"IsOwner","params":[{"name":"address","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompProc","name":"IsNotSender","params":[{"name":"address","typeJSON":"ByStr20","type":"ByStr20"}]},{"type":"CompProc","name":"AuthorizedMint","params":[{"name":"recipient","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompProc","name":"AuthorizedBurnIfSufficientBalance","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompProc","name":"AuthorizedMoveIfSufficientBalance","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Mint","params":[{"name":"recipient","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Burn","params":[{"name":"burn_account","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"IncreaseAllowance","params":[{"name":"spender","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"DecreaseAllowance","params":[{"name":"spender","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"Transfer","params":[{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]},{"type":"CompTrans","name":"TransferFrom","params":[{"name":"from","typeJSON":"ByStr20","type":"ByStr20"},{"name":"to","typeJSON":"ByStr20","type":"ByStr20"},{"name":"amount","typeJSON":"Uint128","type":"Uint128"}]}],"fields":[{"type":"Uint128","name":"total_supply"},{"type":"Map","name":"balances"},{"type":"Map","name":"allowances"}],"constructorParams":[{"type":"ByStr20","name":"contract_owner"},{"type":"String","name":"name"},{"type":"String","name":"symbol"},{"type":"Uint32","name":"decimals"},{"type":"Uint128","name":"init_supply"}],"ctors":[{"typename":"Error","ctorname":"CodeIsSender","argtypes":[]},{"typename":"Error","ctorname":"CodeInsufficientFunds","argtypes":[]},{"typename":"Error","ctorname":"CodeInsufficientAllowance","argtypes":[]},{"typename":"Error","ctorname":"CodeNotOwner","argtypes":[]},{"typename":"Unit","ctorname":"Unit","argtypes":[]}]}}} \ No newline at end of file diff --git a/tests/EvmAcceptanceTests/contracts/BasicInterop.sol b/tests/EvmAcceptanceTests/contracts/BasicInterop.sol index db8d729546..5c47356446 100644 --- a/tests/EvmAcceptanceTests/contracts/BasicInterop.sol +++ b/tests/EvmAcceptanceTests/contracts/BasicInterop.sol @@ -14,13 +14,14 @@ pragma abicoder v2; // Returned value is encoded via abi so you should call abi.decode() with proper type to obtain underlying value contract BasicInterop { + event ResultWas(bool); - function callSimpleMap(address contract_address, string memory tran_name, address recipient, uint128 amount) public { - bytes memory encodedArgs = abi.encode(contract_address, tran_name, recipient, amount); + function callSimpleMap(address contract_address, string memory tran_name, uint256 keep_origin, address recipient, uint128 amount) public { + bytes memory encodedArgs = abi.encode(contract_address, tran_name, keep_origin, recipient, amount); uint256 argsLength = encodedArgs.length; bool success; assembly { - success := call(21000, 0x5a494c51, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) + success := call(21000, 0x5a494c53, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) } require(success); } @@ -39,13 +40,13 @@ contract BasicInterop { } - function callNestedMap(address contract_address, string memory tran_name, address idx1, address idx2, uint128 amount) public { - bytes memory encodedArgs = abi.encode(contract_address, tran_name, idx1, idx2, amount); + function callNestedMap(address contract_address, string memory tran_name, uint256 keep_origin, address idx1, address idx2, uint128 amount) public { + bytes memory encodedArgs = abi.encode(contract_address, tran_name, keep_origin, idx1, idx2, amount); uint256 argsLength = encodedArgs.length; bool success; assembly { - success := call(21000, 0x5a494c51, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) + success := call(21000, 0x5a494c53, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) } require(success); } @@ -64,13 +65,13 @@ contract BasicInterop { } - function callUint(address contract_address, string memory tran_name, uint128 amount) public { - bytes memory encodedArgs = abi.encode(contract_address, tran_name, amount); + function callUint(address contract_address, string memory tran_name, uint256 keep_origin, uint128 amount) public { + bytes memory encodedArgs = abi.encode(contract_address, tran_name, keep_origin, amount); uint256 argsLength = encodedArgs.length; bool success; assembly { - success := call(21000, 0x5a494c51, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) + success := call(21000, 0x5a494c53, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) } require(success); } @@ -90,13 +91,13 @@ contract BasicInterop { return funds; } - function callString(address contract_address, string memory tran_name, string memory value) public { - bytes memory encodedArgs = abi.encode(contract_address, tran_name, value); + function callString(address contract_address, string memory tran_name, uint256 keep_origin, string memory value) public { + bytes memory encodedArgs = abi.encode(contract_address, tran_name, keep_origin, value); uint256 argsLength = encodedArgs.length; bool success; assembly { - success := call(21000, 0x5a494c51, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) + success := call(21000, 0x5a494c53, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) } require(success); } @@ -115,4 +116,4 @@ contract BasicInterop { (retVal) = abi.decode(output, (string)); return retVal; } -} \ No newline at end of file +} diff --git a/tests/EvmAcceptanceTests/contracts/ERC20Interop.sol b/tests/EvmAcceptanceTests/contracts/ERC20Interop.sol index a82d68eea9..2ec387c84f 100644 --- a/tests/EvmAcceptanceTests/contracts/ERC20Interop.sol +++ b/tests/EvmAcceptanceTests/contracts/ERC20Interop.sol @@ -56,6 +56,8 @@ contract ERC20Interop is ZRC2ERC20Interface, SafeMath { address private _contract_owner; address private _zrc2_address; + + uint256 private constant CALL_MODE = 1; constructor(address zrc2_address) { _contract_owner = msg.sender; @@ -157,21 +159,21 @@ contract ERC20Interop is ZRC2ERC20Interface, SafeMath { // Private functions used for accessing ZRC2 contract function _call_scilla_two_args(string memory tran_name, address recipient, uint128 amount) private { - bytes memory encodedArgs = abi.encode(_zrc2_address, tran_name, recipient, amount); + bytes memory encodedArgs = abi.encode(_zrc2_address, tran_name, CALL_MODE, recipient, amount); uint256 argsLength = encodedArgs.length; bool success; assembly { - success := call(21000, 0x5a494c51, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) + success := call(21000, 0x5a494c53, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) } require(success); } function _call_scilla_three_args(string memory tran_name, address from, address to, uint128 amount) private { - bytes memory encodedArgs = abi.encode(_zrc2_address, tran_name, from, to, amount); + bytes memory encodedArgs = abi.encode(_zrc2_address, tran_name, CALL_MODE, from, to, amount); uint256 argsLength = encodedArgs.length; bool success; assembly { - success := call(21000, 0x5a494c51, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) + success := call(21000, 0x5a494c53, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) } require(success); } diff --git a/tests/EvmAcceptanceTests/contracts/ERC20isZRC2.sol b/tests/EvmAcceptanceTests/contracts/ERC20isZRC2.sol index d92bdfb050..8b2c430343 100644 --- a/tests/EvmAcceptanceTests/contracts/ERC20isZRC2.sol +++ b/tests/EvmAcceptanceTests/contracts/ERC20isZRC2.sol @@ -43,6 +43,8 @@ contract ERC20isZRC2 is ERC20Interface, SafeMath { address private _contract_owner; address private _zrc2_address; + uint256 private constant CALL_MODE = 1; + constructor(address zrc2_address) { _contract_owner = msg.sender; _zrc2_address = zrc2_address; @@ -61,6 +63,11 @@ contract ERC20isZRC2 is ERC20Interface, SafeMath { return true; } + function transferFailed(address to, uint128 tokens) external returns (bool) { + _call_scilla_two_args("TransferFailed", to, tokens); + return true; + } + function transferFrom(address from, address to, uint128 tokens) external returns (bool) { _call_scilla_three_args("TransferFrom", from, to, tokens); return true; @@ -96,21 +103,21 @@ contract ERC20isZRC2 is ERC20Interface, SafeMath { // Private functions used for accessing ZRC2 contract function _call_scilla_two_args(string memory tran_name, address recipient, uint128 amount) private { - bytes memory encodedArgs = abi.encode(_zrc2_address, tran_name, recipient, amount); + bytes memory encodedArgs = abi.encode(_zrc2_address, tran_name, CALL_MODE, recipient, amount); uint256 argsLength = encodedArgs.length; bool success; assembly { - success := call(21000, 0x5a494c52, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) + success := call(21000, 0x5a494c53, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) } require(success); } function _call_scilla_three_args(string memory tran_name, address from, address to, uint128 amount) private { - bytes memory encodedArgs = abi.encode(_zrc2_address, tran_name, from, to, amount); + bytes memory encodedArgs = abi.encode(_zrc2_address, tran_name, CALL_MODE, from, to, amount); uint256 argsLength = encodedArgs.length; bool success; assembly { - success := call(21000, 0x5a494c52, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) + success := call(21000, 0x5a494c53, 0, add(encodedArgs, 0x20), argsLength, 0x20, 0) } require(success); } diff --git a/tests/EvmAcceptanceTests/contracts/scilla/FungibleToken.scilla b/tests/EvmAcceptanceTests/contracts/scilla/FungibleToken.scilla index 5ef0e14613..d38f22c0da 100644 --- a/tests/EvmAcceptanceTests/contracts/scilla/FungibleToken.scilla +++ b/tests/EvmAcceptanceTests/contracts/scilla/FungibleToken.scilla @@ -165,6 +165,24 @@ transition Transfer(to: ByStr20, amount: Uint128) send msgs end +(* We expect this call to revert entire transaction *) +(* @dev: Moves an amount tokens from _sender to the recipient. Used by token_owner. *) +(* @dev: Balance of recipient will increase. Balance of _sender will decrease. *) +(* @param to: Address of the recipient whose balance is increased. *) +(* @param amount: Amount of tokens to be sent. *) +transition TransferFailed(to: ByStr20, amount: Uint128) + AuthorizedMoveIfSufficientBalance _sender to amount; + e = {_eventname : "TransferSuccess"; sender : _sender; recipient : to; amount : amount}; + event e; + (* Prevent sending to a contract address that does not support transfers of token *) + msg_to_recipient = {_tag : "RecipientAcceptTransfer"; _recipient : to; _amount : zero; + sender : _sender; recipient : to; amount : amount; _EvmCall: "foo"}; + msg_to_sender = {_tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : zero; + sender : _sender; recipient : to; amount : amount; _EvmCall: "bar"}; + msgs = two_msgs msg_to_recipient msg_to_sender; + send msgs +end + (* @dev: Move a given amount of tokens from one address to another using the allowance mechanism. The caller must be an approved_spender. *) (* @dev: Balance of recipient will increase. Balance of token_owner will decrease. *) (* @param from: Address of the token_owner whose balance is decreased. *) diff --git a/tests/EvmAcceptanceTests/helpers/SignerPool.ts b/tests/EvmAcceptanceTests/helpers/SignerPool.ts index afd8cadb18..1040fdc6f5 100644 --- a/tests/EvmAcceptanceTests/helpers/SignerPool.ts +++ b/tests/EvmAcceptanceTests/helpers/SignerPool.ts @@ -34,11 +34,11 @@ export default class SignerPool { const newSigners = Array.from({length: 10}, (v, k) => Wallet.createRandom().connect(ethers.provider)); const BatchTransferContract = await ethers.getContractFactory("BatchTransfer"); const batchTransfer = await BatchTransferContract.connect(signer).deploy({ - value: ethers.utils.parseUnits("10", "ether") + value: ethers.utils.parseUnits("100", "ether") }); await batchTransfer.deployed(); const addresses = newSigners.map((signer) => signer.address); - await batchTransfer.batchTransfer(addresses, ethers.utils.parseUnits("1", "ether")); + await batchTransfer.batchTransfer(addresses, ethers.utils.parseUnits("10", "ether")); this.signers.push(...newSigners); } diff --git a/tests/EvmAcceptanceTests/test/BasicInterop.ts b/tests/EvmAcceptanceTests/test/BasicInterop.ts index 52964db63f..60cbd48487 100755 --- a/tests/EvmAcceptanceTests/test/BasicInterop.ts +++ b/tests/EvmAcceptanceTests/test/BasicInterop.ts @@ -8,6 +8,7 @@ describe("BasicInterop", function () { // Keys used in all tests cases const addr1 = "0xB3F90B06a7Dd9a860f8722f99B17fAce5abcb259"; const addr2 = "0xc8532d4c6354D717163fAa8B7504b2b4436D20d1"; + const KEEP_ORIGIN = 0; let solidityContract: Contract; let scillaContract: ScillaContract; @@ -32,28 +33,28 @@ describe("BasicInterop", function () { describe("When call is performed from solidity to scilla contract", function () { it("It should return proper string after invoking set method with string arg", async function () { const someString = "SomeString"; - await solidityContract.callString(scillaContractAddress, "setString", someString); + await solidityContract.callString(scillaContractAddress, "setString", KEEP_ORIGIN, someString); let readString = await solidityContract.readString(scillaContractAddress, "strField"); expect(readString).to.be.equal(someString); }); it("It should return proper integer after invoking set method for simpleMap", async function () { const VAL = 1000; - await solidityContract.callSimpleMap(scillaContractAddress, "setSimpleMap", addr1, VAL); + await solidityContract.callSimpleMap(scillaContractAddress, "setSimpleMap", KEEP_ORIGIN, addr1, VAL); let readRes = await solidityContract.readSimpleMap(scillaContractAddress, "simpleMap", addr1); expect(readRes).to.be.eq(VAL); }); it("It should return proper integer after invoking set method for nestedMap", async function () { const VAL = 2000; - await solidityContract.callNestedMap(scillaContractAddress, "setNestedMap", addr1, addr2, VAL); + await solidityContract.callNestedMap(scillaContractAddress, "setNestedMap", KEEP_ORIGIN, addr1, addr2, VAL); let readRes = await solidityContract.readNestedMap(scillaContractAddress, "nestedMap", addr1, addr2); expect(readRes.toNumber()).to.be.eq(VAL); }); it("It should return proper integer after invoking set method with integer arg", async function () { const NUM = 12345; - await solidityContract.callUint(scillaContractAddress, "setUint", NUM); + await solidityContract.callUint(scillaContractAddress, "setUint", KEEP_ORIGIN, NUM); let readRes = await solidityContract.readUint(scillaContractAddress, "uintField"); expect(readRes).to.be.eq(NUM); }); diff --git a/tests/EvmAcceptanceTests/test/ERC20isZRC2.ts b/tests/EvmAcceptanceTests/test/ERC20isZRC2.ts index 1bef049a95..66a2ea385b 100644 --- a/tests/EvmAcceptanceTests/test/ERC20isZRC2.ts +++ b/tests/EvmAcceptanceTests/test/ERC20isZRC2.ts @@ -81,4 +81,17 @@ describe("ERC20 Is ZRC2", function () { const bobTokens = await erc20_contract.balanceOf(await bob.getAddress()); expect(bobTokens).to.be.eq(50); }); + + it("Should be able to transfer to evm contract", async function() { + + expect(await erc20_contract.connect(contractOwner).transfer(erc20_contract.address, 150)).not.to.be.reverted; + const zrc2Tokens = await erc20_contract.balanceOf(erc20_contract.address); + expect(zrc2Tokens).to.be.eq(150); + }); + + it("Should not be able to transfer to evm contract when _EvmCall tag is present", async function() { + expect(erc20_contract.connect(contractOwner).transferFailed(erc20_contract.address, 150)).to.be.reverted; + const zrc2Tokens = await erc20_contract.balanceOf(erc20_contract.address); + expect(zrc2Tokens).to.be.eq(150); + }); });