diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index 52ffba4ceee4..59b9127d0280 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -1406,7 +1406,7 @@ inline constexpr char kJupiterReferralProgramHeader[] = "referral_ata"; inline constexpr char kBlowfishBaseAPIURL[] = "https://blowfish.wallet.brave.com"; inline constexpr char kBlowfishAPIVersionHeader[] = "X-Api-Version"; -inline constexpr char kBlowfishAPIVersion[] = "2023-03-08"; +inline constexpr char kBlowfishAPIVersion[] = "2023-06-05"; constexpr int64_t kBlockTrackerDefaultTimeInSeconds = 20; constexpr int64_t kLogTrackerDefaultTimeInSeconds = 20; diff --git a/components/brave_wallet/browser/simulation_request_helper.cc b/components/brave_wallet/browser/simulation_request_helper.cc index 7c37fa360162..526497ac465f 100644 --- a/components/brave_wallet/browser/simulation_request_helper.cc +++ b/components/brave_wallet/browser/simulation_request_helper.cc @@ -42,7 +42,7 @@ base::Value::Dict GetMetadata(const mojom::OriginInfoPtr& origin_info) { namespace evm { -std::optional EncodeScanTransactionParams( +std::optional> EncodeScanTransactionParams( const mojom::TransactionInfoPtr& tx_info) { if (!tx_info || !tx_info->from_address) { return std::nullopt; @@ -101,11 +101,19 @@ std::optional EncodeScanTransactionParams( } base::Value::Dict params; - params.Set("txObject", std::move(tx_object)); + + base::Value::List tx_objects; + tx_objects.Append(std::move(tx_object)); + params.Set("txObjects", std::move(tx_objects)); params.Set("metadata", GetMetadata(tx_info->origin_info)); params.Set("userAccount", *tx_info->from_address); - return GetJSON(base::Value(std::move(params))); + if (auto* user_account = params.FindString("userAccount")) { + return std::make_pair(GetJSON(base::Value(std::move(params))), + *user_account); + } + + return std::nullopt; } } // namespace evm @@ -153,7 +161,7 @@ std::optional GetBase64TransactionFromTxDataUnion( } // namespace -std::optional EncodeScanTransactionParams( +std::optional> EncodeScanTransactionParams( const mojom::SolanaTransactionRequestUnionPtr& request) { if (!request) { return std::nullopt; @@ -217,7 +225,12 @@ std::optional EncodeScanTransactionParams( return std::nullopt; } - return GetJSON(base::Value(std::move(params))); + if (auto* user_account = params.FindString("userAccount")) { + return std::make_pair(GetJSON(base::Value(std::move(params))), + *user_account); + } + + return std::nullopt; } } // namespace solana diff --git a/components/brave_wallet/browser/simulation_request_helper.h b/components/brave_wallet/browser/simulation_request_helper.h index af9c09b31e84..09096f936490 100644 --- a/components/brave_wallet/browser/simulation_request_helper.h +++ b/components/brave_wallet/browser/simulation_request_helper.h @@ -8,6 +8,7 @@ #include #include +#include #include "brave/components/brave_wallet/common/brave_wallet.mojom.h" @@ -15,14 +16,14 @@ namespace brave_wallet { namespace evm { -std::optional EncodeScanTransactionParams( +std::optional> EncodeScanTransactionParams( const mojom::TransactionInfoPtr& tx_info); } // namespace evm namespace solana { -std::optional EncodeScanTransactionParams( +std::optional> EncodeScanTransactionParams( const mojom::SolanaTransactionRequestUnionPtr& request); } // namespace solana diff --git a/components/brave_wallet/browser/simulation_request_helper_unittest.cc b/components/brave_wallet/browser/simulation_request_helper_unittest.cc index ac63caf827da..0171552334e8 100644 --- a/components/brave_wallet/browser/simulation_request_helper_unittest.cc +++ b/components/brave_wallet/browser/simulation_request_helper_unittest.cc @@ -152,88 +152,100 @@ TEST(SimulationRequestHelperUnitTest, // OK: Params for legacy (type-0) EVM native asset transfer is encoded // correctly. auto tx_info = GetCannedScanEVMTransactionParams(false, true, std::nullopt); - auto encoded_params = evm::EncodeScanTransactionParams(tx_info); + auto params = evm::EncodeScanTransactionParams(tx_info); std::string expected_params(R"( { "metadata":{ "origin":"https://brave.com" }, - "txObject":{ - "data":"0x", - "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", - "to":"0x3535353535353535353535353535353535353535", - "value":"0xde0b6b3a7640000" - }, + "txObjects":[ + { + "data":"0x", + "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", + "to":"0x3535353535353535353535353535353535353535", + "value":"0xde0b6b3a7640000" + } + ], "userAccount":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4" } )"); - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4"); // OK: Params for legacy (type-0) EVM contract interaction is encoded // properly. tx_info = GetCannedScanEVMTransactionParams(false, false, std::nullopt); - encoded_params = evm::EncodeScanTransactionParams(tx_info); + params = evm::EncodeScanTransactionParams(tx_info); expected_params = R"( { "metadata":{ "origin":"https://brave.com" }, - "txObject":{ - "data":"0x0a", - "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", - "to":"0x3535353535353535353535353535353535353535", - "value":"0xde0b6b3a7640000" - }, + "txObjects":[ + { + "data":"0x0a", + "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", + "to":"0x3535353535353535353535353535353535353535", + "value":"0xde0b6b3a7640000" + } + ], "userAccount":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4" } )"; - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4"); } TEST(SimulationRequestHelperUnitTest, EncodeScanTransactionParamsEVMType2DefaultOrigin) { // OK: Params for EIP-1559 (type-2) EVM ETH transfer is encoded correctly. auto tx_info = GetCannedScanEVMTransactionParams(true, true, std::nullopt); - auto encoded_params = evm::EncodeScanTransactionParams(tx_info); + auto params = evm::EncodeScanTransactionParams(tx_info); std::string expected_params(R"( { "metadata":{ "origin":"https://brave.com" }, - "txObject":{ - "data":"0x", - "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", - "to":"0x3535353535353535353535353535353535353535", - "value":"0xde0b6b3a7640000" - }, + "txObjects":[ + { + "data":"0x", + "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", + "to":"0x3535353535353535353535353535353535353535", + "value":"0xde0b6b3a7640000" + } + ], "userAccount":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4" } )"); - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4"); // OK: Params for EIP-1559 (type-2) EVM contract interaction is encoded // correctly. tx_info = GetCannedScanEVMTransactionParams(true, false, std::nullopt); - encoded_params = evm::EncodeScanTransactionParams(tx_info); + params = evm::EncodeScanTransactionParams(tx_info); expected_params = R"( { "metadata":{ "origin":"https://brave.com" }, - "txObject":{ - "data":"0x0a", - "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", - "to":"0x3535353535353535353535353535353535353535", - "value":"0xde0b6b3a7640000" - }, + "txObjects":[ + { + "data":"0x0a", + "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", + "to":"0x3535353535353535353535353535353535353535", + "value":"0xde0b6b3a7640000" + } + ], "userAccount":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4" } )"; - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4"); } TEST(SimulationRequestHelperUnitTest, @@ -242,45 +254,51 @@ TEST(SimulationRequestHelperUnitTest, // correctly. auto tx_info = GetCannedScanEVMTransactionParams(false, true, "https://example.com"); - auto encoded_params = evm::EncodeScanTransactionParams(tx_info); + auto params = evm::EncodeScanTransactionParams(tx_info); std::string expected_params(R"( { "metadata":{ "origin":"https://example.com" }, - "txObject":{ - "data":"0x", - "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", - "to":"0x3535353535353535353535353535353535353535", - "value":"0xde0b6b3a7640000" - }, + "txObjects":[ + { + "data":"0x", + "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", + "to":"0x3535353535353535353535353535353535353535", + "value":"0xde0b6b3a7640000" + } + ], "userAccount":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4" } )"); - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4"); // OK: Custom origin for contract interaction is encoded in params metadata // correctly. tx_info = GetCannedScanEVMTransactionParams(false, false, "https://example.com"); - encoded_params = evm::EncodeScanTransactionParams(tx_info); + params = evm::EncodeScanTransactionParams(tx_info); expected_params = R"( { "metadata":{ "origin":"https://example.com" }, - "txObject":{ - "data":"0x0a", - "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", - "to":"0x3535353535353535353535353535353535353535", - "value":"0xde0b6b3a7640000" - }, + "txObjects":[ + { + "data":"0x0a", + "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", + "to":"0x3535353535353535353535353535353535353535", + "value":"0xde0b6b3a7640000" + } + ], "userAccount":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4" } )"; - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4"); } TEST(SimulationRequestHelperUnitTest, @@ -289,54 +307,60 @@ TEST(SimulationRequestHelperUnitTest, // correctly. auto tx_info = GetCannedScanEVMTransactionParams(true, true, "https://example.com"); - auto encoded_params = evm::EncodeScanTransactionParams(tx_info); + auto params = evm::EncodeScanTransactionParams(tx_info); std::string expected_params(R"( { "metadata":{ "origin":"https://example.com" }, - "txObject":{ - "data":"0x", - "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", - "to":"0x3535353535353535353535353535353535353535", - "value":"0xde0b6b3a7640000" - }, + "txObjects":[ + { + "data":"0x", + "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", + "to":"0x3535353535353535353535353535353535353535", + "value":"0xde0b6b3a7640000" + } + ], "userAccount":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4" } )"); - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4"); // OK: Custom origin for contract interaction is encoded in params metadata // correctly. tx_info = GetCannedScanEVMTransactionParams(true, false, "https://example.com"); - encoded_params = evm::EncodeScanTransactionParams(tx_info); + params = evm::EncodeScanTransactionParams(tx_info); expected_params = R"( { "metadata":{ "origin":"https://example.com" }, - "txObject":{ - "data":"0x0a", - "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", - "to":"0x3535353535353535353535353535353535353535", - "value":"0xde0b6b3a7640000" - }, + "txObjects":[ + { + "data":"0x0a", + "from":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", + "to":"0x3535353535353535353535353535353535353535", + "value":"0xde0b6b3a7640000" + } + ], "userAccount":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4" } )"; - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4"); } TEST(SimulationRequestHelperUnitTest, EncodeScanTransactionParamsEVMInvalidTxData) { auto tx_info = GetCannedScanSolanaTransactionParams(std::nullopt); - auto encoded_params = evm::EncodeScanTransactionParams(tx_info); + auto params = evm::EncodeScanTransactionParams(tx_info); // KO: Invalid tx data is not encoded. - EXPECT_FALSE(encoded_params); + EXPECT_FALSE(params); } TEST(SimulationRequestHelperUnitTest, @@ -349,7 +373,7 @@ TEST(SimulationRequestHelperUnitTest, auto tx_info = GetCannedScanSolanaTransactionParams(std::nullopt); auto request = mojom::SolanaTransactionRequestUnion::NewTransactionInfo( std::move(tx_info)); - auto encoded_params = solana::EncodeScanTransactionParams(request); + auto params = solana::EncodeScanTransactionParams(request); std::string expected_params(R"( { @@ -364,8 +388,9 @@ TEST(SimulationRequestHelperUnitTest, )"); // OK: Params for Solana TransactionInfo is encoded correctly. - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8"); } TEST(SimulationRequestHelperUnitTest, @@ -373,7 +398,7 @@ TEST(SimulationRequestHelperUnitTest, auto tx_info = GetCannedScanSolanaTransactionParams("https://example.com"); auto request = mojom::SolanaTransactionRequestUnion::NewTransactionInfo( std::move(tx_info)); - auto encoded_params = solana::EncodeScanTransactionParams(request); + auto params = solana::EncodeScanTransactionParams(request); std::string expected_params(R"( { @@ -388,8 +413,9 @@ TEST(SimulationRequestHelperUnitTest, )"); // OK: Params for Solana TransactionInfo is encoded correctly. - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8"); } TEST(SimulationRequestHelperUnitTest, @@ -400,7 +426,7 @@ TEST(SimulationRequestHelperUnitTest, auto request = mojom::SolanaTransactionRequestUnion::NewSignTransactionRequest( std::move(parsed)); - auto encoded_params = solana::EncodeScanTransactionParams(request); + auto params = solana::EncodeScanTransactionParams(request); std::string expected_params(R"( { @@ -415,8 +441,9 @@ TEST(SimulationRequestHelperUnitTest, )"); // OK: Params for Solana TransactionInfo is encoded correctly. - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8"); } TEST(SimulationRequestHelperUnitTest, @@ -427,7 +454,7 @@ TEST(SimulationRequestHelperUnitTest, auto request = mojom::SolanaTransactionRequestUnion::NewSignTransactionRequest( std::move(parsed)); - auto encoded_params = solana::EncodeScanTransactionParams(request); + auto params = solana::EncodeScanTransactionParams(request); std::string expected_params(R"( { @@ -442,8 +469,9 @@ TEST(SimulationRequestHelperUnitTest, )"); // OK: Params for Solana TransactionInfo is encoded correctly. - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8"); } TEST(SimulationRequestHelperUnitTest, @@ -454,7 +482,7 @@ TEST(SimulationRequestHelperUnitTest, auto request = mojom::SolanaTransactionRequestUnion::NewSignAllTransactionsRequest( std::move(parsed)); - auto encoded_params = solana::EncodeScanTransactionParams(request); + auto params = solana::EncodeScanTransactionParams(request); std::string expected_params(R"( { @@ -469,8 +497,9 @@ TEST(SimulationRequestHelperUnitTest, )"); // OK: Params for Solana TransactionInfo is encoded correctly. - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8"); } TEST(SimulationRequestHelperUnitTest, @@ -481,7 +510,7 @@ TEST(SimulationRequestHelperUnitTest, auto request = mojom::SolanaTransactionRequestUnion::NewSignAllTransactionsRequest( std::move(parsed)); - auto encoded_params = solana::EncodeScanTransactionParams(request); + auto params = solana::EncodeScanTransactionParams(request); std::string expected_params(R"( { @@ -496,8 +525,9 @@ TEST(SimulationRequestHelperUnitTest, )"); // OK: Params for Solana TransactionInfo is encoded correctly. - ASSERT_TRUE(encoded_params); - EXPECT_EQ(*encoded_params, GetJSON(ParseJson(expected_params))); + ASSERT_TRUE(params); + EXPECT_EQ(params->first, GetJSON(ParseJson(expected_params))); + EXPECT_EQ(params->second, "BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8"); } TEST(SimulationRequestHelperUnitTest, @@ -505,10 +535,10 @@ TEST(SimulationRequestHelperUnitTest, auto tx_info = GetCannedScanEVMTransactionParams(false, true, std::nullopt); auto request = mojom::SolanaTransactionRequestUnion::NewTransactionInfo( std::move(tx_info)); - auto encoded_params = solana::EncodeScanTransactionParams(request); + auto params = solana::EncodeScanTransactionParams(request); // KO: Invalid tx data is not encoded. - EXPECT_FALSE(encoded_params); + EXPECT_FALSE(params); } TEST(SimulationRequestHelperUnitTest, diff --git a/components/brave_wallet/browser/simulation_response_parser.cc b/components/brave_wallet/browser/simulation_response_parser.cc index 1d769a45ce9b..33d65c3b2188 100644 --- a/components/brave_wallet/browser/simulation_response_parser.cc +++ b/components/brave_wallet/browser/simulation_response_parser.cc @@ -56,30 +56,18 @@ mojom::BlowfishWarningKind ParseWarningKind( case simulation_responses::WarningKind::kDanglingApproval: return mojom::BlowfishWarningKind::kDanglingApproval; - case simulation_responses::WarningKind::kDevtoolsDisabled: - return mojom::BlowfishWarningKind::kDevtoolsDisabled; - case simulation_responses::WarningKind::kEthSignTxHash: return mojom::BlowfishWarningKind::kEthSignTxHash; case simulation_responses::WarningKind::kKnownMalicious: return mojom::BlowfishWarningKind::kKnownMalicious; - case simulation_responses::WarningKind::kMainnetReplayPossible: - return mojom::BlowfishWarningKind::kMainnetReplayPossible; - case simulation_responses::WarningKind::kMultiCopyCatDomain: return mojom::BlowfishWarningKind::kMultiCopyCatDomain; case simulation_responses::WarningKind::kNewDomain: return mojom::BlowfishWarningKind::kNewDomain; - case simulation_responses::WarningKind::kNonAsciiUrl: - return mojom::BlowfishWarningKind::kNonAsciiUrl; - - case simulation_responses::WarningKind::kObfuscatedCode: - return mojom::BlowfishWarningKind::kObfuscatedCode; - case simulation_responses::WarningKind::kPermitNoExpiration: return mojom::BlowfishWarningKind::kPermitNoExpiration; @@ -147,85 +135,75 @@ mojom::BlowfishAssetPriceSource ParseAssetPriceSource( } } -mojom::BlowfishEVMRawInfoKind ParseEVMRawInfoKind( - const simulation_responses::EVMRawInfoKind& kind) { - switch (kind) { - case simulation_responses::EVMRawInfoKind::kAnyNftFromCollectionTransfer: - return mojom::BlowfishEVMRawInfoKind::kAnyNftFromCollectionTransfer; - - case simulation_responses::EVMRawInfoKind::kErc1155ApprovalForAll: - return mojom::BlowfishEVMRawInfoKind::kErc1155ApprovalForAll; - - case simulation_responses::EVMRawInfoKind::kErc1155Transfer: - return mojom::BlowfishEVMRawInfoKind::kErc1155Transfer; - - case simulation_responses::EVMRawInfoKind::kErc20Approval: - return mojom::BlowfishEVMRawInfoKind::kErc20Approval; - - case simulation_responses::EVMRawInfoKind::kErc20Transfer: - return mojom::BlowfishEVMRawInfoKind::kErc20Transfer; - - case simulation_responses::EVMRawInfoKind::kErc721Approval: - return mojom::BlowfishEVMRawInfoKind::kErc721Approval; - - case simulation_responses::EVMRawInfoKind::kErc721ApprovalForAll: - return mojom::BlowfishEVMRawInfoKind::kErc721ApprovalForAll; +mojom::BlowfishPricePtr ParsePrice(const base::Value& value) { + if (value.is_dict()) { + const auto& price_value = + simulation_responses::Price::FromValue(value.GetDict()); + if (!price_value) { + return nullptr; + } - case simulation_responses::EVMRawInfoKind::kErc721Transfer: - return mojom::BlowfishEVMRawInfoKind::kErc721Transfer; + return mojom::BlowfishPrice::New(ParseAssetPriceSource(price_value->source), + price_value->updated_at, + price_value->dollar_value_per_token); + } - case simulation_responses::EVMRawInfoKind::kNativeAssetTransfer: - return mojom::BlowfishEVMRawInfoKind::kNativeAssetTransfer; + return nullptr; +} - default: - return mojom::BlowfishEVMRawInfoKind::kUnknown; +mojom::BlowfishSuggestedAction ParseBlowfishActionKind( + const std::string& action) { + if (action == "BLOCK") { + return mojom::BlowfishSuggestedAction::kBlock; + } + if (action == "WARN") { + return mojom::BlowfishSuggestedAction::kWarn; + } + if (action == "NONE") { + return mojom::BlowfishSuggestedAction::kNone; } + return mojom::BlowfishSuggestedAction::kNone; } -mojom::BlowfishSolanaRawInfoKind ParseSolanaRawInfoKind( - const simulation_responses::SolanaRawInfoKind& kind) { - switch (kind) { - case simulation_responses::SolanaRawInfoKind::kSolStakeAuthorityChange: - return mojom::BlowfishSolanaRawInfoKind::kSolStakeAuthorityChange; - - case simulation_responses::SolanaRawInfoKind::kSolTransfer: - return mojom::BlowfishSolanaRawInfoKind::kSolTransfer; - - case simulation_responses::SolanaRawInfoKind::kSplApproval: - return mojom::BlowfishSolanaRawInfoKind::kSplApproval; - - case simulation_responses::SolanaRawInfoKind::kSplTransfer: - return mojom::BlowfishSolanaRawInfoKind::kSplTransfer; +std::vector ParseWarnings( + const std::vector& values) { + std::vector warnings; + for (const auto& warning : values) { + warnings.push_back(mojom::BlowfishWarning::New( + ParseWarningSeverity(warning.severity), ParseWarningKind(warning.kind), + warning.message)); + } + return warnings; +} - case simulation_responses::SolanaRawInfoKind::kUserAccountOwnerChange: - return mojom::BlowfishSolanaRawInfoKind::kUserAccountOwnerChange; +std::optional ParseNullableString(const base::Value& value) { + if (value.is_none()) { + return ""; + } - default: - return mojom::BlowfishSolanaRawInfoKind::kUnknown; + if (value.is_string()) { + return value.GetString(); } + + return std::nullopt; } -mojom::BlowfishMetaplexTokenStandardKind ParseMetaplexTokenStandardKind( - const simulation_responses::MetaplexTokenStandardKind& kind) { - switch (kind) { - case simulation_responses::MetaplexTokenStandardKind::kFungible: - return mojom::BlowfishMetaplexTokenStandardKind::kFungible; +std::optional ParseOptionalNullableString( + const std::optional& value) { + if (!value.has_value()) { + return ""; + } - case simulation_responses::MetaplexTokenStandardKind::kFungibleAsset: - return mojom::BlowfishMetaplexTokenStandardKind::kFungibleAsset; + return ParseNullableString(value.value()); +} - case simulation_responses::MetaplexTokenStandardKind::kNonFungible: - return mojom::BlowfishMetaplexTokenStandardKind::kNonFungible; +} // namespace - case simulation_responses::MetaplexTokenStandardKind::kNonFungibleEdition: - return mojom::BlowfishMetaplexTokenStandardKind::kNonFungibleEdition; +namespace evm { - default: - return mojom::BlowfishMetaplexTokenStandardKind::kUnknown; - } -} +namespace { -mojom::BlowfishEVMErrorKind ParseEVMErrorKind( +mojom::BlowfishEVMErrorKind ParseErrorKind( const simulation_responses::EVMErrorKind& kind) { switch (kind) { case simulation_responses::EVMErrorKind::kSimulationFailed: @@ -243,7 +221,7 @@ mojom::BlowfishEVMErrorKind ParseEVMErrorKind( } } -mojom::BlowfishEVMAddressKind ParseBlowfishEVMAddressKind( +mojom::BlowfishEVMAddressKind ParseBlowfishAddressKind( const simulation_responses::EVMAddressKind& kind) { switch (kind) { case simulation_responses::EVMAddressKind::kAccount: @@ -254,65 +232,59 @@ mojom::BlowfishEVMAddressKind ParseBlowfishEVMAddressKind( } } -mojom::BlowfishSuggestedAction ParseBlowfishActionKind( - const std::string& action) { - if (action == "BLOCK") { - return mojom::BlowfishSuggestedAction::kBlock; - } - if (action == "WARN") { - return mojom::BlowfishSuggestedAction::kWarn; - } - if (action == "NONE") { - return mojom::BlowfishSuggestedAction::kNone; - } - return mojom::BlowfishSuggestedAction::kNone; -} +mojom::BlowfishEVMRawInfoKind ParseRawInfoKind( + const simulation_responses::EVMRawInfoKind& kind) { + switch (kind) { + case simulation_responses::EVMRawInfoKind::kAnyNftFromCollectionTransfer: + return mojom::BlowfishEVMRawInfoKind::kAnyNftFromCollectionTransfer; -std::vector ParseWarnings( - const std::vector& values) { - std::vector warnings; - for (const auto& warning : values) { - warnings.push_back(mojom::BlowfishWarning::New( - ParseWarningSeverity(warning.severity), ParseWarningKind(warning.kind), - warning.message)); - } - return warnings; -} + case simulation_responses::EVMRawInfoKind::kErc1155ApprovalForAll: + return mojom::BlowfishEVMRawInfoKind::kErc1155ApprovalForAll; -std::optional ParseNullableString(const base::Value& value) { - if (value.is_string()) { - return value.GetString(); - } + case simulation_responses::EVMRawInfoKind::kErc1155Transfer: + return mojom::BlowfishEVMRawInfoKind::kErc1155Transfer; - return std::nullopt; -} + case simulation_responses::EVMRawInfoKind::kErc20Approval: + return mojom::BlowfishEVMRawInfoKind::kErc20Approval; -} // namespace + case simulation_responses::EVMRawInfoKind::kErc20Transfer: + return mojom::BlowfishEVMRawInfoKind::kErc20Transfer; -namespace evm { + case simulation_responses::EVMRawInfoKind::kErc721Approval: + return mojom::BlowfishEVMRawInfoKind::kErc721Approval; -namespace { + case simulation_responses::EVMRawInfoKind::kErc721ApprovalForAll: + return mojom::BlowfishEVMRawInfoKind::kErc721ApprovalForAll; -mojom::BlowfishPricePtr ParsePrice(const base::Value& value) { - if (value.is_dict()) { - const auto& price_value = - simulation_responses::Price::FromValue(value.GetDict()); - if (!price_value) { - return nullptr; - } + case simulation_responses::EVMRawInfoKind::kErc721Transfer: + return mojom::BlowfishEVMRawInfoKind::kErc721Transfer; - return mojom::BlowfishPrice::New(ParseAssetPriceSource(price_value->source), - price_value->updated_at, - price_value->dollar_value_per_token); + case simulation_responses::EVMRawInfoKind::kNativeAssetTransfer: + return mojom::BlowfishEVMRawInfoKind::kNativeAssetTransfer; + + default: + return mojom::BlowfishEVMRawInfoKind::kUnknown; } +} - return nullptr; +mojom::BlowfishEVMCounterpartyPtr ParseCounterparty( + const simulation_responses::EVMCounterparty& value) { + return mojom::BlowfishEVMCounterparty::New( + value.address, ParseBlowfishAddressKind(value.kind)); } -mojom::BlowfishEVMContractPtr ParseContract( - const simulation_responses::EVMContract& value) { - return mojom::BlowfishEVMContract::New( - value.address, ParseBlowfishEVMAddressKind(value.kind)); +mojom::BlowfishEVMCounterpartyPtr ParseCounterparty(const base::Value& value) { + if (!value.is_dict()) { + return nullptr; + } + + const auto& counterparty_value = + simulation_responses::EVMCounterparty::FromValue(value.GetDict()); + if (!counterparty_value) { + return nullptr; + } + + return ParseCounterparty(counterparty_value.value()); } mojom::BlowfishEVMAmountPtr ParseAmount( @@ -324,22 +296,32 @@ mojom::BlowfishEVMAssetPtr ParseAsset( const simulation_responses::EVMAsset& value) { auto asset = mojom::BlowfishEVMAsset::New(); asset->address = value.address; - asset->symbol = value.symbol; - asset->name = value.name; - if (!base::StringToInt(value.decimals, &asset->decimals)) { + if (auto symbol = ParseOptionalNullableString(value.symbol)) { + asset->symbol = symbol.value(); + } else { return nullptr; } - asset->verified = value.verified; + if (auto name = ParseOptionalNullableString(value.name)) { + asset->name = name.value(); + } else { + return nullptr; + } - if (value.lists) { - asset->lists = *value.lists; + if (auto collection = ParseOptionalNullableString(value.collection)) { + asset->collection = collection.value(); } else { - asset->lists = {}; + return nullptr; + } + + if (!base::StringToInt(value.decimals.value_or("0"), &asset->decimals)) { + return nullptr; } - asset->image_url = ParseNullableString(value.image_url); + asset->verified = value.verified.value_or(false); + asset->lists = value.lists.value_or(std::vector{}); + asset->image_url = ParseOptionalNullableString(value.image_url).value_or(""); asset->price = ParsePrice(value.price); return asset; } @@ -351,7 +333,7 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( } auto raw_info = mojom::BlowfishEVMStateChangeRawInfo::New(); - raw_info->kind = ParseEVMRawInfoKind(value.kind); + raw_info->kind = ParseRawInfoKind(value.kind); if (value.kind == simulation_responses::EVMRawInfoKind::kErc20Transfer) { auto data_value = simulation_responses::ERC20TransferData::FromValue( @@ -361,8 +343,8 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( } auto data = mojom::BlowfishERC20TransferData::New(); - data->contract = ParseContract(data_value->contract); data->amount = ParseAmount(data_value->amount); + data->counterparty = ParseCounterparty(data_value->counterparty); if (auto asset = ParseAsset(data_value->asset)) { data->asset = std::move(asset); @@ -382,9 +364,18 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( } auto data = mojom::BlowfishERC20ApprovalData::New(); - data->contract = ParseContract(data_value->contract); - data->owner = ParseContract(data_value->owner); - data->spender = ParseContract(data_value->spender); + if (auto counterparty = ParseCounterparty(data_value->owner)) { + data->owner = std::move(counterparty); + } else { + return nullptr; + } + + if (auto counterparty = ParseCounterparty(data_value->spender)) { + data->spender = std::move(counterparty); + } else { + return nullptr; + } + data->amount = ParseAmount(data_value->amount); if (auto asset = ParseAsset(data_value->asset)) { @@ -406,7 +397,7 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( auto data = mojom::BlowfishNativeAssetTransferData::New(); data->amount = ParseAmount(data_value->amount); - data->contract = ParseContract(data_value->contract); + data->counterparty = ParseCounterparty(data_value->counterparty); if (auto asset = ParseAsset(data_value->asset)) { data->asset = std::move(asset); } else { @@ -425,13 +416,20 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( auto data = mojom::BlowfishERC721TransferData::New(); data->amount = ParseAmount(data_value->amount); - data->contract = ParseContract(data_value->contract); + data->counterparty = ParseCounterparty(data_value->counterparty); data->metadata = mojom::BlowfishEVMMetadata::New(data_value->metadata.raw_image_url); - data->name = data_value->name; - data->symbol = data_value->symbol; - data->token_id = ParseNullableString(data_value->token_id); - data->asset_price = ParsePrice(data_value->asset_price); + + if (auto asset = ParseAsset(data_value->asset)) { + data->asset = std::move(asset); + } else { + return nullptr; + } + if (auto token_id = ParseNullableString(data_value->token_id)) { + data->asset->token_id = token_id.value(); + } else { + return nullptr; + } raw_info->data = mojom::BlowfishEVMStateChangeRawInfoDataUnion::NewErc721TransferData( @@ -446,15 +444,31 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( auto data = mojom::BlowfishERC721ApprovalData::New(); data->amount = ParseAmount(data_value->amount); - data->contract = ParseContract(data_value->contract); data->metadata = mojom::BlowfishEVMMetadata::New(data_value->metadata.raw_image_url); - data->name = data_value->name; - data->owner = ParseContract(data_value->owner); - data->spender = ParseContract(data_value->spender); - data->symbol = data_value->symbol; - data->token_id = ParseNullableString(data_value->token_id); - data->asset_price = ParsePrice(data_value->asset_price); + + if (auto counterparty = ParseCounterparty(data_value->owner)) { + data->owner = std::move(counterparty); + } else { + return nullptr; + } + + if (auto counterparty = ParseCounterparty(data_value->spender)) { + data->spender = std::move(counterparty); + } else { + return nullptr; + } + + if (auto asset = ParseAsset(data_value->asset)) { + data->asset = std::move(asset); + } else { + return nullptr; + } + if (auto token_id = ParseNullableString(data_value->token_id)) { + data->asset->token_id = token_id.value(); + } else { + return nullptr; + } raw_info->data = mojom::BlowfishEVMStateChangeRawInfoDataUnion::NewErc721ApprovalData( @@ -469,12 +483,24 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( auto data = mojom::BlowfishERC721ApprovalForAllData::New(); data->amount = ParseAmount(data_value->amount); - data->contract = ParseContract(data_value->contract); - data->name = data_value->name; - data->owner = ParseContract(data_value->owner); - data->spender = ParseContract(data_value->spender); - data->symbol = data_value->symbol; - data->asset_price = ParsePrice(data_value->asset_price); + + if (auto counterparty = ParseCounterparty(data_value->owner)) { + data->owner = std::move(counterparty); + } else { + return nullptr; + } + + if (auto counterparty = ParseCounterparty(data_value->spender)) { + data->spender = std::move(counterparty); + } else { + return nullptr; + } + + if (auto asset = ParseAsset(data_value->asset)) { + data->asset = std::move(asset); + } else { + return nullptr; + } raw_info->data = mojom::BlowfishEVMStateChangeRawInfoDataUnion:: NewErc721ApprovalForAllData(std::move(data)); @@ -488,12 +514,20 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( auto data = mojom::BlowfishERC1155TransferData::New(); data->amount = ParseAmount(data_value->amount); - data->contract = ParseContract(data_value->contract); data->metadata = mojom::BlowfishEVMMetadata::New(data_value->metadata.raw_image_url); - data->token_id = ParseNullableString(data_value->token_id); - data->asset_price = ParsePrice(data_value->asset_price); - data->name = data_value->name; + data->counterparty = ParseCounterparty(data_value->counterparty); + + if (auto asset = ParseAsset(data_value->asset)) { + data->asset = std::move(asset); + } else { + return nullptr; + } + if (auto token_id = ParseNullableString(data_value->token_id)) { + data->asset->token_id = token_id.value(); + } else { + return nullptr; + } raw_info->data = mojom::BlowfishEVMStateChangeRawInfoDataUnion::NewErc1155TransferData( @@ -509,10 +543,24 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( auto data = mojom::BlowfishERC1155ApprovalForAllData::New(); data->amount = ParseAmount(data_value->amount); - data->contract = ParseContract(data_value->contract); - data->owner = ParseContract(data_value->owner); - data->spender = ParseContract(data_value->spender); - data->asset_price = ParsePrice(data_value->asset_price); + + if (auto counterparty = ParseCounterparty(data_value->owner)) { + data->owner = std::move(counterparty); + } else { + return nullptr; + } + + if (auto counterparty = ParseCounterparty(data_value->spender)) { + data->spender = std::move(counterparty); + } else { + return nullptr; + } + + if (auto asset = ParseAsset(data_value->asset)) { + data->asset = std::move(asset); + } else { + return nullptr; + } raw_info->data = mojom::BlowfishEVMStateChangeRawInfoDataUnion:: NewErc1155ApprovalForAllData(std::move(data)); @@ -523,50 +571,73 @@ mojom::BlowfishEVMStateChangeRawInfoPtr ParseStateChangeRawInfo( return raw_info; } +mojom::BlowfishEVMStateChangePtr ParseStateChange(const base::Value& value) { + if (!value.is_dict()) { + return nullptr; + } + + auto state_change_value = + simulation_responses::EVMStateChange::FromValue(value.GetDict()); + if (!state_change_value) { + return nullptr; + } + + auto state_change = mojom::BlowfishEVMStateChange::New(); + state_change->human_readable_diff = state_change_value->human_readable_diff; + + if (auto raw_info = ParseStateChangeRawInfo(state_change_value->raw_info)) { + state_change->raw_info = std::move(raw_info); + } else { + return nullptr; + } + + return state_change; +} + } // namespace mojom::EVMSimulationResponsePtr ParseSimulationResponse( - const base::Value& json_value) { + const base::Value& json_value, + const std::string& user_account) { // { // "action": "NONE", // "warnings": [], // "simulationResults": { - // "error": null, - // "gas": { - // "gasLimit": null - // }, - // "expectedStateChanges": [ - // { - // "humanReadableDiff": "Send 1 ETH", - // "rawInfo": { - // "kind": "NATIVE_ASSET_TRANSFER", - // "data": { - // "amount": { - // "after": "1182957389356504134754", - // "before": "1183957389356504134754" - // }, - // "contract": { - // "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - // "kind": "ACCOUNT" - // }, - // "asset": { - // "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - // "symbol": "ETH", - // "name": "Ether", - // "decimals": 18, - // "verified": true, - // "imageUrl": - // "https://d1ts37qlq4uz4s.cloudfront.net/evm__evm%3A%3Aethereum__evm%3A%3Aethereum%3A%3Amainnet__0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png", - // "price": { - // "source": "Coingecko", - // "updatedAt": 1681958792, - // "dollarValuePerToken": 1945.92 + // "aggregated": { + // "expectedStateChanges": { + // "0x397ff1542f962076d0bfe58ea045ffa2d347aca0": [ + // { + // "humanReadableDiff": "Send 1 ETH", + // "rawInfo": { + // "kind": "NATIVE_ASSET_TRANSFER", + // "data": { + // "amount": { + // "after": "1182957389356504134754", + // "before": "1183957389356504134754" + // }, + // "counterparty": { + // "kind": "ACCOUNT", + // "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" + // }, + // "asset": { + // "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + // "symbol": "ETH", + // "name": "Ether", + // "decimals": "18", + // "verified": true, + // "imageUrl": "https://eth.png", + // "price": { + // "source": "Coingecko", + // "updatedAt": "1681958792", + // "dollarValuePerToken": "1945.92" + // } + // } // } // } // } - // } + // ] // } - // ] + // } // } // } if (!json_value.is_dict()) { @@ -586,41 +657,52 @@ mojom::EVMSimulationResponsePtr ParseSimulationResponse( simulation_response->warnings = ParseWarnings(simulation_response_value->warnings); - auto simulation_results = mojom::EVMSimulationResults::New(); - // Parse nullable field "error" of type EVMError. - if (simulation_response_value->simulation_results.error.is_dict()) { + if (simulation_response_value->simulation_results.aggregated.error + .is_dict()) { const auto& error_value = simulation_responses::EVMError::FromValue( - simulation_response_value->simulation_results.error.GetDict()); + simulation_response_value->simulation_results.aggregated.error + .GetDict()); if (!error_value) { return nullptr; } - simulation_results->error = - mojom::BlowfishEVMError::New(ParseEVMErrorKind(error_value->kind), - error_value->human_readable_error); - } else if (simulation_response_value->simulation_results.error.is_none()) { - simulation_results->error = nullptr; + simulation_response->error = mojom::BlowfishEVMError::New( + ParseErrorKind(error_value->kind), error_value->human_readable_error); + } else if (simulation_response_value->simulation_results.aggregated.error + .is_none()) { + simulation_response->error = nullptr; } else { return nullptr; } - for (const auto& state_change_value : - simulation_response_value->simulation_results.expected_state_changes) { - auto state_change = mojom::BlowfishEVMStateChange::New(); - state_change->human_readable_diff = state_change_value.human_readable_diff; + if (!simulation_response_value->simulation_results.aggregated + .expected_state_changes.is_dict()) { + return nullptr; + } - if (auto raw_info = ParseStateChangeRawInfo(state_change_value.raw_info)) { - state_change->raw_info = std::move(raw_info); - } else { + auto& expected_state_changes_value = + simulation_response_value->simulation_results.aggregated + .expected_state_changes.GetDict(); + + auto* account_state_changes_value = + expected_state_changes_value.FindList(user_account); + if (!account_state_changes_value) { + account_state_changes_value = + expected_state_changes_value.FindList(base::ToLowerASCII(user_account)); + if (!account_state_changes_value) { return nullptr; } - - simulation_results->expected_state_changes.push_back( - std::move(state_change)); } - simulation_response->simulation_results = std::move(simulation_results); + for (const auto& state_change_value : *account_state_changes_value) { + if (auto state_change = ParseStateChange(state_change_value)) { + simulation_response->expected_state_changes.push_back( + std::move(state_change)); + } else { + return nullptr; + } + } return simulation_response; } @@ -631,20 +713,53 @@ namespace solana { namespace { -mojom::BlowfishPricePtr ParsePrice(const base::Value& value) { - if (value.is_dict()) { - const auto& price_value = - simulation_responses::SolanaPrice::FromValue(value.GetDict()); - if (!price_value) { - return nullptr; - } +mojom::BlowfishMetaplexTokenStandardKind ParseMetaplexTokenStandard( + const std::optional& value) { + if (!value || value->is_none() || !value->is_string()) { + return mojom::BlowfishMetaplexTokenStandardKind::kUnknown; + } - return mojom::BlowfishPrice::New(ParseAssetPriceSource(price_value->source), - price_value->last_updated_at, - price_value->dollar_value_per_token); + const auto& kind = + simulation_responses::ParseMetaplexTokenStandardKind(value->GetString()); + switch (kind) { + case simulation_responses::MetaplexTokenStandardKind::kFungible: + return mojom::BlowfishMetaplexTokenStandardKind::kFungible; + + case simulation_responses::MetaplexTokenStandardKind::kFungibleAsset: + return mojom::BlowfishMetaplexTokenStandardKind::kFungibleAsset; + + case simulation_responses::MetaplexTokenStandardKind::kNonFungible: + return mojom::BlowfishMetaplexTokenStandardKind::kNonFungible; + + case simulation_responses::MetaplexTokenStandardKind::kNonFungibleEdition: + return mojom::BlowfishMetaplexTokenStandardKind::kNonFungibleEdition; + + default: + return mojom::BlowfishMetaplexTokenStandardKind::kUnknown; } +} - return nullptr; +mojom::BlowfishSolanaRawInfoKind ParseRawInfoKind( + const simulation_responses::SolanaRawInfoKind& kind) { + switch (kind) { + case simulation_responses::SolanaRawInfoKind::kSolStakeAuthorityChange: + return mojom::BlowfishSolanaRawInfoKind::kSolStakeAuthorityChange; + + case simulation_responses::SolanaRawInfoKind::kSolTransfer: + return mojom::BlowfishSolanaRawInfoKind::kSolTransfer; + + case simulation_responses::SolanaRawInfoKind::kSplApproval: + return mojom::BlowfishSolanaRawInfoKind::kSplApproval; + + case simulation_responses::SolanaRawInfoKind::kSplTransfer: + return mojom::BlowfishSolanaRawInfoKind::kSplTransfer; + + case simulation_responses::SolanaRawInfoKind::kUserAccountOwnerChange: + return mojom::BlowfishSolanaRawInfoKind::kUserAccountOwnerChange; + + default: + return mojom::BlowfishSolanaRawInfoKind::kUnknown; + } } mojom::BlowfishDiffSign ParseDiffSign( @@ -670,6 +785,27 @@ mojom::BlowfishSolanaDiffPtr ParseDiff( return diff; } +mojom::BlowfishSolanaAssetPtr ParseAsset( + const simulation_responses::SolanaAsset& value) { + auto asset = mojom::BlowfishSolanaAsset::New(); + + asset->symbol = value.symbol; + asset->name = value.name; + asset->mint = value.mint.value_or(""); + + if (!base::StringToInt(value.decimals, &asset->decimals)) { + return nullptr; + } + + asset->image_url = ParseOptionalNullableString(value.image_url).value_or(""); + asset->price = ParsePrice(value.price); + + asset->metaplex_token_standard = + ParseMetaplexTokenStandard(value.metaplex_token_standard); + + return asset; +} + mojom::BlowfishSolanaStakeAuthoritiesPtr ParseStakeAuthorities( const simulation_responses::SolanaStakeAuthorities& value) { auto authorities = mojom::BlowfishSolanaStakeAuthorities::New(); @@ -685,7 +821,7 @@ mojom::BlowfishSolanaStateChangeRawInfoPtr ParseStateChangeRawInfo( } auto raw_info = mojom::BlowfishSolanaStateChangeRawInfo::New(); - raw_info->kind = ParseSolanaRawInfoKind(value.kind); + raw_info->kind = ParseRawInfoKind(value.kind); if (value.kind == simulation_responses::SolanaRawInfoKind::kSolTransfer) { auto data_value = @@ -695,13 +831,18 @@ mojom::BlowfishSolanaStateChangeRawInfoPtr ParseStateChangeRawInfo( } auto data = mojom::BlowfishSOLTransferData::New(); - data->symbol = data_value->symbol; - data->name = data_value->name; - if (!base::StringToInt(data_value->decimals, &data->decimals)) { + if (auto asset = ParseAsset(data_value->asset)) { + data->asset = std::move(asset); + } else { + return nullptr; + } + + if (auto diff = ParseDiff(data_value->diff)) { + data->diff = std::move(diff); + } else { return nullptr; } - data->diff = ParseDiff(data_value->diff); raw_info->data = mojom::BlowfishSolanaStateChangeRawInfoDataUnion::NewSolTransferData( @@ -715,19 +856,21 @@ mojom::BlowfishSolanaStateChangeRawInfoPtr ParseStateChangeRawInfo( } auto data = mojom::BlowfishSPLTransferData::New(); - data->symbol = data_value->symbol; - data->name = data_value->name; - data->mint = data_value->mint; - if (!base::StringToInt(data_value->decimals, &data->decimals)) { + + if (auto asset = ParseAsset(data_value->asset)) { + data->asset = std::move(asset); + } else { return nullptr; } - data->diff = ParseDiff(data_value->diff); - if (!base::StringToUint64(data_value->supply, &data->supply)) { + + if (auto diff = ParseDiff(data_value->diff)) { + data->diff = std::move(diff); + } else { return nullptr; } - data->metaplex_token_standard = - ParseMetaplexTokenStandardKind(data_value->metaplex_token_standard); - data->asset_price = ParsePrice(data_value->asset_price); + + data->counterparty = + ParseNullableString(data_value->counterparty).value_or(""); raw_info->data = mojom::BlowfishSolanaStateChangeRawInfoDataUnion::NewSplTransferData( @@ -742,21 +885,18 @@ mojom::BlowfishSolanaStateChangeRawInfoPtr ParseStateChangeRawInfo( auto data = mojom::BlowfishSPLApprovalData::New(); data->delegate = data_value->delegate; - data->mint = data_value->mint; - data->symbol = data_value->symbol; - data->name = data_value->name; - if (!base::StringToInt(data_value->decimals, &data->decimals)) { + if (auto asset = ParseAsset(data_value->asset)) { + data->asset = std::move(asset); + } else { return nullptr; } - data->diff = ParseDiff(data_value->diff); - if (!base::StringToUint64(data_value->supply, &data->supply)) { + if (auto diff = ParseDiff(data_value->diff)) { + data->diff = std::move(diff); + } else { return nullptr; } - data->metaplex_token_standard = - ParseMetaplexTokenStandardKind(data_value->metaplex_token_standard); - data->asset_price = ParsePrice(data_value->asset_price); raw_info->data = mojom::BlowfishSolanaStateChangeRawInfoDataUnion::NewSplApprovalData( @@ -772,20 +912,21 @@ mojom::BlowfishSolanaStateChangeRawInfoPtr ParseStateChangeRawInfo( auto data = mojom::BlowfishSOLStakeAuthorityChangeData::New(); data->stake_account = data_value->stake_account; - data->curr_authorities = - ParseStakeAuthorities(data_value->curr_authorities); + data->current_authorities = + ParseStakeAuthorities(data_value->current_authorities); data->future_authorities = ParseStakeAuthorities(data_value->future_authorities); - data->symbol = data_value->symbol; - data->name = data_value->name; - if (!base::StringToInt(data_value->decimals, &data->decimals)) { + if (auto asset = ParseAsset(data_value->asset)) { + data->asset = std::move(asset); + } else { return nullptr; } if (!base::StringToUint64(data_value->sol_staked, &data->sol_staked)) { return nullptr; } + raw_info->data = mojom::BlowfishSolanaStateChangeRawInfoDataUnion:: NewSolStakeAuthorityChangeData(std::move(data)); } else { @@ -809,7 +950,7 @@ mojom::BlowfishSuggestedColor ParseSuggestedColor( // Detects documented error kinds (see: // https://docs.blowfish.xyz/v2023-03-08/reference/scan-transactions-solana) -mojom::BlowfishSolanaErrorKind ParseSolanaErrorKind(const std::string& error) { +mojom::BlowfishSolanaErrorKind ParseErrorKind(const std::string& error) { // ERROR_PROCESSING_INSTRUCTION_{0}:_{1} if (error.find("ERROR_PROCESSING_INSTRUCTION") == 0) { return mojom::BlowfishSolanaErrorKind::kErrorProcessingInstruction; @@ -1010,49 +1151,78 @@ mojom::BlowfishSolanaErrorKind ParseSolanaErrorKind(const std::string& error) { return mojom::BlowfishSolanaErrorKind::kUnknownError; } +mojom::BlowfishSolanaStateChangePtr ParseStateChange(const base::Value& value) { + if (!value.is_dict()) { + return nullptr; + } + + auto state_change_value = + simulation_responses::SolanaStateChange::FromValue(value.GetDict()); + if (!state_change_value) { + return nullptr; + } + + auto state_change = mojom::BlowfishSolanaStateChange::New(); + state_change->human_readable_diff = state_change_value->human_readable_diff; + state_change->suggested_color = + ParseSuggestedColor(state_change_value->suggested_color); + + if (auto raw_info = ParseStateChangeRawInfo(state_change_value->raw_info)) { + state_change->raw_info = std::move(raw_info); + } else { + return nullptr; + } + + return state_change; +} + } // namespace mojom::SolanaSimulationResponsePtr ParseSimulationResponse( - const base::Value& json_value) { + const base::Value& json_value, + const std::string& user_account) { // { - // "status": "CHECKS_PASSED", - // "action": "NONE", - // "warnings": [], - // "simulationResults": { - // "isRecentBlockhashExpired": false, - // "expectedStateChanges": [ + // "aggregated": { + // "action": "WARN", + // "warnings": [ // { - // "humanReadableDiff": "Send 2 USDT", - // "suggestedColor": "DEBIT", - // "rawInfo": { - // "kind": "SPL_TRANSFER", - // "data": { - // "symbol": "USDT", - // "name": "USDT", - // "mint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", - // "decimals": 6, - // "supply": 1000000000, - // "metaplexTokenStandard": "unknown", - // "assetPrice": { - // "source": "Coingecko", - // "last_updated_at": 1679331222, - // "dollar_value_per_token": 0.99 - // }, - // "diff": { - // "sign": "MINUS", - // "digits": 2000000 + // "severity": "WARNING", + // "kind": "SUSPECTED_MALICIOUS", + // "message": "Domain found on blocklists maintained by: Blowfish. + // This website is very likely to be a scam." + // } + // ], + // "error": { + // "kind": "BAD_REQUEST", + // "humanReadableError": "User account supplied in request not found" + // }, + // "expectedStateChanges": { + // "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT": [ + // { + // "humanReadableDiff": "Receive 0.05657 SOL", + // "suggestedColor": "CREDIT", + // "rawInfo": { + // "kind": "SOL_TRANSFER", + // "data": { + // "asset": { + // "symbol": "SOL", + // "name": "Solana Native Token", + // "decimals": "9", + // "price": { + // "source": "Coingecko", + // "updatedAt": "1679331222", + // "dollarValuePerToken": "0.2784" + // }, + // "imageUrl": "https://sol.png" + // }, + // "diff": { + // "sign": "PLUS", + // "digits": "500000" + // } // } // } // } - // } - // ], - // "error": null, - // "raw": { - // "err": null, - // "logs": [], - // "accounts": [], - // "returnData": null, - // "unitsConsumed": 148013 + // ] // } // } // } @@ -1070,47 +1240,51 @@ mojom::SolanaSimulationResponsePtr ParseSimulationResponse( auto simulation_response = mojom::SolanaSimulationResponse::New(); simulation_response->action = - ParseBlowfishActionKind(simulation_response_value->action); + ParseBlowfishActionKind(simulation_response_value->aggregated.action); simulation_response->warnings = - ParseWarnings(simulation_response_value->warnings); - - auto simulation_results = mojom::SolanaSimulationResults::New(); + ParseWarnings(simulation_response_value->aggregated.warnings); // Parse nullable field "error" of type SolanaError. - if (simulation_response_value->simulation_results.error.is_dict()) { + if (simulation_response_value->aggregated.error.is_dict()) { const auto& error_value = simulation_responses::SolanaError::FromValue( - simulation_response_value->simulation_results.error.GetDict()); + simulation_response_value->aggregated.error.GetDict()); if (!error_value) { return nullptr; } - simulation_results->error = - mojom::BlowfishSolanaError::New(ParseSolanaErrorKind(error_value->kind), - error_value->human_readable_error); - } else if (simulation_response_value->simulation_results.error.is_none()) { - simulation_results->error = nullptr; + simulation_response->error = mojom::BlowfishSolanaError::New( + ParseErrorKind(error_value->kind), error_value->human_readable_error); + } else if (simulation_response_value->aggregated.error.is_none()) { + simulation_response->error = nullptr; } else { return nullptr; } - for (const auto& state_change_value : - simulation_response_value->simulation_results.expected_state_changes) { - auto state_change = mojom::BlowfishSolanaStateChange::New(); - state_change->human_readable_diff = state_change_value.human_readable_diff; - state_change->suggested_color = - ParseSuggestedColor(state_change_value.suggested_color); + if (!simulation_response_value->aggregated.expected_state_changes.is_dict()) { + return nullptr; + } - if (auto raw_info = ParseStateChangeRawInfo(state_change_value.raw_info)) { - state_change->raw_info = std::move(raw_info); - } else { + auto& expected_state_changes_value = + simulation_response_value->aggregated.expected_state_changes.GetDict(); + + auto* account_state_changes_value = + expected_state_changes_value.FindList(user_account); + if (!account_state_changes_value) { + account_state_changes_value = + expected_state_changes_value.FindList(base::ToLowerASCII(user_account)); + if (!account_state_changes_value) { return nullptr; } - - simulation_results->expected_state_changes.push_back( - std::move(state_change)); } - simulation_response->simulation_results = std::move(simulation_results); + for (const auto& state_change_value : *account_state_changes_value) { + if (auto state_change = ParseStateChange(state_change_value)) { + simulation_response->expected_state_changes.push_back( + std::move(state_change)); + } else { + return nullptr; + } + } return simulation_response; } diff --git a/components/brave_wallet/browser/simulation_response_parser.h b/components/brave_wallet/browser/simulation_response_parser.h index 12524836dc62..cbc0b3918ff6 100644 --- a/components/brave_wallet/browser/simulation_response_parser.h +++ b/components/brave_wallet/browser/simulation_response_parser.h @@ -16,14 +16,16 @@ namespace brave_wallet { namespace evm { mojom::EVMSimulationResponsePtr ParseSimulationResponse( - const base::Value& json_value); + const base::Value& json_value, + const std::string& user_account); } // namespace evm namespace solana { mojom::SolanaSimulationResponsePtr ParseSimulationResponse( - const base::Value& json_value); + const base::Value& json_value, + const std::string& user_account); } // namespace solana diff --git a/components/brave_wallet/browser/simulation_response_parser_unittest.cc b/components/brave_wallet/browser/simulation_response_parser_unittest.cc index 6cfc18b9321f..f127869d45b1 100644 --- a/components/brave_wallet/browser/simulation_response_parser_unittest.cc +++ b/components/brave_wallet/browser/simulation_response_parser_unittest.cc @@ -20,234 +20,345 @@ using base::test::ParseJson; namespace brave_wallet { -// Example from https://docs.blowfish.xyz/reference/scan-transaction-evm -TEST(SimulationResponseParserUnitTest, ParseEVMSwapETHForDAI) { +// Example from https://docs.blowfish.xyz/reference/scan-transactions-evm +TEST(SimulationResponseParserUnitTest, ParseEvmSwapSynForUsdc) { std::string json(R"( { - "action": "NONE", - "simulationResults": { - "error": null, - "gas": { - "gasLimit": null - }, - "expectedStateChanges": [ - { - "humanReadableDiff": "Receive 1530.81307 DAI", - "rawInfo": { - "data": { - "amount": { - "after": "557039306766411381864245", - "before": "555508493698012633714742" - }, - "contract": { - "address": "0x6b175474e89094c44da98b954eedeac495271d0f", - "kind": "ACCOUNT" - }, - "decimals": "18", - "name": "Dai Stablecoin", - "symbol": "DAI", - "asset": { - "address": "0x6b175474e89094c44da98b954eedeac495271d0f", - "symbol": "DAI", - "name": "Dai Stablecoin", - "decimals": "18", - "verified": true, - "lists": [ - "COINGECKO", - "ZERION" - ], - "imageUrl": "https://example.com/dai.png", - "price": { - "source": "Defillama", - "updatedAt": "1680557741", - "dollarValuePerToken": "1.001" + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"NONE", + "warnings":[], + "simulationResults":{ + "aggregated":{ + "expectedStateChanges":{ + "0x06924592cdf28acd3c1d23c37875c6c6a667bdf7":[ + { + "humanReadableDiff":"Send 5523.75078 SYN", + "rawInfo":{ + "kind":"ERC20_TRANSFER", + "data":{ + "amount":{ + "before":"5523750784922050828352", + "after":"0" + }, + "counterparty":{ + "kind":"ACCOUNT", + "address":"0x4a86c01d67965f8cb3d0aaa2c655705e64097c31" + }, + "asset":{ + "address":"0x0f2d719407fdbeff09d87557abb7232601fd9f29", + "symbol":"SYN", + "name":"Synapse", + "decimals":"18", + "verified":true, + "lists":[ + "COINGECKO", + "ONE_INCH", + "UNISWAP" + ], + "imageUrl":"https://syn.png", + "price":{ + "source":"Coingecko", + "updatedAt":"1689251218", + "dollarValuePerToken":"0.645586" + } + } } } }, - "kind": "ERC20_TRANSFER" - } - }, - { - "humanReadableDiff": "Send 1 ETH", - "rawInfo": { - "data": { - "amount": { - "after": "1182957389356504134754", - "before": "1183957389356504134754" - }, - "contract": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "kind": "ACCOUNT" - }, - "asset": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "decimals": "18", - "imageUrl": "https://example.com/eth.png", - "name": "Ether", - "price": { - "dollarValuePerToken": "1968.47", - "source": "Coingecko", - "updatedAt": "1670324557" - }, - "symbol": "ETH", - "verified": true - } + { + "humanReadableDiff":"Receive 7975.46196 USDC", + "rawInfo":{ + "kind":"ERC20_TRANSFER", + "data":{ + "amount":{ + "before":"0", + "after":"7975461958" + }, + "counterparty":{ + "kind":"ACCOUNT", + "address":"0x397ff1542f962076d0bfe58ea045ffa2d347aca0" + }, + "asset":{ + "address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "symbol":"USDC", + "name":"USDCoin", + "decimals":"6", + "verified":true, + "lists":[ + "COINGECKO", + "ZERION", + "ONE_INCH", + "UNISWAP", + "MY_CRYPTO_API", + "KLEROS_TOKENS" + ], + "imageUrl":"https://usdc.png", + "price":{ + "source":"Coingecko", + "updatedAt":"1689251215", + "dollarValuePerToken":"0.999694" + } + } + } + } }, - "kind": "NATIVE_ASSET_TRANSFER" - } - } - ] - }, - "warnings": [] + { + "humanReadableDiff":"Approve to transfer any amount of your SYN", + "rawInfo":{ + "kind":"ERC20_APPROVAL", + "data":{ + "spender":{ + "kind":"ACCOUNT", + "address":"0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f" + }, + "amount":{ + "before":"115792089237316195423570985008687907853269984665640564039457584007913129639935", + "after":"115792089237316195423570985008687907853269984665640564033933833222991078811583" + }, + "owner":{ + "kind":"ACCOUNT", + "address":"0x06924592cdf28acd3c1d23c37875c6c6a667bdf7" + }, + "asset":{ + "address":"0x0f2d719407fdbeff09d87557abb7232601fd9f29", + "symbol":"SYN", + "name":"Synapse", + "decimals":"18", + "verified":true, + "lists":[ + "COINGECKO", + "ONE_INCH", + "UNISWAP" + ], + "imageUrl":"https://syn.png", + "price":{ + "source":"Coingecko", + "updatedAt":"1689251218", + "dollarValuePerToken":"0.645586" + } + } + } + } + } + ] + }, + "error":null, + "userAccount":"0x06924592cdf28acd3c1d23c37875c6c6a667bdf7" + } + } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0x06924592cdf28acd3c1d23c37875c6c6a667bdf7"); ASSERT_TRUE(simulation_response); EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kNone); EXPECT_EQ(simulation_response->warnings.size(), 0u); - EXPECT_FALSE(simulation_response->simulation_results->error); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 2u); + EXPECT_FALSE(simulation_response->error); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 3u); const auto& state_change_0 = - simulation_response->simulation_results->expected_state_changes.at(0); - EXPECT_EQ(state_change_0->human_readable_diff, "Receive 1530.81307 DAI"); + simulation_response->expected_state_changes.at(0); + + EXPECT_EQ(state_change_0->human_readable_diff, "Send 5523.75078 SYN"); EXPECT_EQ(state_change_0->raw_info->kind, mojom::BlowfishEVMRawInfoKind::kErc20Transfer); ASSERT_TRUE(state_change_0->raw_info->data->is_erc20_transfer_data()); const auto& state_change_0_raw_info = state_change_0->raw_info->data->get_erc20_transfer_data(); - EXPECT_EQ(state_change_0_raw_info->amount->before, - "555508493698012633714742"); - EXPECT_EQ(state_change_0_raw_info->amount->after, "557039306766411381864245"); - EXPECT_EQ(state_change_0_raw_info->contract->address, - "0x6b175474e89094c44da98b954eedeac495271d0f"); - EXPECT_EQ(state_change_0_raw_info->contract->kind, + EXPECT_EQ(state_change_0_raw_info->amount->before, "5523750784922050828352"); + EXPECT_EQ(state_change_0_raw_info->amount->after, "0"); + ASSERT_TRUE(state_change_0_raw_info->counterparty); + EXPECT_EQ(state_change_0_raw_info->counterparty->kind, mojom::BlowfishEVMAddressKind::kAccount); + EXPECT_EQ(state_change_0_raw_info->counterparty->address, + "0x4a86c01d67965f8cb3d0aaa2c655705e64097c31"); + ASSERT_TRUE(state_change_0_raw_info->asset); EXPECT_EQ(state_change_0_raw_info->asset->address, - "0x6b175474e89094c44da98b954eedeac495271d0f"); - EXPECT_EQ(state_change_0_raw_info->asset->symbol, "DAI"); - EXPECT_EQ(state_change_0_raw_info->asset->name, "Dai Stablecoin"); + "0x0f2d719407fdbeff09d87557abb7232601fd9f29"); + EXPECT_EQ(state_change_0_raw_info->asset->symbol, "SYN"); + EXPECT_EQ(state_change_0_raw_info->asset->name, "Synapse"); EXPECT_EQ(state_change_0_raw_info->asset->decimals, 18); EXPECT_TRUE(state_change_0_raw_info->asset->verified); - ASSERT_EQ(state_change_0_raw_info->asset->lists.size(), 2u); + ASSERT_EQ(state_change_0_raw_info->asset->lists.size(), 3u); EXPECT_EQ(state_change_0_raw_info->asset->lists.at(0), "COINGECKO"); - EXPECT_EQ(state_change_0_raw_info->asset->lists.at(1), "ZERION"); - EXPECT_EQ(state_change_0_raw_info->asset->image_url, - "https://example.com/dai.png"); + EXPECT_EQ(state_change_0_raw_info->asset->lists.at(1), "ONE_INCH"); + EXPECT_EQ(state_change_0_raw_info->asset->lists.at(2), "UNISWAP"); + EXPECT_EQ(state_change_0_raw_info->asset->image_url, "https://syn.png"); EXPECT_EQ(state_change_0_raw_info->asset->price->source, - mojom::BlowfishAssetPriceSource::kDefillama); + mojom::BlowfishAssetPriceSource::kCoingecko); EXPECT_EQ(state_change_0_raw_info->asset->price->last_updated_at, - "1680557741"); + "1689251218"); EXPECT_EQ(state_change_0_raw_info->asset->price->dollar_value_per_token, - "1.001"); + "0.645586"); const auto& state_change_1 = - simulation_response->simulation_results->expected_state_changes.at(1); - EXPECT_EQ(state_change_1->human_readable_diff, "Send 1 ETH"); + simulation_response->expected_state_changes.at(1); + EXPECT_EQ(state_change_1->human_readable_diff, "Receive 7975.46196 USDC"); EXPECT_EQ(state_change_1->raw_info->kind, - mojom::BlowfishEVMRawInfoKind::kNativeAssetTransfer); - ASSERT_TRUE(state_change_1->raw_info->data->is_native_asset_transfer_data()); + mojom::BlowfishEVMRawInfoKind::kErc20Transfer); + ASSERT_TRUE(state_change_1->raw_info->data->is_erc20_transfer_data()); const auto& state_change_1_raw_info = - state_change_1->raw_info->data->get_native_asset_transfer_data(); - EXPECT_EQ(state_change_1_raw_info->amount->before, "1183957389356504134754"); - EXPECT_EQ(state_change_1_raw_info->amount->after, "1182957389356504134754"); - EXPECT_EQ(state_change_1_raw_info->contract->address, - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - EXPECT_EQ(state_change_1_raw_info->contract->kind, + state_change_1->raw_info->data->get_erc20_transfer_data(); + EXPECT_EQ(state_change_1_raw_info->amount->before, "0"); + EXPECT_EQ(state_change_1_raw_info->amount->after, "7975461958"); + ASSERT_TRUE(state_change_1_raw_info->counterparty); + EXPECT_EQ(state_change_1_raw_info->counterparty->kind, mojom::BlowfishEVMAddressKind::kAccount); + EXPECT_EQ(state_change_1_raw_info->counterparty->address, + "0x397ff1542f962076d0bfe58ea045ffa2d347aca0"); + ASSERT_TRUE(state_change_1_raw_info->asset); EXPECT_EQ(state_change_1_raw_info->asset->address, - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - EXPECT_EQ(state_change_1_raw_info->asset->symbol, "ETH"); - EXPECT_EQ(state_change_1_raw_info->asset->name, "Ether"); - EXPECT_EQ(state_change_1_raw_info->asset->decimals, 18); + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"); + EXPECT_EQ(state_change_1_raw_info->asset->symbol, "USDC"); + EXPECT_EQ(state_change_1_raw_info->asset->name, "USDCoin"); + EXPECT_EQ(state_change_1_raw_info->asset->decimals, 6); EXPECT_TRUE(state_change_1_raw_info->asset->verified); - EXPECT_EQ(state_change_1_raw_info->asset->lists.size(), 0u); - EXPECT_EQ(state_change_1_raw_info->asset->image_url, - "https://example.com/eth.png"); + ASSERT_EQ(state_change_1_raw_info->asset->lists.size(), 6u); + EXPECT_EQ(state_change_1_raw_info->asset->lists.at(0), "COINGECKO"); + EXPECT_EQ(state_change_1_raw_info->asset->lists.at(1), "ZERION"); + EXPECT_EQ(state_change_1_raw_info->asset->lists.at(2), "ONE_INCH"); + EXPECT_EQ(state_change_1_raw_info->asset->lists.at(3), "UNISWAP"); + EXPECT_EQ(state_change_1_raw_info->asset->lists.at(4), "MY_CRYPTO_API"); + EXPECT_EQ(state_change_1_raw_info->asset->lists.at(5), "KLEROS_TOKENS"); + EXPECT_EQ(state_change_1_raw_info->asset->image_url, "https://usdc.png"); EXPECT_EQ(state_change_1_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kCoingecko); EXPECT_EQ(state_change_1_raw_info->asset->price->last_updated_at, - "1670324557"); + "1689251215"); EXPECT_EQ(state_change_1_raw_info->asset->price->dollar_value_per_token, - "1968.47"); + "0.999694"); + + const auto& state_change_2 = + simulation_response->expected_state_changes.at(2); + EXPECT_EQ(state_change_2->human_readable_diff, + "Approve to transfer any amount of your SYN"); + EXPECT_EQ(state_change_2->raw_info->kind, + mojom::BlowfishEVMRawInfoKind::kErc20Approval); + ASSERT_TRUE(state_change_2->raw_info->data->is_erc20_approval_data()); + const auto& state_change_2_raw_info = + state_change_2->raw_info->data->get_erc20_approval_data(); + EXPECT_EQ(state_change_2_raw_info->amount->before, + "115792089237316195423570985008687907853269984665640564039457584007" + "913129639935"); + EXPECT_EQ(state_change_2_raw_info->amount->after, + "115792089237316195423570985008687907853269984665640564033933833222" + "991078811583"); + ASSERT_TRUE(state_change_2_raw_info->spender); + EXPECT_EQ(state_change_2_raw_info->spender->kind, + mojom::BlowfishEVMAddressKind::kAccount); + EXPECT_EQ(state_change_2_raw_info->spender->address, + "0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f"); + ASSERT_TRUE(state_change_2_raw_info->owner); + EXPECT_EQ(state_change_2_raw_info->owner->kind, + mojom::BlowfishEVMAddressKind::kAccount); + EXPECT_EQ(state_change_2_raw_info->owner->address, + "0x06924592cdf28acd3c1d23c37875c6c6a667bdf7"); + ASSERT_TRUE(state_change_2_raw_info->asset); + EXPECT_EQ(state_change_2_raw_info->asset->address, + "0x0f2d719407fdbeff09d87557abb7232601fd9f29"); + EXPECT_EQ(state_change_2_raw_info->asset->symbol, "SYN"); + EXPECT_EQ(state_change_2_raw_info->asset->name, "Synapse"); + EXPECT_EQ(state_change_2_raw_info->asset->decimals, 18); + EXPECT_TRUE(state_change_2_raw_info->asset->verified); + ASSERT_EQ(state_change_2_raw_info->asset->lists.size(), 3u); + EXPECT_EQ(state_change_2_raw_info->asset->lists.at(0), "COINGECKO"); + EXPECT_EQ(state_change_2_raw_info->asset->lists.at(1), "ONE_INCH"); + EXPECT_EQ(state_change_2_raw_info->asset->lists.at(2), "UNISWAP"); + EXPECT_EQ(state_change_2_raw_info->asset->image_url, "https://syn.png"); + EXPECT_EQ(state_change_2_raw_info->asset->price->source, + mojom::BlowfishAssetPriceSource::kCoingecko); + EXPECT_EQ(state_change_2_raw_info->asset->price->last_updated_at, + "1689251218"); + EXPECT_EQ(state_change_2_raw_info->asset->price->dollar_value_per_token, + "0.645586"); } -// Example from https://docs.blowfish.xyz/reference/scan-transaction-evm -TEST(SimulationResponseParserUnitTest, ParseEVMERC20Approval) { +// Example from https://docs.blowfish.xyz/reference/scan-transactions-evm +TEST(SimulationResponseParserUnitTest, ParseEvmErc20ApprovalMalicious) { std::string json(R"( { - "action": "NONE", - "simulationResults": { - "error": null, - "gas": { - "gasLimit": null - }, - "expectedStateChanges": [ - { - "humanReadableDiff": "Approve to transfer up to 1000 USDT", - "rawInfo": { - "data": { - "amount": { - "after": "1000000000", - "before": "0" - }, - "asset": { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "name": "Tether USD", - "decimals": "6", - "lists": [ - "COINGECKO", - "ZERION" - ], - "symbol": "USDT", - "verified": true, - "imageUrl": "https://example.com/usdt.png", - "price": { - "source": "Defillama", - "updatedAt": "1680557741", - "dollarValuePerToken": "1.001" + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"BLOCK", + "warnings":[ + { + "kind":"KNOWN_MALICIOUS", + "message":"We believe this transaction is malicious and unsafe to sign. Approving may lead to loss of funds.", + "severity":"CRITICAL" + } + ], + "simulationResults":{ + "aggregated":{ + "error":null, + "expectedStateChanges":{ + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045":[ + { + "humanReadableDiff":"Approve to transfer up to 1000 USDT", + "rawInfo":{ + "kind":"ERC20_APPROVAL", + "data":{ + "amount":{ + "after":"1000000000", + "before":"0" + }, + "asset":{ + "address":"0xdac17f958d2ee523a2206206994597c13d831ec7", + "name":"Tether USD", + "decimals":"6", + "lists":[ + "COINGECKO", + "ZERION", + "ONE_INCH", + "UNISWAP", + "MY_CRYPTO_API", + "KLEROS_TOKENS" + ], + "symbol":"USDT", + "verified":true, + "imageUrl":"https://usdt.png", + "price":{ + "source":"Coingecko", + "updatedAt":"1679331222", + "dollarValuePerToken":"0.99" + } + }, + "owner":{ + "address":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "kind":"ACCOUNT" + }, + "spender":{ + "address":"0x1d5071048370df50839c8879cdf5144ace4b3b3b", + "kind":"ACCOUNT" + } } - }, - "contract": { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "kind": "ACCOUNT" - }, - "decimals": "6", - "name": "Tether USD", - "owner": { - "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", - "kind": "ACCOUNT" - }, - "spender": { - "address": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", - "kind": "ACCOUNT" - }, - "symbol": "USDT" - }, - "kind": "ERC20_APPROVAL" - } - } - ] - }, - "warnings": [] + } + } + ] + }, + "userAccount":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045" + } + } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); - EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kNone); - EXPECT_EQ(simulation_response->warnings.size(), 0u); - EXPECT_FALSE(simulation_response->simulation_results->error); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 1u); + EXPECT_EQ(simulation_response->action, + mojom::BlowfishSuggestedAction::kBlock); + ASSERT_EQ(simulation_response->warnings.size(), 1u); + EXPECT_EQ(simulation_response->warnings.at(0)->kind, + mojom::BlowfishWarningKind::kKnownMalicious); + EXPECT_EQ(simulation_response->warnings.at(0)->message, + "We believe this transaction is malicious and unsafe to sign. " + "Approving may lead to loss of funds."); + EXPECT_EQ(simulation_response->warnings.at(0)->severity, + mojom::BlowfishWarningSeverity::kCritical); + EXPECT_FALSE(simulation_response->error); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 1u); - const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + const auto& state_change = simulation_response->expected_state_changes.at(0); EXPECT_EQ(state_change->human_readable_diff, "Approve to transfer up to 1000 USDT"); EXPECT_EQ(state_change->raw_info->kind, @@ -257,121 +368,133 @@ TEST(SimulationResponseParserUnitTest, ParseEVMERC20Approval) { state_change->raw_info->data->get_erc20_approval_data(); EXPECT_EQ(state_change_raw_info->amount->before, "0"); EXPECT_EQ(state_change_raw_info->amount->after, "1000000000"); + ASSERT_TRUE(state_change_raw_info->spender); + EXPECT_EQ(state_change_raw_info->spender->kind, + mojom::BlowfishEVMAddressKind::kAccount); + EXPECT_EQ(state_change_raw_info->spender->address, + "0x1d5071048370df50839c8879cdf5144ace4b3b3b"); + ASSERT_TRUE(state_change_raw_info->owner); + EXPECT_EQ(state_change_raw_info->owner->kind, + mojom::BlowfishEVMAddressKind::kAccount); + EXPECT_EQ(state_change_raw_info->owner->address, + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); + ASSERT_TRUE(state_change_raw_info->asset); EXPECT_EQ(state_change_raw_info->asset->address, "0xdac17f958d2ee523a2206206994597c13d831ec7"); EXPECT_EQ(state_change_raw_info->asset->symbol, "USDT"); EXPECT_EQ(state_change_raw_info->asset->name, "Tether USD"); EXPECT_EQ(state_change_raw_info->asset->decimals, 6); EXPECT_TRUE(state_change_raw_info->asset->verified); - ASSERT_EQ(state_change_raw_info->asset->lists.size(), 2u); + ASSERT_EQ(state_change_raw_info->asset->lists.size(), 6u); EXPECT_EQ(state_change_raw_info->asset->lists.at(0), "COINGECKO"); EXPECT_EQ(state_change_raw_info->asset->lists.at(1), "ZERION"); - EXPECT_EQ(state_change_raw_info->asset->image_url, - "https://example.com/usdt.png"); + EXPECT_EQ(state_change_raw_info->asset->lists.at(2), "ONE_INCH"); + EXPECT_EQ(state_change_raw_info->asset->lists.at(3), "UNISWAP"); + EXPECT_EQ(state_change_raw_info->asset->lists.at(4), "MY_CRYPTO_API"); + EXPECT_EQ(state_change_raw_info->asset->lists.at(5), "KLEROS_TOKENS"); + EXPECT_EQ(state_change_raw_info->asset->image_url, "https://usdt.png"); EXPECT_EQ(state_change_raw_info->asset->price->source, - mojom::BlowfishAssetPriceSource::kDefillama); - EXPECT_EQ(state_change_raw_info->asset->price->last_updated_at, "1680557741"); + mojom::BlowfishAssetPriceSource::kCoingecko); + EXPECT_EQ(state_change_raw_info->asset->price->last_updated_at, "1679331222"); EXPECT_EQ(state_change_raw_info->asset->price->dollar_value_per_token, - "1.001"); - EXPECT_EQ(state_change_raw_info->contract->address, - "0xdac17f958d2ee523a2206206994597c13d831ec7"); - EXPECT_EQ(state_change_raw_info->contract->kind, - mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_raw_info->owner->address, - "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); - EXPECT_EQ(state_change_raw_info->owner->kind, - mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_raw_info->spender->address, - "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45"); - EXPECT_EQ(state_change_raw_info->spender->kind, - mojom::BlowfishEVMAddressKind::kAccount); + "0.99"); } -// Example from https://docs.blowfish.xyz/reference/scan-transaction-evm -TEST(SimulationResponseParserUnitTest, ParseEVMBuyERC721NFTWithETH) { +// Example from https://docs.blowfish.xyz/reference/scan-transactions-evm +TEST(SimulationResponseParserUnitTest, ParseEvmBuyErc721NftWithEth) { std::string json(R"( { - "action": "NONE", - "warnings": [], - "simulationResults": { - "error": null, - "gas": { - "gasLimit": null - }, - "expectedStateChanges": [ - { - "humanReadableDiff": "Receive PudgyPenguins #7238", - "rawInfo": { - "kind": "ERC721_TRANSFER", - "data": { - "amount": { - "after": "1", - "before": "0" - }, - "contract": { - "address": "0xbd3531da5cf5857e7cfaa92426877b022e612cf8", - "kind": "ACCOUNT" - }, - "metadata": { - "rawImageUrl": "https://example.com/assets/7238.png" - }, - "name": "PudgyPenguins", - "symbol": "PPG", - "tokenId": "7238", - "assetPrice": { - "source": "Simplehash", - "updatedAt": "1679331222", - "dollarValuePerToken": "594.99" + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"NONE", + "warnings":[], + "simulationResults":{ + "aggregated":{ + "expectedStateChanges":{ + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045":[ + { + "humanReadableDiff":"Receive PudgyPenguins #7238", + "rawInfo":{ + "kind":"ERC721_TRANSFER", + "data":{ + "amount":{ + "after":"1", + "before":"0" + }, + "metadata":{ + "rawImageUrl":"https://pudgy-penguins.png" + }, + "tokenId":"7238", + "asset":{ + "address":"0xbd3531da5cf5857e7cfaa92426877b022e612cf8", + "name":"PudgyPenguins", + "collection":"PudgyPenguins", + "symbol":"PPG", + "price":{ + "source":"Simplehash", + "updatedAt":"1679331222", + "dollarValuePerToken":"594.99" + } + }, + "counterparty":{ + "kind":"ACCOUNT", + "address":"0x06924592cdf28acd3c1d23c37875c6c6a667bdf7" + } + } + } + }, + { + "humanReadableDiff":"Send 3.181 ETH", + "rawInfo":{ + "kind":"NATIVE_ASSET_TRANSFER", + "data":{ + "amount":{ + "after":"998426264937289938488", + "before":"1001607264937289938488" + }, + "contract":{ + "address":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "kind":"ACCOUNT" + }, + "asset":{ + "address":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "symbol":"ETH", + "name":"Ether", + "decimals":"18", + "verified":true, + "imageUrl":"https//eth.png", + "price":{ + "source":"Coingecko", + "updatedAt":"1681958792", + "dollarValuePerToken":"1945.92" + } + }, + "counterparty":{ + "kind":"ACCOUNT", + "address":"0x06924592cdf28acd3c1d23c37875c6c6a667bdf7" + } + } } } - } + ] }, - { - "humanReadableDiff": "Send 3.181 ETH", - "rawInfo": { - "kind": "NATIVE_ASSET_TRANSFER", - "data": { - "amount": { - "after": "998426264937289938488", - "before": "1001607264937289938488" - }, - "contract": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "kind": "ACCOUNT" - }, - "asset": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "decimals": "18", - "imageUrl": "https://example.com/eth.png", - "name": "Ether", - "price": { - "dollarValuePerToken": "1968.47", - "source": "Coingecko", - "updatedAt": "1670324557" - }, - "symbol": "ETH", - "verified": true - } - } - } - } - ] + "userAccount":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "error":null + } } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kNone); EXPECT_EQ(simulation_response->warnings.size(), 0u); - EXPECT_FALSE(simulation_response->simulation_results->error); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 2u); + EXPECT_FALSE(simulation_response->error); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 2u); const auto& state_change_0 = - simulation_response->simulation_results->expected_state_changes.at(0); + simulation_response->expected_state_changes.at(0); EXPECT_EQ(state_change_0->human_readable_diff, "Receive PudgyPenguins #7238"); EXPECT_EQ(state_change_0->raw_info->kind, mojom::BlowfishEVMRawInfoKind::kErc721Transfer); @@ -380,24 +503,29 @@ TEST(SimulationResponseParserUnitTest, ParseEVMBuyERC721NFTWithETH) { state_change_0->raw_info->data->get_erc721_transfer_data(); EXPECT_EQ(state_change_0_raw_info->amount->before, "0"); EXPECT_EQ(state_change_0_raw_info->amount->after, "1"); - EXPECT_EQ(state_change_0_raw_info->contract->kind, + EXPECT_EQ(state_change_0_raw_info->metadata->raw_image_url, + "https://pudgy-penguins.png"); + ASSERT_TRUE(state_change_0_raw_info->counterparty); + EXPECT_EQ(state_change_0_raw_info->counterparty->kind, mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_0_raw_info->contract->address, + EXPECT_EQ(state_change_0_raw_info->counterparty->address, + "0x06924592cdf28acd3c1d23c37875c6c6a667bdf7"); + ASSERT_TRUE(state_change_0_raw_info->asset); + EXPECT_EQ(state_change_0_raw_info->asset->address, "0xbd3531da5cf5857e7cfaa92426877b022e612cf8"); - EXPECT_EQ(state_change_0_raw_info->metadata->raw_image_url, - "https://example.com/assets/7238.png"); - EXPECT_EQ(state_change_0_raw_info->name, "PudgyPenguins"); - EXPECT_EQ(state_change_0_raw_info->symbol, "PPG"); - EXPECT_EQ(state_change_0_raw_info->token_id, "7238"); - EXPECT_EQ(state_change_0_raw_info->asset_price->source, + EXPECT_EQ(state_change_0_raw_info->asset->symbol, "PPG"); + EXPECT_EQ(state_change_0_raw_info->asset->name, "PudgyPenguins"); + EXPECT_EQ(state_change_0_raw_info->asset->collection, "PudgyPenguins"); + EXPECT_EQ(state_change_0_raw_info->asset->token_id, "7238"); + EXPECT_EQ(state_change_0_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kSimplehash); - EXPECT_EQ(state_change_0_raw_info->asset_price->last_updated_at, + EXPECT_EQ(state_change_0_raw_info->asset->price->last_updated_at, "1679331222"); - EXPECT_EQ(state_change_0_raw_info->asset_price->dollar_value_per_token, + EXPECT_EQ(state_change_0_raw_info->asset->price->dollar_value_per_token, "594.99"); const auto& state_change_1 = - simulation_response->simulation_results->expected_state_changes.at(1); + simulation_response->expected_state_changes.at(1); EXPECT_EQ(state_change_1->human_readable_diff, "Send 3.181 ETH"); EXPECT_EQ(state_change_1->raw_info->kind, mojom::BlowfishEVMRawInfoKind::kNativeAssetTransfer); @@ -406,100 +534,102 @@ TEST(SimulationResponseParserUnitTest, ParseEVMBuyERC721NFTWithETH) { state_change_1->raw_info->data->get_native_asset_transfer_data(); EXPECT_EQ(state_change_1_raw_info->amount->before, "1001607264937289938488"); EXPECT_EQ(state_change_1_raw_info->amount->after, "998426264937289938488"); - EXPECT_EQ(state_change_1_raw_info->contract->kind, + ASSERT_TRUE(state_change_1_raw_info->counterparty); + EXPECT_EQ(state_change_1_raw_info->counterparty->kind, mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_1_raw_info->contract->address, - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + EXPECT_EQ(state_change_1_raw_info->counterparty->address, + "0x06924592cdf28acd3c1d23c37875c6c6a667bdf7"); + ASSERT_TRUE(state_change_1_raw_info->asset); EXPECT_EQ(state_change_1_raw_info->asset->address, "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - EXPECT_EQ(state_change_1_raw_info->asset->decimals, 18); - EXPECT_EQ(state_change_1_raw_info->asset->image_url, - "https://example.com/eth.png"); + EXPECT_EQ(state_change_1_raw_info->asset->symbol, "ETH"); EXPECT_EQ(state_change_1_raw_info->asset->name, "Ether"); - EXPECT_EQ(state_change_1_raw_info->asset->price->dollar_value_per_token, - "1968.47"); - EXPECT_EQ(state_change_1_raw_info->asset->price->source, - mojom::BlowfishAssetPriceSource::kCoingecko); + EXPECT_EQ(state_change_1_raw_info->asset->decimals, 18); + EXPECT_TRUE(state_change_1_raw_info->asset->verified); + ASSERT_EQ(state_change_1_raw_info->asset->lists.size(), 0u); + EXPECT_EQ(state_change_1_raw_info->asset->image_url, "https//eth.png"); EXPECT_EQ(state_change_1_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kCoingecko); EXPECT_EQ(state_change_1_raw_info->asset->price->last_updated_at, - "1670324557"); - EXPECT_EQ(state_change_1_raw_info->asset->symbol, "ETH"); - EXPECT_TRUE(state_change_1_raw_info->asset->verified); + "1681958792"); + EXPECT_EQ(state_change_1_raw_info->asset->price->dollar_value_per_token, + "1945.92"); } -// Example from https://docs.blowfish.xyz/reference/scan-transaction-evm -TEST(SimulationResponseParserUnitTest, ParseEVMERC721ApprovalForAll) { +// Example from https://docs.blowfish.xyz/reference/scan-transactions-evm +TEST(SimulationResponseParserUnitTest, ParseEvmErc721ApprovalForAll) { std::string json(R"( { - "action": "WARN", - "warnings": [ + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"WARN", + "warnings":[ { - "kind": "UNLIMITED_ALLOWANCE_TO_NFTS", - "message": "You are allowing this website to withdraw funds from your account in the future", - "severity": "WARNING" + "kind":"UNLIMITED_ALLOWANCE_TO_NFTS", + "message":"You are allowing this website to withdraw funds from your account in the future.", + "severity":"WARNING" } ], - "simulationResults": { - "error": null, - "gas": { - "gasLimit": null - }, - "expectedStateChanges": [ - { - "humanReadableDiff": "Approve to transfer all your BoredApeYachtClub", - "rawInfo": { - "kind": "ERC721_APPROVAL_FOR_ALL", - "data": { - "amount": { - "after": "115792089237316195423570985008687907853269984665640564039457584007913129639935", - "before": "0" - }, - "contract": { - "address": "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", - "kind": "ACCOUNT" - }, - "name": "BoredApeYachtClub", - "owner": { - "address": "0x38191ca1307ebf67ca1a7caf5346dbd91d882ca6", - "kind": "ACCOUNT" - }, - "spender": { - "address": "0x1e0049783f008a0085193e00003d00cd54003c71", - "kind": "ACCOUNT" - }, - "symbol": "BAYC", - "assetPrice": { - "source": "Simplehash", - "updatedAt": "1679331222", - "dollarValuePerToken": "7865.43" + "simulationResults":{ + "aggregated":{ + "expectedStateChanges":{ + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045":[ + { + "humanReadableDiff":"Approve to transfer all your BoredApeYachtClub", + "rawInfo":{ + "kind":"ERC721_APPROVAL_FOR_ALL", + "data":{ + "amount":{ + "after":"115792089237316195423570985008687907853269984665640564039457584007913129639935", + "before":"0" + }, + "owner":{ + "address":"0x38191ca1307ebf67ca1a7caf5346dbd91d882ca6", + "kind":"ACCOUNT" + }, + "spender":{ + "address":"0x1e0049783f008a0085193e00003d00cd54003c71", + "kind":"ACCOUNT" + }, + "asset":{ + "address":"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", + "name":"BoredApeYachtClub", + "collection":"BoredApeYachtClub", + "symbol":"BAYC", + "price":{ + "source":"Simplehash", + "updatedAt":"1679331222", + "dollarValuePerToken":"7865.43" + } + } + } } } - } - } - ] + ] + }, + "userAccount":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "error":null + } } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); + EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kWarn); ASSERT_EQ(simulation_response->warnings.size(), 1u); EXPECT_EQ(simulation_response->warnings.at(0)->kind, mojom::BlowfishWarningKind::kUnlimitedAllowanceToNfts); EXPECT_EQ(simulation_response->warnings.at(0)->message, "You are allowing this website to withdraw funds from your account " - "in the future"); + "in the future."); EXPECT_EQ(simulation_response->warnings.at(0)->severity, mojom::BlowfishWarningSeverity::kWarning); - EXPECT_FALSE(simulation_response->simulation_results->error); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 1u); + EXPECT_FALSE(simulation_response->error); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 1u); - const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + const auto& state_change = simulation_response->expected_state_changes.at(0); EXPECT_EQ(state_change->human_readable_diff, "Approve to transfer all your BoredApeYachtClub"); EXPECT_EQ(state_change->raw_info->kind, @@ -511,90 +641,94 @@ TEST(SimulationResponseParserUnitTest, ParseEVMERC721ApprovalForAll) { EXPECT_EQ(state_change_raw_info->amount->after, "115792089237316195423570985008687907853269984665640564039457584007" "913129639935"); - EXPECT_EQ(state_change_raw_info->contract->kind, - mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_raw_info->contract->address, - "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"); - EXPECT_EQ(state_change_raw_info->name, "BoredApeYachtClub"); + ASSERT_TRUE(state_change_raw_info->owner); EXPECT_EQ(state_change_raw_info->owner->kind, mojom::BlowfishEVMAddressKind::kAccount); EXPECT_EQ(state_change_raw_info->owner->address, "0x38191ca1307ebf67ca1a7caf5346dbd91d882ca6"); + ASSERT_TRUE(state_change_raw_info->spender); EXPECT_EQ(state_change_raw_info->spender->kind, mojom::BlowfishEVMAddressKind::kAccount); EXPECT_EQ(state_change_raw_info->spender->address, "0x1e0049783f008a0085193e00003d00cd54003c71"); - EXPECT_EQ(state_change_raw_info->symbol, "BAYC"); - EXPECT_EQ(state_change_raw_info->asset_price->source, + ASSERT_TRUE(state_change_raw_info->asset); + EXPECT_EQ(state_change_raw_info->asset->address, + "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"); + EXPECT_EQ(state_change_raw_info->asset->symbol, "BAYC"); + EXPECT_EQ(state_change_raw_info->asset->name, "BoredApeYachtClub"); + EXPECT_EQ(state_change_raw_info->asset->collection, "BoredApeYachtClub"); + EXPECT_EQ(state_change_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kSimplehash); - EXPECT_EQ(state_change_raw_info->asset_price->last_updated_at, "1679331222"); - EXPECT_EQ(state_change_raw_info->asset_price->dollar_value_per_token, + EXPECT_EQ(state_change_raw_info->asset->price->last_updated_at, "1679331222"); + EXPECT_EQ(state_change_raw_info->asset->price->dollar_value_per_token, "7865.43"); } -// Example from https://docs.blowfish.xyz/reference/scan-transaction-evm -TEST(SimulationResponseParserUnitTest, ParseEVMERC721Approval) { +// Example from https://docs.blowfish.xyz/reference/scan-transactions-evm +TEST(SimulationResponseParserUnitTest, ParseEvmErc721Approval) { std::string json(R"( { - "action": "NONE", - "warnings": [], - "simulationResults": { - "error": null, - "gas": { - "gasLimit": null - }, - "expectedStateChanges": [ - { - "humanReadableDiff": "Approve to transfer BoredApeYachtClub", - "rawInfo": { - "kind": "ERC721_APPROVAL", - "data": { - "amount": { - "after": "1", - "before": "0" - }, - "contract": { - "address": "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", - "kind": "ACCOUNT" - }, - "metadata": { - "rawImageUrl": "https://example.com/6603.png" - }, - "name": "BoredApeYachtClub", - "owner": { - "address": "0xed2ab4948ba6a909a7751dec4f34f303eb8c7236", - "kind": "ACCOUNT" - }, - "spender": { - "address": "0x1e0049783f008a0085193e00003d00cd54003c71", - "kind": "ACCOUNT" - }, - "symbol": "BAYC", - "tokenId": "6603", - "assetPrice": { - "source": "Simplehash", - "updatedAt": "1679331222", - "dollarValuePerToken": "7865.43" + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"NONE", + "warnings":[], + "simulationResults":{ + "aggregated":{ + "expectedStateChanges":{ + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045":[ + { + "humanReadableDiff":"Approve to transfer BoredApeYachtClub", + "rawInfo":{ + "kind":"ERC721_APPROVAL", + "data":{ + "amount":{ + "after":"1", + "before":"0" + }, + "metadata":{ + "rawImageUrl":"https://bayc.png" + }, + "owner":{ + "address":"0xed2ab4948ba6a909a7751dec4f34f303eb8c7236", + "kind":"ACCOUNT" + }, + "spender":{ + "address":"0x1e0049783f008a0085193e00003d00cd54003c71", + "kind":"ACCOUNT" + }, + "tokenId":"6603", + "asset":{ + "address":"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", + "name":"BoredApeYachtClub", + "collection":"BoredApeYachtClub", + "symbol":"BAYC", + "price":{ + "source":"Simplehash", + "updatedAt":"1679331222", + "dollarValuePerToken":"7865.43" + } + } + } } } - } - } - ] + ] + }, + "userAccount":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "error":null + } } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); + EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kNone); EXPECT_EQ(simulation_response->warnings.size(), 0u); - EXPECT_FALSE(simulation_response->simulation_results->error); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 1u); + EXPECT_FALSE(simulation_response->error); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 1u); - const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + const auto& state_change = simulation_response->expected_state_changes.at(0); EXPECT_EQ(state_change->human_readable_diff, "Approve to transfer BoredApeYachtClub"); EXPECT_EQ(state_change->raw_info->kind, @@ -604,113 +738,124 @@ TEST(SimulationResponseParserUnitTest, ParseEVMERC721Approval) { state_change->raw_info->data->get_erc721_approval_data(); EXPECT_EQ(state_change_raw_info->amount->before, "0"); EXPECT_EQ(state_change_raw_info->amount->after, "1"); - EXPECT_EQ(state_change_raw_info->contract->kind, - mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_raw_info->contract->address, - "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"); - EXPECT_EQ(state_change_raw_info->metadata->raw_image_url, - "https://example.com/6603.png"); - EXPECT_EQ(state_change_raw_info->name, "BoredApeYachtClub"); + EXPECT_EQ(state_change_raw_info->metadata->raw_image_url, "https://bayc.png"); + ASSERT_TRUE(state_change_raw_info->owner); EXPECT_EQ(state_change_raw_info->owner->kind, mojom::BlowfishEVMAddressKind::kAccount); EXPECT_EQ(state_change_raw_info->owner->address, "0xed2ab4948ba6a909a7751dec4f34f303eb8c7236"); + ASSERT_TRUE(state_change_raw_info->spender); EXPECT_EQ(state_change_raw_info->spender->kind, mojom::BlowfishEVMAddressKind::kAccount); EXPECT_EQ(state_change_raw_info->spender->address, "0x1e0049783f008a0085193e00003d00cd54003c71"); - EXPECT_EQ(state_change_raw_info->symbol, "BAYC"); - EXPECT_EQ(state_change_raw_info->token_id, "6603"); - EXPECT_EQ(state_change_raw_info->asset_price->source, + ASSERT_TRUE(state_change_raw_info->asset); + EXPECT_EQ(state_change_raw_info->asset->address, + "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"); + EXPECT_EQ(state_change_raw_info->asset->symbol, "BAYC"); + EXPECT_EQ(state_change_raw_info->asset->name, "BoredApeYachtClub"); + EXPECT_EQ(state_change_raw_info->asset->collection, "BoredApeYachtClub"); + EXPECT_EQ(state_change_raw_info->asset->token_id, "6603"); + EXPECT_EQ(state_change_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kSimplehash); - EXPECT_EQ(state_change_raw_info->asset_price->last_updated_at, "1679331222"); - EXPECT_EQ(state_change_raw_info->asset_price->dollar_value_per_token, + EXPECT_EQ(state_change_raw_info->asset->price->last_updated_at, "1679331222"); + EXPECT_EQ(state_change_raw_info->asset->price->dollar_value_per_token, "7865.43"); } -// Example from https://docs.blowfish.xyz/reference/scan-transaction-evm -TEST(SimulationResponseParserUnitTest, ParseEVMBuyERC1155TokenWithETH) { +// Example from https://docs.blowfish.xyz/reference/scan-transactions-evm +TEST(SimulationResponseParserUnitTest, ParseEvmBuyErc1155TokenWithEth) { std::string json(R"( { - "action": "NONE", - "simulationResults": { - "error": null, - "gas": { - "gasLimit": null - }, - "expectedStateChanges": [ - { - "humanReadableDiff": "Send 0.033 ETH", - "rawInfo": { - "kind": "NATIVE_ASSET_TRANSFER", - "data": { - "amount": { - "after": "71057321770366572", - "before": "104057321770366572" - }, - "contract": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "kind": "ACCOUNT" - }, - "asset": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "decimals": "18", - "imageUrl": "https://example.com/eth.png", - "name": "Ether", - "price": { - "dollarValuePerToken": "1968.47", - "source": "Coingecko", - "updatedAt": "1670324557" - }, - "symbol": "ETH", - "verified": true - } - } - } - }, - { - "humanReadableDiff": "Receive Corgi", - "rawInfo": { - "kind": "ERC1155_TRANSFER", - "data": { - "name": "Corgi", - "amount": { - "after": "1", - "before": "0" - }, - "contract": { - "address": "0x51e613727fdd2e0b91b51c3e5427e9440a7957e4", - "kind": "ACCOUNT" - }, - "metadata": { - "rawImageUrl": "https://example.com/13014975.png" - }, - "tokenId": "13014975", - "assetPrice": { - "source": "Simplehash", - "updatedAt": "1679331222", - "dollarValuePerToken": "232.43" + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"NONE", + "simulationResults":{ + "aggregated":{ + "error":null, + "userAccount":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "expectedStateChanges":{ + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045":[ + { + "humanReadableDiff":"Send 0.033 ETH", + "rawInfo":{ + "kind":"NATIVE_ASSET_TRANSFER", + "data":{ + "amount":{ + "after":"71057321770366572", + "before":"104057321770366572" + }, + "contract":{ + "address":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "kind":"ACCOUNT" + }, + "asset":{ + "address":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "symbol":"ETH", + "name":"Ether", + "decimals":"18", + "verified":true, + "imageUrl":"https://eth.png", + "price":{ + "source":"Coingecko", + "updatedAt":"1681958792", + "dollarValuePerToken":"1945.92" + } + }, + "counterparty":{ + "kind":"ACCOUNT", + "address":"0x06924592cdf28acd3c1d23c37875c6c6a667bdf7" + } + } + } + }, + { + "humanReadableDiff":"Receive Corgi", + "rawInfo":{ + "kind":"ERC1155_TRANSFER", + "data":{ + "amount":{ + "after":"1", + "before":"0" + }, + "metadata":{ + "rawImageUrl":"https://corgi.png" + }, + "tokenId":"13014975", + "counterparty":{ + "kind":"ACCOUNT", + "address":"0x06924592cdf28acd3c1d23c37875c6c6a667bdf7" + }, + "asset":{ + "address":"0x51e613727fdd2e0b91b51c3e5427e9440a7957e4", + "name":"Corgi", + "price":{ + "source":"Simplehash", + "updatedAt":"1679331222", + "dollarValuePerToken":"232.43" + } + } + } } } - } + ] } - ] + } }, - "warnings": [] + "warnings":[] } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); + EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kNone); EXPECT_EQ(simulation_response->warnings.size(), 0u); - EXPECT_FALSE(simulation_response->simulation_results->error); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 2u); + EXPECT_FALSE(simulation_response->error); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 2u); const auto& state_change_0 = - simulation_response->simulation_results->expected_state_changes.at(0); + simulation_response->expected_state_changes.at(0); EXPECT_EQ(state_change_0->human_readable_diff, "Send 0.033 ETH"); EXPECT_EQ(state_change_0->raw_info->kind, mojom::BlowfishEVMRawInfoKind::kNativeAssetTransfer); @@ -719,215 +864,133 @@ TEST(SimulationResponseParserUnitTest, ParseEVMBuyERC1155TokenWithETH) { state_change_0->raw_info->data->get_native_asset_transfer_data(); EXPECT_EQ(state_change_0_raw_info->amount->before, "104057321770366572"); EXPECT_EQ(state_change_0_raw_info->amount->after, "71057321770366572"); - EXPECT_EQ(state_change_0_raw_info->contract->kind, + ASSERT_TRUE(state_change_0_raw_info->counterparty); + EXPECT_EQ(state_change_0_raw_info->counterparty->kind, mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_0_raw_info->contract->address, - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + EXPECT_EQ(state_change_0_raw_info->counterparty->address, + "0x06924592cdf28acd3c1d23c37875c6c6a667bdf7"); + ASSERT_TRUE(state_change_0_raw_info->asset); EXPECT_EQ(state_change_0_raw_info->asset->address, "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - EXPECT_EQ(state_change_0_raw_info->asset->decimals, 18); - EXPECT_EQ(state_change_0_raw_info->asset->image_url, - "https://example.com/eth.png"); + EXPECT_EQ(state_change_0_raw_info->asset->symbol, "ETH"); EXPECT_EQ(state_change_0_raw_info->asset->name, "Ether"); + EXPECT_EQ(state_change_0_raw_info->asset->decimals, 18); + EXPECT_TRUE(state_change_0_raw_info->asset->verified); + ASSERT_EQ(state_change_0_raw_info->asset->lists.size(), 0u); + EXPECT_EQ(state_change_0_raw_info->asset->image_url, "https://eth.png"); EXPECT_EQ(state_change_0_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kCoingecko); - EXPECT_EQ(state_change_0_raw_info->asset->price->last_updated_at, - "1670324557"); + "1681958792"); EXPECT_EQ(state_change_0_raw_info->asset->price->dollar_value_per_token, - "1968.47"); - EXPECT_EQ(state_change_0_raw_info->asset->symbol, "ETH"); - EXPECT_TRUE(state_change_0_raw_info->asset->verified); + "1945.92"); const auto& state_change_1 = - simulation_response->simulation_results->expected_state_changes.at(1); + simulation_response->expected_state_changes.at(1); EXPECT_EQ(state_change_1->human_readable_diff, "Receive Corgi"); EXPECT_EQ(state_change_1->raw_info->kind, mojom::BlowfishEVMRawInfoKind::kErc1155Transfer); ASSERT_TRUE(state_change_1->raw_info->data->is_erc1155_transfer_data()); const auto& state_change_1_raw_info = state_change_1->raw_info->data->get_erc1155_transfer_data(); - EXPECT_EQ(state_change_1_raw_info->name, "Corgi"); EXPECT_EQ(state_change_1_raw_info->amount->before, "0"); EXPECT_EQ(state_change_1_raw_info->amount->after, "1"); - EXPECT_EQ(state_change_1_raw_info->contract->kind, - mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_1_raw_info->contract->address, - "0x51e613727fdd2e0b91b51c3e5427e9440a7957e4"); EXPECT_EQ(state_change_1_raw_info->metadata->raw_image_url, - "https://example.com/13014975.png"); - EXPECT_EQ(state_change_1_raw_info->token_id, "13014975"); - EXPECT_EQ(state_change_1_raw_info->asset_price->source, - mojom::BlowfishAssetPriceSource::kSimplehash); - EXPECT_EQ(state_change_1_raw_info->asset_price->last_updated_at, - "1679331222"); - EXPECT_EQ(state_change_1_raw_info->asset_price->dollar_value_per_token, - "232.43"); -} - -// Example from https://docs.blowfish.xyz/reference/scan-transaction-evm -TEST(SimulationResponseParserUnitTest, ParseEVMERC1155Transfer) { - std::string json(R"( - { - "action": "NONE", - "simulationResults": { - "error": null, - "gas": { - "gasLimit": null - }, - "expectedStateChanges": [ - { - "humanReadableDiff": "Receive Corgi", - "rawInfo": { - "kind": "ERC1155_TRANSFER", - "data": { - "amount": { - "after": "1", - "before": "0" - }, - "contract": { - "address": "0x51e613727fdd2e0b91b51c3e5427e9440a7957e4", - "kind": "ACCOUNT" - }, - "metadata": { - "rawImageUrl": "https://example.com/13014975.png" - }, - "tokenId": "13014975", - "assetPrice": { - "source": "Simplehash", - "updatedAt": "1679331222", - "dollarValuePerToken": "232.43" - }, - "name": %s - } - } - } - ] - }, - "warnings": [] - } - )"); - - auto json_with_token_name = base::StringPrintf(json.c_str(), "\"Corgi\""); - - auto simulation_response = - evm::ParseSimulationResponse(ParseJson(json_with_token_name)); - ASSERT_TRUE(simulation_response); - - EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kNone); - EXPECT_EQ(simulation_response->warnings.size(), 0u); - EXPECT_FALSE(simulation_response->simulation_results->error); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 1u); - - const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); - EXPECT_EQ(state_change->human_readable_diff, "Receive Corgi"); - EXPECT_EQ(state_change->raw_info->kind, - mojom::BlowfishEVMRawInfoKind::kErc1155Transfer); - ASSERT_TRUE(state_change->raw_info->data->is_erc1155_transfer_data()); - const auto& state_change_raw_info = - state_change->raw_info->data->get_erc1155_transfer_data(); - EXPECT_EQ(state_change_raw_info->amount->before, "0"); - EXPECT_EQ(state_change_raw_info->amount->after, "1"); - EXPECT_EQ(state_change_raw_info->name, "Corgi"); - EXPECT_EQ(state_change_raw_info->contract->kind, + "https://corgi.png"); + ASSERT_TRUE(state_change_1_raw_info->counterparty); + EXPECT_EQ(state_change_1_raw_info->counterparty->kind, mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_raw_info->contract->address, + EXPECT_EQ(state_change_1_raw_info->counterparty->address, + "0x06924592cdf28acd3c1d23c37875c6c6a667bdf7"); + ASSERT_TRUE(state_change_1_raw_info->asset); + EXPECT_EQ(state_change_1_raw_info->asset->address, "0x51e613727fdd2e0b91b51c3e5427e9440a7957e4"); - EXPECT_EQ(state_change_raw_info->metadata->raw_image_url, - "https://example.com/13014975.png"); - EXPECT_EQ(state_change_raw_info->token_id, "13014975"); - EXPECT_EQ(state_change_raw_info->asset_price->source, + EXPECT_EQ(state_change_1_raw_info->asset->name, "Corgi"); + EXPECT_EQ(state_change_1_raw_info->asset->token_id, "13014975"); + EXPECT_EQ(state_change_1_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kSimplehash); - EXPECT_EQ(state_change_raw_info->asset_price->last_updated_at, "1679331222"); - EXPECT_EQ(state_change_raw_info->asset_price->dollar_value_per_token, + EXPECT_EQ(state_change_1_raw_info->asset->price->last_updated_at, + "1679331222"); + EXPECT_EQ(state_change_1_raw_info->asset->price->dollar_value_per_token, "232.43"); - - json_with_token_name = base::StringPrintf(json.c_str(), "null"); - EXPECT_FALSE(evm::ParseSimulationResponse(ParseJson(json_with_token_name))); - - json_with_token_name = base::StringPrintf(json.c_str(), "[]"); - EXPECT_FALSE(evm::ParseSimulationResponse(ParseJson(json_with_token_name))); - - json_with_token_name = base::StringPrintf(json.c_str(), "{}"); - EXPECT_FALSE(evm::ParseSimulationResponse(ParseJson(json_with_token_name))); - - json_with_token_name = base::StringPrintf(json.c_str(), "false"); - EXPECT_FALSE(evm::ParseSimulationResponse(ParseJson(json_with_token_name))); } -// Example from https://docs.blowfish.xyz/reference/scan-transaction-evm -TEST(SimulationResponseParserUnitTest, ParseEVMERC1155ApprovalForAll) { +// Example from https://docs.blowfish.xyz/reference/scan-transactions-evm +TEST(SimulationResponseParserUnitTest, ParseEvmErc1155ApprovalForAll) { std::string json(R"( { - "action": "WARN", - "warnings": [ + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"WARN", + "warnings":[ { - "kind": "UNLIMITED_ALLOWANCE_TO_NFTS", - "message": "You are allowing this website to withdraw funds from your account in the future", - "severity": "WARNING" + "kind":"UNLIMITED_ALLOWANCE_TO_NFTS", + "message":"You are allowing this website to withdraw funds from your account in the future.", + "severity":"WARNING" } ], - "simulationResults": { - "error": null, - "gas": { - "gasLimit": null - }, - "expectedStateChanges": [ - { - "humanReadableDiff": "Approve to transfer all your Sandbox's ASSETs", - "rawInfo": { - "kind": "ERC1155_APPROVAL_FOR_ALL", - "data": { - "amount": { - "after": "115792089237316195423570985008687907853269984665640564039457584007913129639935", - "before": "0" - }, - "contract": { - "address": "0xa342f5d851e866e18ff98f351f2c6637f4478db5", - "kind": "ACCOUNT" - }, - "owner": { - "address": "0xed2ab4948ba6a909a7751dec4f34f303eb8c7236", - "kind": "ACCOUNT" - }, - "spender": { - "address": "0x00000000006c3852cbef3e08e8df289169ede581", - "kind": "ACCOUNT" - }, - "assetPrice": { - "source": "Simplehash", - "updatedAt": "1679331222", - "dollarValuePerToken": "232.43" + "simulationResults":{ + "aggregated":{ + "error":null, + "userAccount":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "expectedStateChanges":{ + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045":[ + { + "humanReadableDiff":"Approve to transfer all your Sandbox's ASSETs", + "rawInfo":{ + "kind":"ERC1155_APPROVAL_FOR_ALL", + "data":{ + "amount":{ + "after":"115792089237316195423570985008687907853269984665640564039457584007913129639935", + "before":"0" + }, + "owner":{ + "address":"0xed2ab4948ba6a909a7751dec4f34f303eb8c7236", + "kind":"ACCOUNT" + }, + "spender":{ + "address":"0x00000000006c3852cbef3e08e8df289169ede581", + "kind":"ACCOUNT" + }, + "asset":{ + "address":"0xa342f5d851e866e18ff98f351f2c6637f4478db5", + "name":%s, + "price":{ + "source":"Simplehash", + "updatedAt":"1679331222", + "dollarValuePerToken":"232.43" + } + } + } } } - } + ] } - ] + } } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto json_with_token_name = + base::StringPrintf(json.c_str(), "\"Sandbox ASSET\""); + + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json_with_token_name), + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); + EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kWarn); ASSERT_EQ(simulation_response->warnings.size(), 1u); EXPECT_EQ(simulation_response->warnings.at(0)->kind, mojom::BlowfishWarningKind::kUnlimitedAllowanceToNfts); EXPECT_EQ(simulation_response->warnings.at(0)->message, "You are allowing this website to withdraw funds from your account " - "in the future"); + "in the future."); EXPECT_EQ(simulation_response->warnings.at(0)->severity, mojom::BlowfishWarningSeverity::kWarning); - EXPECT_FALSE(simulation_response->simulation_results->error); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 1u); + EXPECT_FALSE(simulation_response->error); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 1u); - const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + const auto& state_change = simulation_response->expected_state_changes.at(0); EXPECT_EQ(state_change->human_readable_diff, "Approve to transfer all your Sandbox's ASSETs"); EXPECT_EQ(state_change->raw_info->kind, @@ -939,26 +1002,48 @@ TEST(SimulationResponseParserUnitTest, ParseEVMERC1155ApprovalForAll) { EXPECT_EQ(state_change_raw_info->amount->after, "115792089237316195423570985008687907853269984665640564039457584007" "913129639935"); - EXPECT_EQ(state_change_raw_info->contract->kind, - mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_raw_info->contract->address, - "0xa342f5d851e866e18ff98f351f2c6637f4478db5"); + ASSERT_TRUE(state_change_raw_info->owner); EXPECT_EQ(state_change_raw_info->owner->kind, mojom::BlowfishEVMAddressKind::kAccount); EXPECT_EQ(state_change_raw_info->owner->address, "0xed2ab4948ba6a909a7751dec4f34f303eb8c7236"); + ASSERT_TRUE(state_change_raw_info->spender); EXPECT_EQ(state_change_raw_info->spender->kind, mojom::BlowfishEVMAddressKind::kAccount); EXPECT_EQ(state_change_raw_info->spender->address, "0x00000000006c3852cbef3e08e8df289169ede581"); - EXPECT_EQ(state_change_raw_info->asset_price->source, + ASSERT_TRUE(state_change_raw_info->asset); + EXPECT_EQ(state_change_raw_info->asset->address, + "0xa342f5d851e866e18ff98f351f2c6637f4478db5"); + EXPECT_EQ(state_change_raw_info->asset->name, "Sandbox ASSET"); + EXPECT_EQ(state_change_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kSimplehash); - EXPECT_EQ(state_change_raw_info->asset_price->last_updated_at, "1679331222"); - EXPECT_EQ(state_change_raw_info->asset_price->dollar_value_per_token, + EXPECT_EQ(state_change_raw_info->asset->price->last_updated_at, "1679331222"); + EXPECT_EQ(state_change_raw_info->asset->price->dollar_value_per_token, "232.43"); + + json_with_token_name = base::StringPrintf(json.c_str(), "null"); + EXPECT_TRUE(evm::ParseSimulationResponse( + ParseJson(json_with_token_name), + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045")); + + json_with_token_name = base::StringPrintf(json.c_str(), "[]"); + EXPECT_FALSE(evm::ParseSimulationResponse( + ParseJson(json_with_token_name), + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045")); + + json_with_token_name = base::StringPrintf(json.c_str(), "{}"); + EXPECT_FALSE(evm::ParseSimulationResponse( + ParseJson(json_with_token_name), + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045")); + + json_with_token_name = base::StringPrintf(json.c_str(), "false"); + EXPECT_FALSE(evm::ParseSimulationResponse( + ParseJson(json_with_token_name), + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045")); } -TEST(SimulationResponseParserUnitTest, ParseEVMSimulationErrorResponse) { +TEST(SimulationResponseParserUnitTest, ParseEvmSimulationErrorResponse) { std::string json(R"( { "error": "No transactions to simulate" @@ -978,326 +1063,279 @@ TEST(SimulationResponseParserUnitTest, ParseEVMSimulationErrorResponse) { EXPECT_FALSE(error_response); } -TEST(SimulationResponseParserUnitTest, ParseEVMInvalidDecimal) { +TEST(SimulationResponseParserUnitTest, ParseEvmInvalidDecimals) { std::string json(R"( { - "action": "NONE", - "simulationResults": { - "error": null, - "expectedStateChanges": [ - { - "humanReadableDiff": "Send 3.181 ETH", - "rawInfo": { - "kind": "NATIVE_ASSET_TRANSFER", - "data": { - "amount": { - "after": "998426264937289938488", - "before": "1001607264937289938488" - }, - "contract": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "kind": "ACCOUNT" - }, - "asset": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "decimals": "boo", - "imageUrl": "https://example.com/eth.png", - "name": "Ether", - "price": { - "dollarValuePerToken": "1968.47", - "source": "Coingecko", - "updatedAt": "1670324557" - }, - "symbol": "ETH", - "verified": true - } + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"NONE", + "warnings":[], + "simulationResults":{ + "aggregated":{ + "error":null, + "userAccount":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "expectedStateChanges":{ + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045":[ + { + "humanReadableDiff":"Send 3.181 ETH", + "rawInfo":{ + "kind":"NATIVE_ASSET_TRANSFER", + "data":{ + "amount":{ + "after":"998426264937289938488", + "before":"1001607264937289938488" + }, + "asset":{ + "address":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "symbol":"ETH", + "name":"Ether", + "decimals":"boo", + "verified":true, + "imageUrl":"https//eth.png", + "price":null + }, + "counterparty":{ + "kind":"ACCOUNT", + "address":"0x06924592cdf28acd3c1d23c37875c6c6a667bdf7" + } + } + } } - } + ] } - ], - "gas": { - "gasLimit": "269476" } - }, - "warnings": [] + } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); EXPECT_FALSE(simulation_response); } -TEST(SimulationResponseParserUnitTest, ParseEVMUnknownError) { +TEST(SimulationResponseParserUnitTest, ParseEvmUnknownError) { std::string json(R"( { - "action": "NONE", - "simulationResults": { - "error": { - "humanReadableError": "Unable to simulate transaction", - "kind": "UNKNOWN_ERROR" - }, - "expectedStateChanges": [], - "gas": { - "gasLimit": null + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"NONE", + "warnings":[], + "simulationResults":{ + "aggregated": { + "error": { + "humanReadableError": "Unable to simulate transaction", + "kind": "UNKNOWN_ERROR" + }, + "expectedStateChanges": { + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045": [] + }, } - }, - "warnings": [] + } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kNone); EXPECT_EQ(simulation_response->warnings.size(), 0ULL); - ASSERT_TRUE(simulation_response->simulation_results->error); - EXPECT_EQ(simulation_response->simulation_results->error->kind, + ASSERT_TRUE(simulation_response->error); + EXPECT_EQ(simulation_response->error->kind, mojom::BlowfishEVMErrorKind::kUnknownError); - EXPECT_EQ( - simulation_response->simulation_results->error->human_readable_error, - "Unable to simulate transaction"); - EXPECT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 0ULL); + EXPECT_EQ(simulation_response->error->human_readable_error, + "Unable to simulate transaction"); + EXPECT_EQ(simulation_response->expected_state_changes.size(), 0ULL); } -TEST(SimulationResponseParserUnitTest, ParseEVMNullableFields) { +TEST(SimulationResponseParserUnitTest, ParseEvmNullableFields) { std::string json_fmt(R"( { + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", "action":"NONE", + "warnings":[], "simulationResults":{ - "error":null, - "expectedStateChanges":[ - { - "humanReadableDiff":"Send 0.00307 BNB", - "rawInfo":{ - "data":{ - "amount":{ - "after":"90862208830306021", - "before":"93930808830306021" - }, - "asset":{ - "address":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "decimals":"18", - "imageUrl":%s, - "name":"Binance Coin", - "price":%s, - "symbol":"BNB", - "verified":true - }, - "contract":{ - "address":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "kind":"ACCOUNT" + "aggregated":{ + "error":null, + "userAccount":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "expectedStateChanges":{ + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045":[ + { + "humanReadableDiff":"Send 3.181 ETH", + "rawInfo":{ + "kind":"NATIVE_ASSET_TRANSFER", + "data":{ + "amount":{ + "after":"998426264937289938488", + "before":"1001607264937289938488" + }, + "asset":{ + "address":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "symbol":%s, + "name":%s, + "decimals":"18", + "verified":true, + "imageUrl":%s, + "price":%s + }, + "counterparty":{ + "kind":"ACCOUNT", + "address":"0x06924592cdf28acd3c1d23c37875c6c6a667bdf7" + } + } } - }, - "kind":"NATIVE_ASSET_TRANSFER" - } + } + ] } - ], - "gas":{ - "gasLimit":"241822" } - }, - "warnings":[] + } } )"); { - auto json = base::StringPrintf(json_fmt.c_str(), "null", "null"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); - - // OK: null values for asset->imageUrl and asset->price are allowed. + auto json = + base::StringPrintf(json_fmt.c_str(), "null", "null", "null", "null"); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); + + // OK: null values are allowed for the following fields: + // - asset->imageUrl + // - asset->price + // - asset->name + // - asset->symbol ASSERT_TRUE(simulation_response); EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kNone); EXPECT_EQ(simulation_response->warnings.size(), 0ULL); - EXPECT_FALSE(simulation_response->simulation_results->error); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 1ULL); + EXPECT_FALSE(simulation_response->error); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 1ULL); const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); - EXPECT_EQ(state_change->human_readable_diff, "Send 0.00307 BNB"); + simulation_response->expected_state_changes.at(0); + EXPECT_EQ(state_change->human_readable_diff, "Send 3.181 ETH"); EXPECT_EQ(state_change->raw_info->kind, mojom::BlowfishEVMRawInfoKind::kNativeAssetTransfer); ASSERT_TRUE(state_change->raw_info->data->is_native_asset_transfer_data()); - const auto& state_change_raw_info = state_change->raw_info->data->get_native_asset_transfer_data(); - EXPECT_EQ(state_change_raw_info->amount->after, "90862208830306021"); - EXPECT_EQ(state_change_raw_info->amount->before, "93930808830306021"); - EXPECT_EQ(state_change_raw_info->contract->address, - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - EXPECT_EQ(state_change_raw_info->contract->kind, + EXPECT_EQ(state_change_raw_info->amount->before, "1001607264937289938488"); + EXPECT_EQ(state_change_raw_info->amount->after, "998426264937289938488"); + ASSERT_TRUE(state_change_raw_info->counterparty); + EXPECT_EQ(state_change_raw_info->counterparty->kind, mojom::BlowfishEVMAddressKind::kAccount); + EXPECT_EQ(state_change_raw_info->counterparty->address, + "0x06924592cdf28acd3c1d23c37875c6c6a667bdf7"); + ASSERT_TRUE(state_change_raw_info->asset); EXPECT_EQ(state_change_raw_info->asset->address, "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + EXPECT_EQ(state_change_raw_info->asset->symbol, ""); + EXPECT_EQ(state_change_raw_info->asset->name, ""); EXPECT_EQ(state_change_raw_info->asset->decimals, 18); - EXPECT_EQ(state_change_raw_info->asset->image_url, std::nullopt); - EXPECT_EQ(state_change_raw_info->asset->name, "Binance Coin"); - EXPECT_FALSE(state_change_raw_info->asset->price); - EXPECT_EQ(state_change_raw_info->asset->symbol, "BNB"); EXPECT_TRUE(state_change_raw_info->asset->verified); + ASSERT_EQ(state_change_raw_info->asset->lists.size(), 0u); + EXPECT_EQ(state_change_raw_info->asset->image_url, ""); + EXPECT_FALSE(state_change_raw_info->asset->price); } { - auto json = base::StringPrintf(json_fmt.c_str(), "true", "null"); + auto json = + base::StringPrintf(json_fmt.c_str(), "null", "null", "true", "null"); // OK: invalid values for nullable field asset->imageUrl are treated as // null. - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + simulation_response->expected_state_changes.at(0); const auto& state_change_raw_info = state_change->raw_info->data->get_native_asset_transfer_data(); - - EXPECT_EQ(state_change_raw_info->asset->image_url, std::nullopt); + EXPECT_EQ(state_change_raw_info->asset->image_url, ""); } { - auto json = base::StringPrintf(json_fmt.c_str(), "null", "true"); + auto json = + base::StringPrintf(json_fmt.c_str(), "null", "null", "null", "true"); // OK: invalid values for nullable field asset->price are treated as // null. - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + simulation_response->expected_state_changes.at(0); const auto& state_change_raw_info = state_change->raw_info->data->get_native_asset_transfer_data(); - EXPECT_FALSE(state_change_raw_info->asset->price); } - json_fmt = R"( - { - "action":"NONE", - "simulationResults":{ - "error":null, - "expectedStateChanges":[ - { - "humanReadableDiff": "Receive PudgyPenguins #7238", - "rawInfo": { - "kind": "ERC721_TRANSFER", - "data": { - "amount": { - "after": "1", - "before": "0" - }, - "contract": { - "address": "0xbd3531da5cf5857e7cfaa92426877b022e612cf8", - "kind": "ACCOUNT" - }, - "metadata": { - "rawImageUrl": "https://example.com/assets/7238.png" - }, - "name": "PudgyPenguins", - "symbol": "PPG", - "tokenId": "7238", - "assetPrice":%s - } - } - } - ], - "gas":{ - "gasLimit":"241822" - } - }, - "warnings":[] - } - )"; - { - auto json = base::StringPrintf(json_fmt.c_str(), "null"); - // OK: null value for assetPrice field is allowed. - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto json = base::StringPrintf(json_fmt.c_str(), "null", "null", "null", + "{\"foo\": 1}"); + // OK: invalid dict for nullable field asset->price is treated as null. + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); ASSERT_TRUE(simulation_response); const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + simulation_response->expected_state_changes.at(0); const auto& state_change_raw_info = - state_change->raw_info->data->get_erc721_transfer_data(); - - EXPECT_FALSE(state_change_raw_info->asset_price); - } - - { - auto json = base::StringPrintf(json_fmt.c_str(), "true"); - // OK: invalid values for nullable field assetPrice are treated as - // null. - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); - ASSERT_TRUE(simulation_response); - const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); - const auto& state_change_raw_info = - state_change->raw_info->data->get_erc721_transfer_data(); - - EXPECT_FALSE(state_change_raw_info->asset_price); - } - - { - auto json = base::StringPrintf(json_fmt.c_str(), "{\"foo\": 1}"); - // OK: invalid dict for nullable field assetPrice is treated as null. - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); - ASSERT_TRUE(simulation_response); - const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); - const auto& state_change_raw_info = - state_change->raw_info->data->get_erc721_transfer_data(); - - EXPECT_FALSE(state_change_raw_info->asset_price); + state_change->raw_info->data->get_native_asset_transfer_data(); + EXPECT_FALSE(state_change_raw_info->asset->price); } } -TEST(SimulationResponseParserUnitTest, ParseEVMInvalidRawInfoData) { +TEST(SimulationResponseParserUnitTest, ParseEvmInvalidRawInfoData) { std::string json(R"( { + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", "action":"NONE", + "warnings":[], "simulationResults":{ - "error":null, - "expectedStateChanges":[ - { - "humanReadableDiff": "Receive PudgyPenguins #7238", - "rawInfo": { - "kind": "ERC721_TRANSFER", - "data": null - } + "aggregated":{ + "error": null, + "userAccount":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "expectedStateChanges":{ + "0xd8da6bf26964af9d7eed9e03e53415d37aa96045":[ + { + "humanReadableDiff":"Send 3.181 ETH", + "rawInfo":{ + "kind":"NATIVE_ASSET_TRANSFER", + "data":null + } + } + ] } - ], - "gas":{ - "gasLimit":"241822" } - }, - "warnings":[] + } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); EXPECT_FALSE(simulation_response); } -TEST(SimulationResponseParserUnitTest, ParseEVMInvalidError) { +TEST(SimulationResponseParserUnitTest, ParseEvmInvalidError) { std::string json(R"( { + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", "action":"NONE", + "warnings":[], + "error":false, "simulationResults":{ - "error":false, - "expectedStateChanges":[], - "gas":{ - "gasLimit":"241822" + "aggregated":{ + "expectedStateChanges":{} } - }, - "warnings":[] + } } )"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); EXPECT_FALSE(simulation_response); } -TEST(SimulationResponseParserUnitTest, ParseEVMResponseNotDict) { +TEST(SimulationResponseParserUnitTest, ParseEvmResponseNotDict) { std::string json(R"([])"); - auto simulation_response = evm::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = evm::ParseSimulationResponse( + ParseJson(json), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); EXPECT_FALSE(simulation_response); } @@ -1305,194 +1343,217 @@ TEST(SimulationResponseParserUnitTest, ParseEVMResponseNotDict) { TEST(SimulationResponseParserUnitTest, ParseSolanaStateChanges) { std::string json(R"( { - "status": "CHECKS_PASSED", - "action": "NONE", - "warnings": [], - "simulationResults": { - "isRecentBlockhashExpired": false, - "expectedStateChanges": [ - { - "humanReadableDiff": "Receive 0.05657 SOL", - "suggestedColor": "CREDIT", - "rawInfo": { - "kind": "SOL_TRANSFER", - "data": { - "symbol": "SOL", - "name": "Solana Native Token", - "decimals": "9", - "diff": { - "sign": "PLUS", - "digits": "56573477" + "aggregated": { + "action": "NONE", + "warnings": [], + "expectedStateChanges": { + "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT": [ + { + "humanReadableDiff": "Receive 0.05657 SOL", + "suggestedColor": "CREDIT", + "rawInfo": { + "kind": "SOL_TRANSFER", + "data": { + "asset": { + "symbol": "SOL", + "name": "Solana Native Token", + "decimals": "9", + "price": { + "source": "Coingecko", + "updatedAt": "1679331222", + "dollarValuePerToken": "100.92" + }, + "imageUrl": "https://sol.png" + }, + "diff": { + "sign": "PLUS", + "digits": "56573477" + } } } - } - }, - { - "humanReadableDiff": "Send 2 USDT", - "suggestedColor": "DEBIT", - "rawInfo": { - "kind": "SPL_TRANSFER", - "data": { - "symbol": "USDT", - "name": "USDT", - "mint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", - "decimals": "6", - "supply": "1000000000", - "metaplexTokenStandard": "unknown", - "assetPrice": { - "source": "Coingecko", - "last_updated_at": "1679331222", - "dollar_value_per_token": "0.99" - }, - "diff": { - "sign": "MINUS", - "digits": "2000000" + }, + { + "humanReadableDiff": "Send 2 USDT", + "suggestedColor": "DEBIT", + "rawInfo": { + "kind": "SPL_TRANSFER", + "data": { + "asset": { + "symbol": "USDT", + "name": "USDT", + "mint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", + "decimals": "6", + "supply": "1000000000", + "metaplexTokenStandard": "unknown", + "price": { + "source": "Coingecko", + "updatedAt": "1679331222", + "dollarValuePerToken": "0.99" + }, + "imageUrl": "https://usdt.png" + }, + "diff": { + "sign": "MINUS", + "digits": "2000000" + }, + "counterparty": "5wytVPbjLb2VCXbynhUQabEZZD2B6Wxrkvwm6v6Cuy5X" } } - } - }, - { - "humanReadableDiff": "Approve to transfer Phantom QA NFT", - "suggestedColor": "DEBIT", - "rawInfo": { - "kind": "SPL_APPROVAL", - "data": { - "delegate": "CL38BiCb5fs3qGnKKSusaJdY24aFUUZ6vkvkujrmah83", - "mint": "4ERKJVpwqS6Kcj115YSAjqU4WYV3YCDiYHPS72eQTY6p", - "symbol": "PHANTOMQA", - "name": "Phantom QA NFT", - "decimals": "0", - "diff": { - "sign": "PLUS", - "digits": "1525878906250000000" - }, - "supply": "1", - "metaplexTokenStandard": "non_fungible", - "assetPrice": null + }, + { + "humanReadableDiff": "Approve to transfer Phantom QA NFT", + "suggestedColor": "DEBIT", + "rawInfo": { + "kind": "SPL_APPROVAL", + "data": { + "delegate": "CL38BiCb5fs3qGnKKSusaJdY24aFUUZ6vkvkujrmah83", + "asset": { + "symbol": "PHANTOMQA", + "name": "Phantom QA NFT", + "mint": "4ERKJVpwqS6Kcj115YSAjqU4WYV3YCDiYHPS72eQTY6p", + "decimals": "0", + "supply": "1", + "metaplexTokenStandard": "non_fungible", + "price": null + }, + "diff": { + "sign": "PLUS", + "digits": "1525878906250000000" + } + } } - } - }, - { - "humanReadableDiff": "Unapprove from transferring up to 0.00132 USDC", - "suggestedColor": "CREDIT", - "rawInfo": { - "kind": "SPL_APPROVAL", - "data": { - "delegate": "FCRBwXC5vrzHi2Vxgn3L2NB2KKFuRhiHBjioC47sSm2o", - "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "symbol": "USDC", - "name": "USD Coin", - "decimals": "6", - "diff": { - "sign": "MINUS", - "digits": "1321" - }, - "supply": "5034964468128435", - "metaplexTokenStandard": "unknown", - "assetPrice": { - "source": "Coingecko", - "last_updated_at": "1679331222", - "dollar_value_per_token": "1.01" + }, + { + "humanReadableDiff": "Unapprove from transferring up to 0.00132 USDC", + "suggestedColor": "CREDIT", + "rawInfo": { + "kind": "SPL_APPROVAL", + "data": { + "delegate": "FCRBwXC5vrzHi2Vxgn3L2NB2KKFuRhiHBjioC47sSm2o", + "asset": { + "symbol": "USDC", + "name": "USD Coin", + "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "decimals": "6", + "supply": "5034964468128435", + "metaplexTokenStandard": "unknown", + "price": { + "source": "Coingecko", + "updatedAt": "1679331222", + "dollarValuePerToken": "1.01" + }, + "imageUrl": "https://usdc.png" + }, + "diff": { + "sign": "MINUS", + "digits": "1321" + } } } - } - }, - { - "humanReadableDiff": "Transfer control over your SOL staking account 2AG3be..p2vFQS", - "suggestedColor": "DEBIT", - "rawInfo": { - "kind": "SOL_STAKE_AUTHORITY_CHANGE", - "data": { - "stakeAccount": "2AG3beWwvyvEMLfwJcQS9DKMV62C3UWTTf8d7gp2vFQS", - "currAuthorities": { - "staker": "J58MrVr9qJPzJJS8RPQUDfaFirN3PiVHXU48zr95FY48", - "withdrawer": "J58MrVr9qJPzJJS8RPQUDfaFirN3PiVHXU48zr95FY48" - }, - "futureAuthorities": { - "staker": "EpochxXNkmM2akxBTuCEizW1oWyzgrPZ1CVZ3GpD7Egm", - "withdrawer": "EpochxXNkmM2akxBTuCEizW1oWyzgrPZ1CVZ3GpD7Egm" - }, - "symbol": "SOL", - "name": "Solana Native Token", - "decimals": "9", - "solStaked": "228895995552" + }, + { + "humanReadableDiff": "Transfer control over your SOL staking account 2AG3be..p2vFQS", + "suggestedColor": "DEBIT", + "rawInfo": { + "kind": "SOL_STAKE_AUTHORITY_CHANGE", + "data": { + "stakeAccount": "2AG3beWwvyvEMLfwJcQS9DKMV62C3UWTTf8d7gp2vFQS", + "currentAuthorities": { + "staker": "J58MrVr9qJPzJJS8RPQUDfaFirN3PiVHXU48zr95FY48", + "withdrawer": "J58MrVr9qJPzJJS8RPQUDfaFirN3PiVHXU48zr95FY48" + }, + "futureAuthorities": { + "staker": "EpochxXNkmM2akxBTuCEizW1oWyzgrPZ1CVZ3GpD7Egm", + "withdrawer": "EpochxXNkmM2akxBTuCEizW1oWyzgrPZ1CVZ3GpD7Egm" + }, + "asset": { + "symbol": "SOL", + "name": "Solana Native Token", + "decimals": "9", + "price": { + "source": "Coingecko", + "updatedAt": "1679331222", + "dollarValuePerToken": "100.92" + }, + "imageUrl": "https://sol.png" + }, + "solStaked": "228895995552" + } } } - } - ], - "error": null, - "raw": { - "err": null, - "logs": [], - "accounts": [], - "returnData": null, - "unitsConsumed": 148013 - } + ] + }, + "error": null } } )"); - auto simulation_response = solana::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = solana::ParseSimulationResponse( + ParseJson(json), "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT"); ASSERT_TRUE(simulation_response); EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kNone); EXPECT_EQ(simulation_response->warnings.size(), 0u); - EXPECT_FALSE(simulation_response->simulation_results->error); - EXPECT_FALSE( - simulation_response->simulation_results->is_recent_blockhash_expired); - ASSERT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 5u); + EXPECT_FALSE(simulation_response->error); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 5u); const auto& state_change_0 = - simulation_response->simulation_results->expected_state_changes.at(0); + simulation_response->expected_state_changes.at(0); EXPECT_EQ(state_change_0->human_readable_diff, "Receive 0.05657 SOL"); EXPECT_EQ(state_change_0->suggested_color, mojom::BlowfishSuggestedColor::kCredit); EXPECT_EQ(state_change_0->raw_info->kind, mojom::BlowfishSolanaRawInfoKind::kSolTransfer); ASSERT_TRUE(state_change_0->raw_info->data->is_sol_transfer_data()); - const auto& state_change_0_raw_info = state_change_0->raw_info->data->get_sol_transfer_data(); - EXPECT_EQ(state_change_0_raw_info->symbol, "SOL"); - EXPECT_EQ(state_change_0_raw_info->name, "Solana Native Token"); - EXPECT_EQ(state_change_0_raw_info->decimals, 9); + EXPECT_EQ(state_change_0_raw_info->asset->symbol, "SOL"); + EXPECT_EQ(state_change_0_raw_info->asset->name, "Solana Native Token"); + EXPECT_EQ(state_change_0_raw_info->asset->mint, ""); + EXPECT_EQ(state_change_0_raw_info->asset->decimals, 9); + ASSERT_TRUE(state_change_0_raw_info->asset->price); + EXPECT_EQ(state_change_0_raw_info->asset->price->source, + mojom::BlowfishAssetPriceSource::kCoingecko); + EXPECT_EQ(state_change_0_raw_info->asset->price->last_updated_at, + "1679331222"); + EXPECT_EQ(state_change_0_raw_info->asset->price->dollar_value_per_token, + "100.92"); EXPECT_EQ(state_change_0_raw_info->diff->sign, mojom::BlowfishDiffSign::kPlus); EXPECT_EQ(state_change_0_raw_info->diff->digits, 56573477ULL); const auto& state_change_1 = - simulation_response->simulation_results->expected_state_changes.at(1); + simulation_response->expected_state_changes.at(1); EXPECT_EQ(state_change_1->human_readable_diff, "Send 2 USDT"); EXPECT_EQ(state_change_1->suggested_color, mojom::BlowfishSuggestedColor::kDebit); EXPECT_EQ(state_change_1->raw_info->kind, mojom::BlowfishSolanaRawInfoKind::kSplTransfer); ASSERT_TRUE(state_change_1->raw_info->data->is_spl_transfer_data()); - const auto& state_change_1_raw_info = state_change_1->raw_info->data->get_spl_transfer_data(); - EXPECT_EQ(state_change_1_raw_info->symbol, "USDT"); - EXPECT_EQ(state_change_1_raw_info->name, "USDT"); - EXPECT_EQ(state_change_1_raw_info->mint, + EXPECT_EQ(state_change_1_raw_info->asset->symbol, "USDT"); + EXPECT_EQ(state_change_1_raw_info->asset->name, "USDT"); + EXPECT_EQ(state_change_1_raw_info->asset->mint, "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); - EXPECT_EQ(state_change_1_raw_info->decimals, 6); - EXPECT_EQ(state_change_1_raw_info->supply, 1000000000ULL); - EXPECT_EQ(state_change_1_raw_info->metaplex_token_standard, + EXPECT_EQ(state_change_1_raw_info->asset->decimals, 6); + EXPECT_EQ(state_change_1_raw_info->asset->metaplex_token_standard, mojom::BlowfishMetaplexTokenStandardKind::kUnknown); - EXPECT_EQ(state_change_1_raw_info->asset_price->source, + ASSERT_TRUE(state_change_1_raw_info->asset->price); + EXPECT_EQ(state_change_1_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kCoingecko); - EXPECT_EQ(state_change_1_raw_info->asset_price->last_updated_at, + EXPECT_EQ(state_change_1_raw_info->asset->price->last_updated_at, "1679331222"); - EXPECT_EQ(state_change_1_raw_info->asset_price->dollar_value_per_token, + EXPECT_EQ(state_change_1_raw_info->asset->price->dollar_value_per_token, "0.99"); EXPECT_EQ(state_change_1_raw_info->diff->sign, mojom::BlowfishDiffSign::kMinus); EXPECT_EQ(state_change_1_raw_info->diff->digits, 2000000ULL); + EXPECT_EQ(state_change_1_raw_info->counterparty, + "5wytVPbjLb2VCXbynhUQabEZZD2B6Wxrkvwm6v6Cuy5X"); const auto& state_change_2 = - simulation_response->simulation_results->expected_state_changes.at(2); + simulation_response->expected_state_changes.at(2); EXPECT_EQ(state_change_2->human_readable_diff, "Approve to transfer Phantom QA NFT"); EXPECT_EQ(state_change_2->suggested_color, @@ -1500,26 +1561,24 @@ TEST(SimulationResponseParserUnitTest, ParseSolanaStateChanges) { EXPECT_EQ(state_change_2->raw_info->kind, mojom::BlowfishSolanaRawInfoKind::kSplApproval); ASSERT_TRUE(state_change_2->raw_info->data->is_spl_approval_data()); - const auto& state_change_2_raw_info = state_change_2->raw_info->data->get_spl_approval_data(); EXPECT_EQ(state_change_2_raw_info->delegate, "CL38BiCb5fs3qGnKKSusaJdY24aFUUZ6vkvkujrmah83"); - EXPECT_EQ(state_change_2_raw_info->mint, + EXPECT_EQ(state_change_2_raw_info->asset->symbol, "PHANTOMQA"); + EXPECT_EQ(state_change_2_raw_info->asset->name, "Phantom QA NFT"); + EXPECT_EQ(state_change_2_raw_info->asset->mint, "4ERKJVpwqS6Kcj115YSAjqU4WYV3YCDiYHPS72eQTY6p"); - EXPECT_EQ(state_change_2_raw_info->symbol, "PHANTOMQA"); - EXPECT_EQ(state_change_2_raw_info->name, "Phantom QA NFT"); - EXPECT_EQ(state_change_2_raw_info->decimals, 0); + EXPECT_EQ(state_change_2_raw_info->asset->decimals, 0); + EXPECT_EQ(state_change_2_raw_info->asset->metaplex_token_standard, + mojom::BlowfishMetaplexTokenStandardKind::kNonFungible); + EXPECT_FALSE(state_change_2_raw_info->asset->price); EXPECT_EQ(state_change_2_raw_info->diff->sign, mojom::BlowfishDiffSign::kPlus); EXPECT_EQ(state_change_2_raw_info->diff->digits, 1525878906250000000ULL); - EXPECT_EQ(state_change_2_raw_info->supply, 1ULL); - EXPECT_EQ(state_change_2_raw_info->metaplex_token_standard, - mojom::BlowfishMetaplexTokenStandardKind::kNonFungible); - EXPECT_FALSE(state_change_2_raw_info->asset_price); const auto& state_change_3 = - simulation_response->simulation_results->expected_state_changes.at(3); + simulation_response->expected_state_changes.at(3); EXPECT_EQ(state_change_3->human_readable_diff, "Unapprove from transferring up to 0.00132 USDC"); EXPECT_EQ(state_change_3->suggested_color, @@ -1527,31 +1586,30 @@ TEST(SimulationResponseParserUnitTest, ParseSolanaStateChanges) { EXPECT_EQ(state_change_3->raw_info->kind, mojom::BlowfishSolanaRawInfoKind::kSplApproval); ASSERT_TRUE(state_change_3->raw_info->data->is_spl_approval_data()); - const auto& state_change_3_raw_info = state_change_3->raw_info->data->get_spl_approval_data(); EXPECT_EQ(state_change_3_raw_info->delegate, "FCRBwXC5vrzHi2Vxgn3L2NB2KKFuRhiHBjioC47sSm2o"); - EXPECT_EQ(state_change_3_raw_info->mint, + EXPECT_EQ(state_change_3_raw_info->asset->symbol, "USDC"); + EXPECT_EQ(state_change_3_raw_info->asset->name, "USD Coin"); + EXPECT_EQ(state_change_3_raw_info->asset->mint, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); - EXPECT_EQ(state_change_3_raw_info->symbol, "USDC"); - EXPECT_EQ(state_change_3_raw_info->name, "USD Coin"); - EXPECT_EQ(state_change_3_raw_info->decimals, 6); - EXPECT_EQ(state_change_3_raw_info->diff->sign, - mojom::BlowfishDiffSign::kMinus); - EXPECT_EQ(state_change_3_raw_info->diff->digits, 1321ULL); - EXPECT_EQ(state_change_3_raw_info->supply, 5034964468128435ULL); - EXPECT_EQ(state_change_3_raw_info->metaplex_token_standard, + EXPECT_EQ(state_change_3_raw_info->asset->decimals, 6); + EXPECT_EQ(state_change_3_raw_info->asset->metaplex_token_standard, mojom::BlowfishMetaplexTokenStandardKind::kUnknown); - EXPECT_EQ(state_change_3_raw_info->asset_price->source, + ASSERT_TRUE(state_change_3_raw_info->asset->price); + EXPECT_EQ(state_change_3_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kCoingecko); - EXPECT_EQ(state_change_3_raw_info->asset_price->last_updated_at, + EXPECT_EQ(state_change_3_raw_info->asset->price->last_updated_at, "1679331222"); - EXPECT_EQ(state_change_3_raw_info->asset_price->dollar_value_per_token, + EXPECT_EQ(state_change_3_raw_info->asset->price->dollar_value_per_token, "1.01"); + EXPECT_EQ(state_change_3_raw_info->diff->sign, + mojom::BlowfishDiffSign::kMinus); + EXPECT_EQ(state_change_3_raw_info->diff->digits, 1321ULL); const auto& state_change_4 = - simulation_response->simulation_results->expected_state_changes.at(4); + simulation_response->expected_state_changes.at(4); EXPECT_EQ(state_change_4->human_readable_diff, "Transfer control over your SOL staking account 2AG3be..p2vFQS"); EXPECT_EQ(state_change_4->suggested_color, @@ -1560,22 +1618,29 @@ TEST(SimulationResponseParserUnitTest, ParseSolanaStateChanges) { mojom::BlowfishSolanaRawInfoKind::kSolStakeAuthorityChange); ASSERT_TRUE( state_change_4->raw_info->data->is_sol_stake_authority_change_data()); - const auto& state_change_4_raw_info = state_change_4->raw_info->data->get_sol_stake_authority_change_data(); EXPECT_EQ(state_change_4_raw_info->stake_account, "2AG3beWwvyvEMLfwJcQS9DKMV62C3UWTTf8d7gp2vFQS"); - EXPECT_EQ(state_change_4_raw_info->curr_authorities->staker, + EXPECT_EQ(state_change_4_raw_info->current_authorities->staker, "J58MrVr9qJPzJJS8RPQUDfaFirN3PiVHXU48zr95FY48"); - EXPECT_EQ(state_change_4_raw_info->curr_authorities->withdrawer, + EXPECT_EQ(state_change_4_raw_info->current_authorities->withdrawer, "J58MrVr9qJPzJJS8RPQUDfaFirN3PiVHXU48zr95FY48"); - EXPECT_EQ(state_change_4_raw_info->future_authorities->withdrawer, + EXPECT_EQ(state_change_4_raw_info->future_authorities->staker, "EpochxXNkmM2akxBTuCEizW1oWyzgrPZ1CVZ3GpD7Egm"); EXPECT_EQ(state_change_4_raw_info->future_authorities->withdrawer, "EpochxXNkmM2akxBTuCEizW1oWyzgrPZ1CVZ3GpD7Egm"); - EXPECT_EQ(state_change_4_raw_info->symbol, "SOL"); - EXPECT_EQ(state_change_4_raw_info->name, "Solana Native Token"); - EXPECT_EQ(state_change_4_raw_info->decimals, 9); + EXPECT_EQ(state_change_4_raw_info->asset->symbol, "SOL"); + EXPECT_EQ(state_change_4_raw_info->asset->name, "Solana Native Token"); + EXPECT_EQ(state_change_4_raw_info->asset->mint, ""); + EXPECT_EQ(state_change_4_raw_info->asset->decimals, 9); + ASSERT_TRUE(state_change_4_raw_info->asset->price); + EXPECT_EQ(state_change_4_raw_info->asset->price->source, + mojom::BlowfishAssetPriceSource::kCoingecko); + EXPECT_EQ(state_change_4_raw_info->asset->price->last_updated_at, + "1679331222"); + EXPECT_EQ(state_change_4_raw_info->asset->price->dollar_value_per_token, + "100.92"); EXPECT_EQ(state_change_4_raw_info->sol_staked, 228895995552ULL); } @@ -1584,36 +1649,30 @@ TEST(SimulationResponseParserUnitTest, ParseSolanaStateChanges) { TEST(SimulationResponseParserUnitTest, ParseSolanaWarnings) { std::string json(R"( { - "action": "BLOCK", - "status": "KNOWN_MALICIOUS", - "warnings": [ - { - "severity": "CRITICAL", - "kind": "TRUSTED_BLOCKLIST_DOMAIN", - "message": "This transaction originates from a known malicious domain." + "aggregated": { + "action": "BLOCK", + "warnings": [ + { + "severity": "CRITICAL", + "kind": "TRUSTED_BLOCKLIST_DOMAIN", + "message": "This transaction originates from a known malicious domain." + }, + { + "severity": "WARNING", + "kind": "SUSPECTED_MALICIOUS", + "message": "We suspect this transaction is malicious. Approving may lead to loss of funds." + } + ], + "expectedStateChanges": { + "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT": [] }, - { - "severity": "WARNING", - "kind": "SUSPECTED_MALICIOUS", - "message": "We suspect this transaction is malicious. Approving may lead to loss of funds." - } - ], - "simulationResults": { - "isRecentBlockhashExpired": false, - "expectedStateChanges": [], - "error": null, - "raw": { - "err": null, - "logs": [], - "accounts": [], - "returnData": null, - "unitsConsumed": 148013 - } + "error": null } } )"); - auto simulation_response = solana::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = solana::ParseSimulationResponse( + ParseJson(json), "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT"); ASSERT_TRUE(simulation_response); EXPECT_EQ(simulation_response->action, mojom::BlowfishSuggestedAction::kBlock); @@ -1633,159 +1692,155 @@ TEST(SimulationResponseParserUnitTest, ParseSolanaWarnings) { "We suspect this transaction is malicious. Approving may lead to " "loss of funds."); - EXPECT_FALSE(simulation_response->simulation_results->error); - EXPECT_FALSE( - simulation_response->simulation_results->is_recent_blockhash_expired); - EXPECT_EQ( - simulation_response->simulation_results->expected_state_changes.size(), - 0ULL); + EXPECT_FALSE(simulation_response->error); + EXPECT_EQ(simulation_response->expected_state_changes.size(), 0ULL); } TEST(SimulationResponseParserUnitTest, ParseSolanaNullableFields) { std::string json_fmt(R"( { - "status": "CHECKS_PASSED", - "action": "NONE", - "warnings": [], - "simulationResults": { - "isRecentBlockhashExpired": false, - "expectedStateChanges": [ - { - "humanReadableDiff": "Send 2 USDT", - "suggestedColor": "DEBIT", - "rawInfo": { - "kind": "SPL_TRANSFER", - "data": { - "symbol": "USDT", - "name": "USDT", - "mint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", - "decimals": "6", - "supply": "1000000000", - "metaplexTokenStandard": "unknown", - "assetPrice":%s, - "diff": { - "sign": "MINUS", - "digits": "2000000" + "aggregated": { + "action": "NONE", + "warnings": [], + "expectedStateChanges": { + "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT": [ + { + "humanReadableDiff": "Send 2 USDT", + "suggestedColor": "DEBIT", + "rawInfo": { + "kind": "SPL_TRANSFER", + "data": { + "asset": { + "symbol": "USDT", + "name": "USDT", + "mint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", + "decimals": "6", + "supply": "1000000000", + "metaplexTokenStandard": "unknown", + "price": null, + "imageUrl": "https://usdt.png" + }, + "diff": { + "sign": "MINUS", + "digits": "2000000" + }, + "counterparty": "5wytVPbjLb2VCXbynhUQabEZZD2B6Wxrkvwm6v6Cuy5X" } } } - } - ], + ] + }, "error": null, - "raw": { - "err": null, - "logs": [], - "accounts": [], - "returnData": null, - "unitsConsumed": 148013 - } } } )"); { auto json = base::StringPrintf(json_fmt.c_str(), "null"); - auto simulation_response = solana::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = solana::ParseSimulationResponse( + ParseJson(json), "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT"); - // OK: null value for assetPrice is allowed + // OK: null value for asset->price is allowed ASSERT_TRUE(simulation_response); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 1u); const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + simulation_response->expected_state_changes.at(0); const auto& state_change_raw_info = state_change->raw_info->data->get_spl_transfer_data(); - EXPECT_FALSE(state_change_raw_info->asset_price); + EXPECT_FALSE(state_change_raw_info->asset->price); } { auto json = base::StringPrintf(json_fmt.c_str(), "true"); - auto simulation_response = solana::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = solana::ParseSimulationResponse( + ParseJson(json), "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT"); - // OK: invalid values for nullable field assetPrice are treated as + // OK: invalid values for nullable field asset->price are treated as // null. ASSERT_TRUE(simulation_response); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 1u); const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + simulation_response->expected_state_changes.at(0); const auto& state_change_raw_info = state_change->raw_info->data->get_spl_transfer_data(); - EXPECT_FALSE(state_change_raw_info->asset_price); + EXPECT_FALSE(state_change_raw_info->asset->price); } { auto json = base::StringPrintf(json_fmt.c_str(), "{\"foo\": 1}"); - auto simulation_response = solana::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = solana::ParseSimulationResponse( + ParseJson(json), "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT"); - // OK: invalid dict for nullable field assetPrice is treated as null. + // OK: invalid dict for nullable field asset->price is treated as null. ASSERT_TRUE(simulation_response); + ASSERT_EQ(simulation_response->expected_state_changes.size(), 1u); const auto& state_change = - simulation_response->simulation_results->expected_state_changes.at(0); + simulation_response->expected_state_changes.at(0); const auto& state_change_raw_info = state_change->raw_info->data->get_spl_transfer_data(); - EXPECT_FALSE(state_change_raw_info->asset_price); + EXPECT_FALSE(state_change_raw_info->asset->price); } } TEST(SimulationResponseParserUnitTest, ParseSolanaInvalidRawInfoData) { std::string json(R"( { - "status": "CHECKS_PASSED", - "action": "NONE", - "warnings": [], - "simulationResults": { - "isRecentBlockhashExpired": false, - "expectedStateChanges": [ - { - "humanReadableDiff": "Send 2 USDT", - "suggestedColor": "DEBIT", - "rawInfo": { - "kind": "SPL_TRANSFER", - "data": null + "aggregated": { + "status": "CHECKS_PASSED", + "action": "NONE", + "warnings": [], + "expectedStateChanges": { + "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT": [ + { + "humanReadableDiff": "Send 2 USDT", + "suggestedColor": "DEBIT", + "rawInfo": { + "kind": "SPL_TRANSFER", + "data": null + } } - } - ], - "error": null, - "raw": { - "err": null, - "logs": [], - "accounts": [], - "returnData": null, - "unitsConsumed": 148013 - } + ] + }, + "error": null } } )"); - auto simulation_response = solana::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = solana::ParseSimulationResponse( + ParseJson(json), "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT"); EXPECT_FALSE(simulation_response); } TEST(SimulationResponseParserUnitTest, ParseSolanaInvalidError) { std::string json(R"( { - "status": "CHECKS_PASSED", - "action": "NONE", - "warnings": [], - "simulationResults": { - "isRecentBlockhashExpired": false, - "expectedStateChanges": [], + "aggregated": { + "action": "NONE", + "warnings": [], + "expectedStateChanges": { + "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT": [] + }, "error": true } } )"); - auto simulation_response = solana::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = solana::ParseSimulationResponse( + ParseJson(json), "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT"); EXPECT_FALSE(simulation_response); } TEST(SimulationResponseParserUnitTest, ParseSolanaResponseNotDict) { std::string json(R"([])"); - auto simulation_response = solana::ParseSimulationResponse(ParseJson(json)); + auto simulation_response = solana::ParseSimulationResponse( + ParseJson(json), "8eekKfUAGSJbq3CdA2TmHb8tKuyzd5gtEas3MYAtXzrT"); EXPECT_FALSE(simulation_response); } diff --git a/components/brave_wallet/browser/simulation_responses.idl b/components/brave_wallet/browser/simulation_responses.idl index 0d52caf24760..3a432207b91e 100644 --- a/components/brave_wallet/browser/simulation_responses.idl +++ b/components/brave_wallet/browser/simulation_responses.idl @@ -16,19 +16,22 @@ namespace simulation_responses { enum WarningKind { APPROVAL_TO_E_O_A, BLOCKLISTED_DOMAIN_CROSS_ORIGIN, + BLUR_BULK_ORDER_NOT_ON_BLUR, + BLUR_V2_ORDER_NOT_ON_BLUR, BULK_APPROVALS_REQUEST, COMPROMISED_AUTHORITY_UPGRADE, COPY_CAT_DOMAIN, COPY_CAT_IMAGE_UNRESPONSIVE_DOMAIN, DANGLING_APPROVAL, - DEVTOOLS_DISABLED, + DEBUGGER_PAUSED, + DURABLE_NONCE, ETH_SIGN_TX_HASH, + FORTA, + IMBALANCED_DOLLAR_VALUE, KNOWN_MALICIOUS, - MAINNET_REPLAY_POSSIBLE, + MALICIOUS_PACKAGES, MULTI_COPY_CAT_DOMAIN, NEW_DOMAIN, - NON_ASCII_URL, - OBFUSCATED_CODE, PERMIT_NO_EXPIRATION, PERMIT_UNLIMITED_ALLOWANCE, POISONED_ADDRESS, @@ -39,11 +42,16 @@ namespace simulation_responses { TOO_MANY_TRANSACTIONS, TRADE_FOR_NOTHING, TRANSFERRING_ERC20_TO_OWN_CONTRACT, + TRANSFERRING_TOO_MUCH_SOL, + TRANSFERS_MAJORITY_OF_YOUR_SOL, TRUSTED_BLOCKLIST_DOMAIN, UNLIMITED_ALLOWANCE_TO_NFTS, UNUSUAL_GAS_CONSUMPTION, USER_ACCOUNT_OWNER_CHANGE, - WHITELISTED_DOMAIN_CROSS_ORIGIN + TRANSFER_TO_MINT_ACCOUNT, + WHITELISTED_DOMAIN_CROSS_ORIGIN, + YAKOA_NFT_IP_INFRINGEMENT, + RELIABLE_SIMULATION_NOT_POSSIBLE }; enum EVMRawInfoKind { @@ -101,14 +109,6 @@ namespace simulation_responses { DEBIT }; - dictionary SolanaPrice { - AssetPriceSource source; - - // FIXME: The fields below should be in camelCase. Needs an upstream fix. - DOMString last_updated_at; - DOMString dollar_value_per_token; - }; - dictionary Price { AssetPriceSource source; DOMString updatedAt; @@ -126,7 +126,7 @@ namespace simulation_responses { DOMString humanReadableError; }; - dictionary EVMContract { + dictionary EVMCounterparty { EVMAddressKind kind; DOMString address; }; @@ -138,14 +138,17 @@ namespace simulation_responses { dictionary EVMAsset { DOMString address; - DOMString symbol; - DOMString name; - DOMString decimals; - boolean verified; + any? symbol; + any? name; + any? collection; + DOMString? decimals; + boolean? verified; + + // This field is not be present in case of a native asset. DOMString[]? lists; // nullable field of type DOMString - any imageUrl; + any? imageUrl; // nullable field of type Price any price; @@ -156,15 +159,16 @@ namespace simulation_responses { }; dictionary ERC20TransferData { - EVMContract contract; + // nullable field of type EVMCounterparty + any counterparty; + EVMAmount amount; EVMAsset asset; }; dictionary ERC20ApprovalData { - EVMContract contract; - EVMContract owner; - EVMContract spender; + EVMCounterparty owner; + EVMCounterparty spender; EVMAmount amount; EVMAsset asset; }; @@ -172,71 +176,62 @@ namespace simulation_responses { dictionary NativeAssetTransferData { EVMAmount amount; EVMAsset asset; - EVMContract contract; + + // nullable field of type EVMCounterparty + any counterparty; }; dictionary ERC721TransferData { EVMAmount amount; - EVMContract contract; + + // nullable field of type EVMCounterparty + any counterparty; + NFTMetadata metadata; - DOMString name; - DOMString symbol; // nullable field of type DOMString any tokenId; - // nullable field of type Price - any assetPrice; + EVMAsset asset; }; dictionary ERC721ApprovalData { EVMAmount amount; - EVMContract contract; NFTMetadata metadata; - DOMString name; - EVMContract owner; - EVMContract spender; - DOMString symbol; + EVMCounterparty owner; + EVMCounterparty spender; // nullable field of type DOMString any tokenId; - // nullable field of type Price - any assetPrice; + EVMAsset asset; }; dictionary ERC721ApprovalForAllData { EVMAmount amount; - EVMContract contract; - DOMString name; - EVMContract owner; - EVMContract spender; - DOMString symbol; - - // nullable field of type Price - any assetPrice; + EVMCounterparty owner; + EVMCounterparty spender; + EVMAsset asset; }; dictionary ERC1155TransferData { EVMAmount amount; - EVMContract contract; NFTMetadata metadata; - DOMString name; // nullable field of type DOMString any tokenId; - // nullable field of type Price - any assetPrice; + // nullable field of type EVMCounterparty + any counterparty; + + EVMAsset asset; }; dictionary ERC1155ApprovalForAllData { EVMAmount amount; - EVMContract contract; - EVMContract owner; - EVMContract spender; - // nullable field of type Price - any assetPrice; + EVMCounterparty owner; + EVMCounterparty spender; + EVMAsset asset; }; dictionary EVMStateChangeRawInfo { @@ -259,13 +254,19 @@ namespace simulation_responses { EVMStateChangeRawInfo rawInfo; }; - dictionary EVMSimulationResults { - EVMStateChange[] expectedStateChanges; + dictionary EVMAggregatedSimulationResults { + // object with key being the account address and value being of type + // EVMStateChange[]. + any expectedStateChanges; // nullable field of type EVMError any error; }; + dictionary EVMSimulationResults { + EVMAggregatedSimulationResults aggregated; + }; + dictionary EVMSimulationResponse { DOMString action; // BLOCK | WARN | NONE Warning[] warnings; @@ -352,47 +353,43 @@ namespace simulation_responses { DOMString humanReadableError; }; - dictionary SOLTransferData { + dictionary SolanaAsset { DOMString symbol; DOMString name; + DOMString? mint; DOMString decimals; + any? imageUrl; + any price; + + // Optional field of type MetaplexTokenStandardKind. We set the type to any + // because optional enums are not yet supported by the IDL compiler. + any? metaplexTokenStandard; + }; + + dictionary SOLTransferData { + SolanaAsset asset; SolanaDiff diff; }; dictionary SPLTransferData { - DOMString symbol; - DOMString name; - DOMString mint; - DOMString decimals; + SolanaAsset asset; SolanaDiff diff; - DOMString supply; - MetaplexTokenStandardKind metaplexTokenStandard; - // nullable field of type SolanaPrice - any assetPrice; + // nullable field of type DOMString + any counterparty; }; dictionary SPLApprovalData { DOMString delegate; - DOMString mint; - DOMString symbol; - DOMString name; - DOMString decimals; SolanaDiff diff; - DOMString supply; - MetaplexTokenStandardKind metaplexTokenStandard; - - // nullable field of type SolanaPrice - any assetPrice; + SolanaAsset asset; }; dictionary SOLStakeAuthorityChangeData { DOMString stakeAccount; - SolanaStakeAuthorities currAuthorities; + SolanaStakeAuthorities currentAuthorities; SolanaStakeAuthorities futureAuthorities; - DOMString symbol; - DOMString name; - DOMString decimals; + SolanaAsset asset; DOMString solStaked; }; @@ -404,6 +401,8 @@ namespace simulation_responses { // - SPLTransferData // - SPLApprovalData // - SOLStakeAuthorityChangeData + // - BFPLoaderAuthorityChangeData + // - CompressedNFTTransferData any data; }; @@ -413,17 +412,20 @@ namespace simulation_responses { SolanaStateChangeRawInfo rawInfo; }; - dictionary SolanaSimulationResults { - SolanaStateChange[] expectedStateChanges; + dictionary SolanaAggregatedSimulationResults { + DOMString action; // BLOCK | WARN | NONE + Warning[] warnings; + + // object with key being the account address and value being of type + // SolanaStateChange[]. + any expectedStateChanges; // nullable field of type SolanaError any error; }; dictionary SolanaSimulationResponse { - DOMString action; // BLOCK | WARN | NONE - Warning[] warnings; - SolanaSimulationResults simulationResults; + SolanaAggregatedSimulationResults aggregated; }; dictionary HTTPError { diff --git a/components/brave_wallet/browser/simulation_service.cc b/components/brave_wallet/browser/simulation_service.cc index 26240409dcd3..4123a43186ed 100644 --- a/components/brave_wallet/browser/simulation_service.cc +++ b/components/brave_wallet/browser/simulation_service.cc @@ -86,6 +86,9 @@ std::string GetRelativeScanPath(const std::string& chain_id, } else if (coin == mojom::CoinType::ETH && chain_id == mojom::kArbitrumMainnetChainId) { return "arbitrum/v0/one/scan"; + } else if (coin == mojom::CoinType::ETH && + chain_id == mojom::kBaseMainnetChainId) { + return "base/v0/mainnet/scan"; } return ""; @@ -103,7 +106,9 @@ bool HasTransactionScanSupportInternal(const std::string& chain_id, (coin == mojom::CoinType::ETH && chain_id == mojom::kBinanceSmartChainMainnetChainId) || (coin == mojom::CoinType::ETH && - chain_id == mojom::kArbitrumMainnetChainId); + chain_id == mojom::kArbitrumMainnetChainId) || + (coin == mojom::CoinType::ETH && + chain_id == mojom::kBaseMainnetChainId); } bool HasMessageScanSupportInternal(const std::string& chain_id, @@ -148,11 +153,10 @@ void SimulationService::Bind( GURL SimulationService::GetScanTransactionURL(const std::string& chain_id, mojom::CoinType coin, const std::string& language) { + DCHECK(coin == mojom::CoinType::SOL || coin == mojom::CoinType::ETH); std::string spec = base::StringPrintf( "%s/%s/%s", kBlowfishBaseAPIURL, - GetRelativeScanPath(chain_id, coin).c_str(), - coin == mojom::CoinType::SOL ? "transactions" : "transaction"); - + GetRelativeScanPath(chain_id, coin).c_str(), "transactions"); return net::AppendQueryParameter(GURL(spec), "language", language); } @@ -215,29 +219,30 @@ void SimulationService::ScanSolanaTransaction( return; } - const auto& encoded_params = solana::EncodeScanTransactionParams(request); - if (!encoded_params) { + const auto& params = solana::EncodeScanTransactionParams(request); + if (!params) { std::move(callback).Run( nullptr, "", l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR)); return; } - auto internal_callback = - base::BindOnce(&SimulationService::OnScanSolanaTransaction, - weak_ptr_factory_.GetWeakPtr(), std::move(callback)); + auto internal_callback = base::BindOnce( + &SimulationService::OnScanSolanaTransaction, + weak_ptr_factory_.GetWeakPtr(), std::move(callback), params->second); auto conversion_callback = base::BindOnce(&ConvertAllNumbersToString); api_request_helper_.Request( net::HttpRequestHeaders::kPostMethod, GetScanTransactionURL(chain_id, mojom::CoinType::SOL, language), - *encoded_params, "application/json", std::move(internal_callback), + params->first, "application/json", std::move(internal_callback), GetHeaders(), {.auto_retry_on_network_change = true}, std::move(conversion_callback)); } void SimulationService::OnScanSolanaTransaction( ScanSolanaTransactionCallback callback, + const std::string& user_account, APIRequestResult api_request_result) { if (!api_request_result.Is2XXResponseCode()) { if (auto error_response = @@ -251,8 +256,8 @@ void SimulationService::OnScanSolanaTransaction( return; } - if (auto simulation_response = - solana::ParseSimulationResponse(api_request_result.value_body())) { + if (auto simulation_response = solana::ParseSimulationResponse( + api_request_result.value_body(), user_account)) { std::move(callback).Run(std::move(simulation_response), "", ""); } else { std::move(callback).Run(nullptr, "", @@ -279,29 +284,30 @@ void SimulationService::ScanEVMTransaction( return; } - const auto& encoded_params = evm::EncodeScanTransactionParams(tx_info); - if (!encoded_params) { + const auto& params = evm::EncodeScanTransactionParams(tx_info); + if (!params) { std::move(callback).Run( nullptr, "", l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR)); return; } - auto internal_callback = - base::BindOnce(&SimulationService::OnScanEVMTransaction, - weak_ptr_factory_.GetWeakPtr(), std::move(callback)); + auto internal_callback = base::BindOnce( + &SimulationService::OnScanEVMTransaction, weak_ptr_factory_.GetWeakPtr(), + std::move(callback), params->second); auto conversion_callback = base::BindOnce(&ConvertAllNumbersToString); api_request_helper_.Request( net::HttpRequestHeaders::kPostMethod, GetScanTransactionURL(chain_id, mojom::CoinType::ETH, language), - *encoded_params, "application/json", std::move(internal_callback), + params->first, "application/json", std::move(internal_callback), GetHeaders(), {.auto_retry_on_network_change = true}, std::move(conversion_callback)); } void SimulationService::OnScanEVMTransaction( ScanEVMTransactionCallback callback, + const std::string& user_account, APIRequestResult api_request_result) { if (!api_request_result.Is2XXResponseCode()) { if (auto error_response = @@ -315,8 +321,8 @@ void SimulationService::OnScanEVMTransaction( return; } - if (auto simulation_response = - evm::ParseSimulationResponse(api_request_result.value_body())) { + if (auto simulation_response = evm::ParseSimulationResponse( + api_request_result.value_body(), user_account)) { std::move(callback).Run(std::move(simulation_response), "", ""); } else { std::move(callback).Run(nullptr, "", diff --git a/components/brave_wallet/browser/simulation_service.h b/components/brave_wallet/browser/simulation_service.h index 1aea98bd1ace..6bb89c2b9610 100644 --- a/components/brave_wallet/browser/simulation_service.h +++ b/components/brave_wallet/browser/simulation_service.h @@ -69,9 +69,11 @@ class SimulationService : public KeyedService, public mojom::SimulationService { private: void OnScanEVMTransaction(ScanEVMTransactionCallback callback, + const std::string& user_account, APIRequestResult api_request_result); void OnScanSolanaTransaction(ScanSolanaTransactionCallback callback, + const std::string& user_account, APIRequestResult api_request_result); api_request_helper::APIRequestHelper api_request_helper_; diff --git a/components/brave_wallet/browser/simulation_service_unittest.cc b/components/brave_wallet/browser/simulation_service_unittest.cc index 296004828380..c5081ead45bc 100644 --- a/components/brave_wallet/browser/simulation_service_unittest.cc +++ b/components/brave_wallet/browser/simulation_service_unittest.cc @@ -177,31 +177,37 @@ TEST_F(SimulationServiceUnitTest, GetScanTransactionURL) { mojom::kMainnetChainId, mojom::CoinType::ETH, "en-US"); EXPECT_EQ(url, "https://blowfish.wallet.brave.com/ethereum/v0/mainnet/scan/" - "transaction?language=en-US"); + "transactions?language=en-US"); url = simulation_service_->GetScanTransactionURL( mojom::kGoerliChainId, mojom::CoinType::ETH, "en-US"); EXPECT_EQ(url, "https://blowfish.wallet.brave.com/ethereum/v0/goerli/scan/" - "transaction?language=en-US"); + "transactions?language=en-US"); url = simulation_service_->GetScanTransactionURL( mojom::kPolygonMainnetChainId, mojom::CoinType::ETH, "en-US"); EXPECT_EQ(url, "https://blowfish.wallet.brave.com/polygon/v0/mainnet/scan/" - "transaction?language=en-US"); + "transactions?language=en-US"); url = simulation_service_->GetScanTransactionURL( mojom::kBinanceSmartChainMainnetChainId, mojom::CoinType::ETH, "en-US"); EXPECT_EQ(url, "https://blowfish.wallet.brave.com/bnb/v0/mainnet/scan/" - "transaction?language=en-US"); + "transactions?language=en-US"); url = simulation_service_->GetScanTransactionURL( mojom::kArbitrumMainnetChainId, mojom::CoinType::ETH, "en-US"); EXPECT_EQ(url, "https://blowfish.wallet.brave.com/arbitrum/v0/one/scan/" - "transaction?language=en-US"); + "transactions?language=en-US"); + + url = simulation_service_->GetScanTransactionURL( + mojom::kBaseMainnetChainId, mojom::CoinType::ETH, "en-US"); + EXPECT_EQ(url, + "https://blowfish.wallet.brave.com/base/v0/mainnet/scan/" + "transactions?language=en-US"); url = simulation_service_->GetScanTransactionURL( mojom::kSolanaMainnet, mojom::CoinType::SOL, "en-US"); @@ -254,48 +260,51 @@ TEST_F(SimulationServiceUnitTest, GetScanMessageURL) { "message?language=en-US"); } -TEST_F(SimulationServiceUnitTest, ScanEVMTransactionValidResponse) { +TEST_F(SimulationServiceUnitTest, ScanEvmTransactionValidResponse) { SetInterceptor(R"( { - "action": "NONE", - "simulationResults": { - "error": null, - "gas": { - "gasLimit": null - }, - "expectedStateChanges": [ - { - "humanReadableDiff": "Send 1 ETH", - "rawInfo": { - "data": { - "amount": { - "after": "1182957389356504134754", - "before": "1183957389356504134754" - }, - "contract": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "kind": "ACCOUNT" - }, - "asset": { - "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "decimals": 18, - "imageUrl": "https://example.com/eth.png", - "name": "Ether", - "price": { - "dollarValuePerToken": "1968.47", - "source": "Coingecko", - "updatedAt": "1670324557" - }, - "symbol": "ETH", - "verified": true + "requestId":"e8cd35ce-f743-4ef2-8e94-f26857744db7", + "action":"NONE", + "simulationResults":{ + "aggregated":{ + "error":null, + "userAccount":"0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4", + "expectedStateChanges":{ + "0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4":[ + { + "humanReadableDiff":"Send 0.033 ETH", + "rawInfo":{ + "kind":"NATIVE_ASSET_TRANSFER", + "data":{ + "amount":{ + "after":"71057321770366572", + "before":"104057321770366572" + }, + "asset":{ + "address":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "symbol":"ETH", + "name":"Ether", + "decimals":"18", + "verified":true, + "imageUrl":"https://eth.png", + "price":{ + "source":"Coingecko", + "updatedAt":"1681958792", + "dollarValuePerToken":"1945.92" + } + }, + "counterparty":{ + "kind":"ACCOUNT", + "address":"0x06924592cdf28acd3c1d23c37875c6c6a667bdf7" + } + } } - }, - "kind": "NATIVE_ASSET_TRANSFER" - } + } + ] } - ] + } }, - "warnings": [] + "warnings":[] } )"); @@ -306,44 +315,37 @@ TEST_F(SimulationServiceUnitTest, ScanEVMTransactionValidResponse) { const std::string& error_response, const std::string& error_string) { ASSERT_TRUE(response); + EXPECT_EQ(response->action, mojom::BlowfishSuggestedAction::kNone); EXPECT_EQ(response->warnings.size(), 0u); - EXPECT_FALSE(response->simulation_results->error); - ASSERT_EQ(response->simulation_results->expected_state_changes.size(), - 1u); - - const auto& state_change = - response->simulation_results->expected_state_changes.at(0).Clone(); - EXPECT_EQ(state_change->human_readable_diff, "Send 1 ETH"); - EXPECT_EQ(state_change->raw_info->kind, + EXPECT_FALSE(response->error); + ASSERT_EQ(response->expected_state_changes.size(), 1u); + const auto& state_change_0 = response->expected_state_changes.at(0); + EXPECT_EQ(state_change_0->human_readable_diff, "Send 0.033 ETH"); + EXPECT_EQ(state_change_0->raw_info->kind, mojom::BlowfishEVMRawInfoKind::kNativeAssetTransfer); ASSERT_TRUE( - state_change->raw_info->data->is_native_asset_transfer_data()); - const auto& state_change_raw_info = - state_change->raw_info->data->get_native_asset_transfer_data(); - - EXPECT_EQ(state_change_raw_info->amount->after, - "1182957389356504134754"); - EXPECT_EQ(state_change_raw_info->amount->before, - "1183957389356504134754"); - EXPECT_EQ(state_change_raw_info->contract->address, - "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - EXPECT_EQ(state_change_raw_info->contract->kind, - mojom::BlowfishEVMAddressKind::kAccount); - EXPECT_EQ(state_change_raw_info->asset->address, + state_change_0->raw_info->data->is_native_asset_transfer_data()); + const auto& state_change_0_raw_info = + state_change_0->raw_info->data->get_native_asset_transfer_data(); + + EXPECT_EQ(state_change_0_raw_info->amount->after, "71057321770366572"); + EXPECT_EQ(state_change_0_raw_info->amount->before, + "104057321770366572"); + EXPECT_EQ(state_change_0_raw_info->asset->address, "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - EXPECT_EQ(state_change_raw_info->asset->decimals, 18); - EXPECT_EQ(state_change_raw_info->asset->image_url, - "https://example.com/eth.png"); - EXPECT_EQ(state_change_raw_info->asset->name, "Ether"); - EXPECT_EQ(state_change_raw_info->asset->price->dollar_value_per_token, - "1968.47"); - EXPECT_EQ(state_change_raw_info->asset->price->source, + EXPECT_EQ(state_change_0_raw_info->asset->decimals, 18); + EXPECT_EQ(state_change_0_raw_info->asset->image_url, "https://eth.png"); + EXPECT_EQ(state_change_0_raw_info->asset->name, "Ether"); + EXPECT_EQ(state_change_0_raw_info->asset->symbol, "ETH"); + EXPECT_TRUE(state_change_0_raw_info->asset->verified); + EXPECT_EQ(state_change_0_raw_info->asset->lists.size(), 0u); + EXPECT_EQ(state_change_0_raw_info->asset->price->dollar_value_per_token, + "1945.92"); + EXPECT_EQ(state_change_0_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kCoingecko); - EXPECT_EQ(state_change_raw_info->asset->price->last_updated_at, - "1670324557"); - EXPECT_EQ(state_change_raw_info->asset->symbol, "ETH"); - EXPECT_TRUE(state_change_raw_info->asset->verified); + EXPECT_EQ(state_change_0_raw_info->asset->price->last_updated_at, + "1681958792"); EXPECT_EQ(error_response, ""); EXPECT_EQ(error_string, ""); @@ -438,45 +440,42 @@ TEST_F(SimulationServiceUnitTest, ScanEVMTransactionNullParams) { TEST_F(SimulationServiceUnitTest, ScanSolanaTransactionValid) { SetInterceptor(R"( { - "status": "CHECKS_PASSED", - "action": "NONE", - "warnings": [], - "simulationResults": { - "isRecentBlockhashExpired": false, - "expectedStateChanges": [ - { - "humanReadableDiff": "Send 2 USDT", - "suggestedColor": "DEBIT", - "rawInfo": { - "kind": "SPL_TRANSFER", - "data": { - "symbol": "USDT", - "name": "USDT", - "mint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", - "decimals": "6", - "supply": "1000000000", - "metaplexTokenStandard": "unknown", - "assetPrice": { - "source": "Coingecko", - "last_updated_at": "1679331222", - "dollar_value_per_token": "0.99" - }, - "diff": { - "sign": "MINUS", - "digits": "2000000" + "aggregated": { + "action": "NONE", + "warnings": [], + "expectedStateChanges": { + "BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8": [ + { + "humanReadableDiff": "Send 2 USDT", + "suggestedColor": "DEBIT", + "rawInfo": { + "kind": "SPL_TRANSFER", + "data": { + "asset": { + "symbol": "USDT", + "name": "USDT", + "mint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", + "decimals": "6", + "supply": "1000000000", + "metaplexTokenStandard": "unknown", + "price": { + "source": "Coingecko", + "updatedAt": "1679331222", + "dollarValuePerToken": "0.99" + }, + "imageUrl": "https://usdt.png" + }, + "diff": { + "sign": "MINUS", + "digits": "2000000" + }, + "counterparty": "5wytVPbjLb2VCXbynhUQabEZZD2B6Wxrkvwm6v6Cuy5X" } } } - } - ], - "error": null, - "raw": { - "err": null, - "logs": [], - "accounts": [], - "returnData": null, - "unitsConsumed": 148013 - } + ] + }, + "error": null } } )"); @@ -497,39 +496,37 @@ TEST_F(SimulationServiceUnitTest, ScanSolanaTransactionValid) { EXPECT_EQ(response->action, mojom::BlowfishSuggestedAction::kNone); EXPECT_EQ(response->warnings.size(), 0u); - EXPECT_FALSE(response->simulation_results->error); - EXPECT_FALSE(response->simulation_results->is_recent_blockhash_expired); - ASSERT_EQ(response->simulation_results->expected_state_changes.size(), - 1u); + EXPECT_FALSE(response->error); + ASSERT_EQ(response->expected_state_changes.size(), 1u); - const auto& state_change = - response->simulation_results->expected_state_changes.at(0); + const auto& state_change = response->expected_state_changes.at(0); EXPECT_EQ(state_change->human_readable_diff, "Send 2 USDT"); EXPECT_EQ(state_change->suggested_color, mojom::BlowfishSuggestedColor::kDebit); EXPECT_EQ(state_change->raw_info->kind, mojom::BlowfishSolanaRawInfoKind::kSplTransfer); ASSERT_TRUE(state_change->raw_info->data->is_spl_transfer_data()); - const auto& state_change_raw_info = state_change->raw_info->data->get_spl_transfer_data(); - EXPECT_EQ(state_change_raw_info->symbol, "USDT"); - EXPECT_EQ(state_change_raw_info->name, "USDT"); - EXPECT_EQ(state_change_raw_info->mint, + EXPECT_EQ(state_change_raw_info->asset->symbol, "USDT"); + EXPECT_EQ(state_change_raw_info->asset->name, "USDT"); + EXPECT_EQ(state_change_raw_info->asset->mint, "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); - EXPECT_EQ(state_change_raw_info->decimals, 6); - EXPECT_EQ(state_change_raw_info->supply, 1000000000ULL); - EXPECT_EQ(state_change_raw_info->metaplex_token_standard, + EXPECT_EQ(state_change_raw_info->asset->decimals, 6); + EXPECT_EQ(state_change_raw_info->asset->metaplex_token_standard, mojom::BlowfishMetaplexTokenStandardKind::kUnknown); - EXPECT_EQ(state_change_raw_info->asset_price->source, + ASSERT_TRUE(state_change_raw_info->asset->price); + EXPECT_EQ(state_change_raw_info->asset->price->source, mojom::BlowfishAssetPriceSource::kCoingecko); - EXPECT_EQ(state_change_raw_info->asset_price->last_updated_at, + EXPECT_EQ(state_change_raw_info->asset->price->last_updated_at, "1679331222"); - EXPECT_EQ(state_change_raw_info->asset_price->dollar_value_per_token, + EXPECT_EQ(state_change_raw_info->asset->price->dollar_value_per_token, "0.99"); EXPECT_EQ(state_change_raw_info->diff->sign, mojom::BlowfishDiffSign::kMinus); EXPECT_EQ(state_change_raw_info->diff->digits, 2000000ULL); + EXPECT_EQ(state_change_raw_info->counterparty, + "5wytVPbjLb2VCXbynhUQabEZZD2B6Wxrkvwm6v6Cuy5X"); EXPECT_EQ(error_response, ""); EXPECT_EQ(error_string, ""); diff --git a/components/brave_wallet/common/brave_wallet.mojom b/components/brave_wallet/common/brave_wallet.mojom index 2089d01d2c0a..997b651ad543 100644 --- a/components/brave_wallet/common/brave_wallet.mojom +++ b/components/brave_wallet/common/brave_wallet.mojom @@ -2298,12 +2298,9 @@ enum BlowfishWarningKind { kPermitUnlimitedAllowance, kPermitNoExpiration, kEthSignTxHash, - kObfuscatedCode, - kDevtoolsDisabled, kBlocklistedDomainCrossOrigin, kWhitelistedDomainCrossOrigin, kTooManyTransactions, - kNonAsciiUrl, kCompromisedAuthorityUpgrade, kPoisonedAddress, kApprovalToEOA, @@ -2314,7 +2311,6 @@ enum BlowfishWarningKind { kNewDomain, kUnusualGasConsumption, kReferencedOfacAddress, - kMainnetReplayPossible, kUnknown = -1 }; @@ -2463,7 +2459,7 @@ struct BlowfishEVMError { string human_readable_error; }; -struct BlowfishEVMContract { +struct BlowfishEVMCounterparty { string address; BlowfishEVMAddressKind kind; }; @@ -2475,12 +2471,37 @@ struct BlowfishEVMAmount { struct BlowfishEVMAsset { string address; + + // The symbol field is defined in the following cases: + // - ERC20: always defined. + // - ERC721: may not be defined as it's not required in the standard. + // - ERC1155: never defined as it's not part of the standard. string symbol; + + // The name field is defined in the following cases: + // - ERC20: always defined. + // - ERC721: may not be defined as it's not required in the standard. + // - ERC1155: may not be defined as it's not required in the standard. string name; + + // For ERC721 and ERC1155, decimals will always be 0, since each token is + // distinct and cannot be partitioned into smaller units. int32 decimals; + + // The token_id field is defined only for ERC721 and ERC1155 tokens. In some + // edge cases where Blowfish is unable to parse the value, it may be empty. + string token_id; + + // The collection field is defined only for ERC721. + string collection; + bool verified; array lists; - string? image_url; + + // This field is only used for ERC20 tokens. For NFT images, please use the + // corresponding BlowfishEVMMetadata field. + string image_url; + BlowfishPrice? price; }; @@ -2489,72 +2510,58 @@ struct BlowfishEVMMetadata { }; struct BlowfishERC20TransferData { - BlowfishEVMContract contract; BlowfishEVMAmount amount; + BlowfishEVMCounterparty? counterparty; BlowfishEVMAsset asset; }; struct BlowfishERC20ApprovalData { - BlowfishEVMContract contract; - BlowfishEVMContract owner; - BlowfishEVMContract spender; + BlowfishEVMCounterparty owner; + BlowfishEVMCounterparty spender; BlowfishEVMAmount amount; BlowfishEVMAsset asset; }; struct BlowfishNativeAssetTransferData { BlowfishEVMAmount amount; + BlowfishEVMCounterparty? counterparty; BlowfishEVMAsset asset; - BlowfishEVMContract contract; }; struct BlowfishERC721TransferData { BlowfishEVMAmount amount; - BlowfishEVMContract contract; + BlowfishEVMCounterparty? counterparty; BlowfishEVMMetadata metadata; - string name; - string symbol; - string? token_id; - BlowfishPrice? asset_price; + BlowfishEVMAsset asset; }; struct BlowfishERC721ApprovalData { BlowfishEVMAmount amount; - BlowfishEVMContract contract; BlowfishEVMMetadata metadata; - string name; - BlowfishEVMContract owner; - BlowfishEVMContract spender; - string symbol; - string? token_id; - BlowfishPrice? asset_price; + BlowfishEVMCounterparty owner; + BlowfishEVMCounterparty spender; + BlowfishEVMAsset asset; }; struct BlowfishERC721ApprovalForAllData { BlowfishEVMAmount amount; - BlowfishEVMContract contract; - string name; - BlowfishEVMContract owner; - BlowfishEVMContract spender; - string symbol; - BlowfishPrice? asset_price; + BlowfishEVMCounterparty owner; + BlowfishEVMCounterparty spender; + BlowfishEVMAsset asset; }; struct BlowfishERC1155TransferData { BlowfishEVMAmount amount; - BlowfishEVMContract contract; BlowfishEVMMetadata metadata; - string name; - string? token_id; - BlowfishPrice? asset_price; + BlowfishEVMCounterparty? counterparty; + BlowfishEVMAsset asset; }; struct BlowfishERC1155ApprovalForAllData { BlowfishEVMAmount amount; - BlowfishEVMContract contract; - BlowfishEVMContract owner; - BlowfishEVMContract spender; - BlowfishPrice? asset_price; + BlowfishEVMCounterparty owner; + BlowfishEVMCounterparty spender; + BlowfishEVMAsset asset; }; union BlowfishEVMStateChangeRawInfoDataUnion { @@ -2578,15 +2585,11 @@ struct BlowfishEVMStateChange { BlowfishEVMStateChangeRawInfo raw_info; }; -struct EVMSimulationResults { - BlowfishEVMError? error; - array expected_state_changes; -}; - struct EVMSimulationResponse { BlowfishSuggestedAction action; - EVMSimulationResults simulation_results; + BlowfishEVMError? error; array warnings; + array expected_state_changes; }; // Blowfish types specific to Solana. @@ -2605,43 +2608,45 @@ struct BlowfishSolanaStakeAuthorities { string withdrawer; }; -struct BlowfishSOLTransferData { +struct BlowfishSolanaAsset { string symbol; string name; + string mint; int32 decimals; + string image_url; + BlowfishPrice? price; + + // Blowfish API may return a null or missing value for this field if the token + // does not conform to the Metaplex Token Standard. In all such cases, the + // value of this field is set to kUnknown and should be ignored. + // + // The choice of using kUnknown is intentional, since Mojo does not allow + // enums to be optional. + BlowfishMetaplexTokenStandardKind metaplex_token_standard; +}; + +struct BlowfishSOLTransferData { + BlowfishSolanaAsset asset; BlowfishSolanaDiff diff; }; struct BlowfishSPLTransferData { - string symbol; - string name; - string mint; - int32 decimals; + BlowfishSolanaAsset asset; BlowfishSolanaDiff diff; - uint64 supply; - BlowfishMetaplexTokenStandardKind metaplex_token_standard; - BlowfishPrice? asset_price; + string counterparty; }; struct BlowfishSPLApprovalData { string delegate; - string mint; - string symbol; - string name; - int32 decimals; + BlowfishSolanaAsset asset; BlowfishSolanaDiff diff; - uint64 supply; - BlowfishMetaplexTokenStandardKind metaplex_token_standard; - BlowfishPrice? asset_price; }; struct BlowfishSOLStakeAuthorityChangeData { string stake_account; - BlowfishSolanaStakeAuthorities curr_authorities; + BlowfishSolanaStakeAuthorities current_authorities; BlowfishSolanaStakeAuthorities future_authorities; - string symbol; - string name; - int32 decimals; + BlowfishSolanaAsset asset; uint64 sol_staked; }; @@ -2663,16 +2668,11 @@ struct BlowfishSolanaStateChange { BlowfishSolanaStateChangeRawInfo raw_info; }; -struct SolanaSimulationResults { - bool is_recent_blockhash_expired; - array expected_state_changes; - BlowfishSolanaError? error; -}; - struct SolanaSimulationResponse { BlowfishSuggestedAction action; array warnings; - SolanaSimulationResults simulation_results; + BlowfishSolanaError? error; + array expected_state_changes; }; // Implements transaction and message scanning functionality through the diff --git a/components/brave_wallet_ui/common/constants/mocks.ts b/components/brave_wallet_ui/common/constants/mocks.ts index 0736b580de32..63106345b8a4 100644 --- a/components/brave_wallet_ui/common/constants/mocks.ts +++ b/components/brave_wallet_ui/common/constants/mocks.ts @@ -690,6 +690,8 @@ export const BlowfishEVMAssets = { 'https://d1ts37qlq4uz4s.cloudfront.net/evm__evm%3A%3Aethereum__' + 'evm%3A%3Aethereum%3A%3Amainnet__' + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png', + tokenId: '', + collection: '', lists: [], name: mockEthToken.name, price: { @@ -707,7 +709,9 @@ export const BlowfishEVMAssets = { decimals: 18, verified: false, lists: [], - imageUrl: undefined, + imageUrl: '', + tokenId: '', + collection: '', price: mockBlowfishAssetPrice } as BraveWallet.BlowfishEVMAsset, mainnetDai: { @@ -721,6 +725,8 @@ export const BlowfishEVMAssets = { 'https://d1ts37qlq4uz4s.cloudfront.net/evm__' + 'evm%3A%3Aethereum__evm%3A%3Aethereum%3A%3Amainnet__' + '0x6b175474e89094c44da98b954eedeac495271d0f.png', + tokenId: '', + collection: '', price: { source: BraveWallet.BlowfishAssetPriceSource.kCoingecko, lastUpdatedAt: '1679331222', @@ -738,18 +744,83 @@ export const BlowfishEVMAssets = { 'https://d1ts37qlq4uz4s.cloudfront.net' + '/evm__evm%3A%3Aethereum__evm%3A%3Aethereum%3A%3Amainnet__' + '0xdac17f958d2ee523a2206206994597c13d831ec7.png', + tokenId: '', + collection: '', price: { source: BraveWallet.BlowfishAssetPriceSource.kCoingecko, lastUpdatedAt: '1679331222', dollarValuePerToken: '0.99' } + } as BraveWallet.BlowfishEVMAsset, + moonCat: { + address: mockMoonCatNFT.contractAddress, + name: mockMoonCatNFT.name, + decimals: 0, + lists: [], + symbol: mockMoonCatNFT.symbol, + verified: false, + imageUrl: '', + tokenId: mockMoonCatNFT.tokenId, + collection: 'MoonCat', + price: { + source: BraveWallet.BlowfishAssetPriceSource.kSimplehash, + lastUpdatedAt: new Date().toUTCString(), + dollarValuePerToken: '572.50' + } + } as BraveWallet.BlowfishEVMAsset, + pudgyPenguins: { + address: '0xbd3531da5cf5857e7cfaa92426877b022e612cf8', + name: 'PudgyPenguins', + collection: 'PudgyPenguins', + symbol: 'PPG', + decimals: 0, + tokenId: '7238', + verified: false, + lists: [], + imageUrl: '', + price: { + source: BraveWallet.BlowfishAssetPriceSource.kSimplehash, + lastUpdatedAt: new Date().toUTCString(), + dollarValuePerToken: '594.99' + } + } as BraveWallet.BlowfishEVMAsset, + bayc: { + address: '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d', + name: 'BoredApeYachtClub', + collection: 'BoredApeYachtClub', + symbol: 'BAYC', + decimals: 0, + tokenId: '6603', + verified: false, + lists: [], + imageUrl: '', + price: { + source: BraveWallet.BlowfishAssetPriceSource.kSimplehash, + lastUpdatedAt: new Date().toUTCString(), + dollarValuePerToken: '7865.43' + } + } as BraveWallet.BlowfishEVMAsset, + corgi: { + address: '0x51e613727fdd2e0b91b51c3e5427e9440a7957e4', + tokenId: '1234', + collection: '', + symbol: '', + name: 'Corgi #1234', + decimals: 0, + verified: false, + lists: [], + imageUrl: '', + price: { + source: BraveWallet.BlowfishAssetPriceSource.kSimplehash, + lastUpdatedAt: new Date().toUTCString(), + dollarValuePerToken: '232.43' + } } as BraveWallet.BlowfishEVMAsset } as const const goerliLinkTransferData: BraveWallet.BlowfishERC20TransferData = { - contract: { - // Goerli LINK - address: '0x326c977e6efc84e512bb9c30f76e30c160ed06fb', + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, amount: { @@ -778,8 +849,8 @@ export const mockedReceiveDaiEvent: SafeERC20TransferEvent = { after: '557039306766411381864245', before: '555508493698012633714742' }, - contract: { - address: BlowfishEVMAssets.mainnetDai.address, + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, asset: BlowfishEVMAssets.mainnetDai @@ -798,8 +869,8 @@ export const mockSendEthEvent: SafeNativeTransferEvent = { after: '1182957389356504134754', before: '1183957389356504134754' }, - contract: { - address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, asset: BlowfishEVMAssets.mainnetETH @@ -819,10 +890,6 @@ export const mockApproveUsdtEvent: SafeERC20ApprovalEvent = { before: '0' }, asset: BlowfishEVMAssets.mainnetTetherUSD, - contract: { - address: '0xdac17f958d2ee523a2206206994597c13d831ec7', - kind: BraveWallet.BlowfishEVMAddressKind.kAccount - }, owner: { address: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045', kind: BraveWallet.BlowfishEVMAddressKind.kAccount @@ -845,8 +912,8 @@ export const mockReceiveNftEvent: SafeERC721TransferEvent = { after: '1', before: '0' }, - contract: { - address: '0xbd3531da5cf5857e7cfaa92426877b022e612cf8', + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, metadata: { @@ -854,14 +921,7 @@ export const mockReceiveNftEvent: SafeERC721TransferEvent = { 'https://cdn.simplehash.com/assets/' + '97e1c9e3e9eb21a1114351f9c5c14fe611c94916f360c4eb3aa9263afd8b837b.png' }, - name: 'PudgyPenguins', - symbol: 'PPG', - tokenId: '7238', - assetPrice: { - source: BraveWallet.BlowfishAssetPriceSource.kSimplehash, - lastUpdatedAt: '1679331222', - dollarValuePerToken: '594.99' - } + asset: BlowfishEVMAssets.pudgyPenguins } }, kind: BraveWallet.BlowfishEVMRawInfoKind.kErc721Transfer @@ -878,16 +938,11 @@ export const mockApproveBoredApeNftTransferEvent: SafeERC721ApprovalEvent = { after: '1', before: '0' }, - contract: { - address: '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d', - kind: BraveWallet.BlowfishEVMAddressKind.kAccount - }, metadata: { rawImageUrl: 'https://cdn.simplehash.com/assets/beca5f0f88c267276318' + 'edd8a6019b6b47327f42efd0ba22a3835e77f27732e5.png' }, - name: 'BoredApeYachtClub', owner: { address: '0xed2ab4948ba6a909a7751dec4f34f303eb8c7236', kind: BraveWallet.BlowfishEVMAddressKind.kAccount @@ -896,13 +951,7 @@ export const mockApproveBoredApeNftTransferEvent: SafeERC721ApprovalEvent = { address: '0x1e0049783f008a0085193e00003d00cd54003c71', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, - symbol: 'BAYC', - tokenId: '6603', - assetPrice: { - source: BraveWallet.BlowfishAssetPriceSource.kSimplehash, - lastUpdatedAt: '1679331222', - dollarValuePerToken: '7865.43' - } + asset: BlowfishEVMAssets.bayc } } } @@ -918,11 +967,6 @@ export const mockApproveAllBoredApeNFTsEvent: SafeERC721ApprovalForAllEvent = { after: '1157920892373161954235709850086879078532699846656405640394', before: '0' }, - contract: { - address: '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d', - kind: BraveWallet.BlowfishEVMAddressKind.kAccount - }, - name: 'BoredApeYachtClub', owner: { address: '0x38191ca1307ebf67ca1a7caf5346dbd91d882ca6', kind: BraveWallet.BlowfishEVMAddressKind.kAccount @@ -931,12 +975,7 @@ export const mockApproveAllBoredApeNFTsEvent: SafeERC721ApprovalForAllEvent = { address: '0x1e0049783f008a0085193e00003d00cd54003c71', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, - symbol: 'BAYC', - assetPrice: { - source: BraveWallet.BlowfishAssetPriceSource.kSimplehash, - lastUpdatedAt: '1679331222', - dollarValuePerToken: '7865.43' - } + asset: BlowfishEVMAssets.bayc } } } @@ -976,10 +1015,6 @@ export const mockEvmSimulatedResponse: SafeBlowfishEvmResponse = { before: '1' }, asset: BlowfishEVMAssets.goerliLink, - contract: { - address: BlowfishEVMAssets.goerliLink.address, - kind: BraveWallet.BlowfishEVMAddressKind.kAccount - }, owner: { address: mockAccount.address, kind: BraveWallet.BlowfishEVMAddressKind.kAccount @@ -994,8 +1029,8 @@ export const mockEvmSimulatedResponse: SafeBlowfishEvmResponse = { }, { humanReadableDiff: `Send ${ - mockErc721Token.name // - } #${mockErc721Token.tokenId}`, + BlowfishEVMAssets.pudgyPenguins.name // + } #${BlowfishEVMAssets.pudgyPenguins.tokenId}`, rawInfo: { kind: BraveWallet.BlowfishEVMRawInfoKind.kErc721Transfer, data: { @@ -1004,25 +1039,22 @@ export const mockEvmSimulatedResponse: SafeBlowfishEvmResponse = { after: '0', before: '1' }, - assetPrice: mockBlowfishAssetPrice, - contract: { - address: mockErc721Token.contractAddress, + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, metadata: { rawImageUrl: mockNFTMetadata[0].imageURL || '' }, - name: mockErc721Token.name, - symbol: mockErc721Token.symbol, - tokenId: mockErc721Token.tokenId + asset: BlowfishEVMAssets.pudgyPenguins } } } }, { humanReadableDiff: `Send ${ - mockErc721Token.symbol // - } #${mockErc721Token.tokenId}`, + BlowfishEVMAssets.bayc.symbol // + } #${BlowfishEVMAssets.bayc.tokenId}`, rawInfo: { kind: BraveWallet.BlowfishEVMRawInfoKind.kErc1155Transfer, data: { @@ -1031,20 +1063,14 @@ export const mockEvmSimulatedResponse: SafeBlowfishEvmResponse = { after: '0', before: '1' }, - assetPrice: { - dollarValuePerToken: '100', // $100 - lastUpdatedAt: new Date().toUTCString(), - source: BraveWallet.BlowfishAssetPriceSource.kCoingecko - }, - contract: { - address: mockErc721Token.contractAddress, + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, metadata: { rawImageUrl: mockNFTMetadata[0].imageURL || '' }, - tokenId: mockErc721Token.tokenId, - name: mockErc721Token.name + asset: BlowfishEVMAssets.bayc } } } @@ -1060,8 +1086,8 @@ export const mockEvmSimulatedResponse: SafeBlowfishEvmResponse = { before: '1000000000000000000' }, asset: BlowfishEVMAssets.mainnetETH, - contract: { - address: '', + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount } } @@ -1070,8 +1096,8 @@ export const mockEvmSimulatedResponse: SafeBlowfishEvmResponse = { }, { humanReadableDiff: `Approve ${ - mockMoonCatNFT.name // - } #${mockMoonCatNFT.tokenId}`, + BlowfishEVMAssets.moonCat.name // + } #${BlowfishEVMAssets.moonCat.tokenId}`, rawInfo: { kind: BraveWallet.BlowfishEVMRawInfoKind.kErc721Approval, data: { @@ -1080,19 +1106,9 @@ export const mockEvmSimulatedResponse: SafeBlowfishEvmResponse = { after: '0', before: '1' }, - assetPrice: { - dollarValuePerToken: '100', - lastUpdatedAt: new Date().toISOString(), - source: BraveWallet.BlowfishAssetPriceSource.kCoingecko - }, metadata: { rawImageUrl: mockMoonCatNFT.logo }, - contract: { - address: '', - kind: BraveWallet.BlowfishEVMAddressKind.kAccount - }, - name: mockMoonCatNFT.name, owner: { kind: BraveWallet.BlowfishEVMAddressKind.kAccount, address: mockAccount.address @@ -1101,8 +1117,7 @@ export const mockEvmSimulatedResponse: SafeBlowfishEvmResponse = { address: mockAccount.address, kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, - symbol: mockMoonCatNFT.symbol, - tokenId: mockMoonCatNFT.tokenId + asset: BlowfishEVMAssets.moonCat } } } @@ -1160,8 +1175,8 @@ export const mockSimulatedBuyNFTWithETH: SafeBlowfishEvmResponse = { after: '998426264937289938488', before: '1001607264937289938488' }, - contract: { - address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, asset: BlowfishEVMAssets.mainnetETH @@ -1217,8 +1232,8 @@ export const mockReceiveMultiStandardTokenEvent: SafeERC1155TransferEvent = { 'after': '1', 'before': '0' }, - contract: { - address: '0x51e613727fdd2e0b91b51c3e5427e9440a7957e4', + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, metadata: { @@ -1226,13 +1241,7 @@ export const mockReceiveMultiStandardTokenEvent: SafeERC1155TransferEvent = { 'https://cdn.simplehash.com/' + 'assets/4bedd702e7ea8c4a9d04d83302138fa5b63d0cca0f06df9b87bdb09cff253b88.png' }, - tokenId: '13014975', - assetPrice: { - source: BraveWallet.BlowfishAssetPriceSource.kSimplehash, - lastUpdatedAt: '1679331222', - dollarValuePerToken: '232.43' - }, - name: 'Corgi #1234' + asset: BlowfishEVMAssets.corgi, } } } @@ -1259,8 +1268,8 @@ export const mockSimulatedBuyERC1155Token: SafeBlowfishEvmResponse = { after: '71057321770366572', before: '104057321770366572' }, - contract: { - address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + counterparty: { + address: '0xa92D461a9a988A7f11ec285d39783A637Fdd6ba4', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, asset: BlowfishEVMAssets.mainnetETH @@ -1292,7 +1301,7 @@ export const mockEvmERC1155ApproveForAll: SafeBlowfishEvmResponse = { error: undefined, expectedStateChanges: [ { - humanReadableDiff: `Approve to transfer all your Sandbox's ASSETs`, + humanReadableDiff: `Approve to transfer all your Corgi's ASSETs`, rawInfo: { kind: BraveWallet.BlowfishEVMRawInfoKind.kErc1155ApprovalForAll, data: { @@ -1302,10 +1311,6 @@ export const mockEvmERC1155ApproveForAll: SafeBlowfishEvmResponse = { '1157920892373161954235709850086879078532699846656405640394', before: '0' }, - contract: { - address: '0xa342f5d851e866e18ff98f351f2c6637f4478db5', - kind: BraveWallet.BlowfishEVMAddressKind.kAccount - }, owner: { address: '0xed2ab4948ba6a909a7751dec4f34f303eb8c7236', kind: BraveWallet.BlowfishEVMAddressKind.kAccount @@ -1314,11 +1319,7 @@ export const mockEvmERC1155ApproveForAll: SafeBlowfishEvmResponse = { address: '0x00000000006c3852cbef3e08e8df289169ede581', kind: BraveWallet.BlowfishEVMAddressKind.kAccount }, - assetPrice: { - source: BraveWallet.BlowfishAssetPriceSource.kSimplehash, - lastUpdatedAt: '1679331222', - dollarValuePerToken: '232.43' - } + asset: BlowfishEVMAssets.corgi } } } @@ -1330,6 +1331,21 @@ export const mockEvmERC1155ApproveForAll: SafeBlowfishEvmResponse = { // // Solana SVM Simulations // +const BlowfishSolanaAssets = { + mainnetSol: { + name: 'Solana', + symbol: 'SOL', + mint: '', + decimals: 9, + imageUrl: 'https://d1ts37qlq4uz4s.cloudfront.net/solana__solana%3A%3Asolana__solana%3A%3Asolana%3A%3Amainnet__So11111111111111111111111111111111111111112.png', + price: { + source: BraveWallet.BlowfishAssetPriceSource.kCoingecko, + lastUpdatedAt: new Date().toUTCString(), + dollarValuePerToken: '98.54' + } + } as BraveWallet.BlowfishSolanaAsset, +} + export const mockReceiveSolSimulation: SafeBlowfishSolanaResponse = { action: BraveWallet.BlowfishSuggestedAction.kBlock, simulationResults: { @@ -1343,13 +1359,11 @@ export const mockReceiveSolSimulation: SafeBlowfishSolanaResponse = { kind: BraveWallet.BlowfishSolanaRawInfoKind.kSolTransfer, data: { solTransferData: { - decimals: 9, - symbol: 'SOL', + asset: BlowfishSolanaAssets.mainnetSol, diff: { digits: BigInt(500000), sign: BraveWallet.BlowfishDiffSign.kPlus - }, - name: 'Solana Native Token' + } } } } @@ -1372,7 +1386,7 @@ export const mockSolStakingChangeEvent: SafeSolanaStakeChangeEvent = { kind: BraveWallet.BlowfishSolanaRawInfoKind.kSolStakeAuthorityChange, data: { solStakeAuthorityChangeData: { - currAuthorities: { + currentAuthorities: { staker: mockSolanaAccount.address, withdrawer: mockSolanaAccount.address }, @@ -1380,11 +1394,9 @@ export const mockSolStakingChangeEvent: SafeSolanaStakeChangeEvent = { staker: mockSplNft.contractAddress, withdrawer: mockSplNft.contractAddress }, - decimals: mockSolanaMainnetNetwork.decimals, - name: mockSolanaMainnetNetwork.symbolName, + asset: BlowfishSolanaAssets.mainnetSol, solStaked: BigInt(5657), - stakeAccount: mockSolanaAccountInfo.address, - symbol: mockSolanaMainnetNetwork.symbol + stakeAccount: mockSolanaAccountInfo.address } } } diff --git a/components/brave_wallet_ui/components/extension/confirm-transaction-panel/common/evm_state_changes.tsx b/components/brave_wallet_ui/components/extension/confirm-transaction-panel/common/evm_state_changes.tsx index 999cb6e69357..38b1a33c6175 100644 --- a/components/brave_wallet_ui/components/extension/confirm-transaction-panel/common/evm_state_changes.tsx +++ b/components/brave_wallet_ui/components/extension/confirm-transaction-panel/common/evm_state_changes.tsx @@ -185,20 +185,20 @@ export const NonFungibleErcTokenTransfer = ({ transfer: Pick< | BraveWallet.BlowfishERC721TransferData | BraveWallet.BlowfishERC1155TransferData, - 'amount' | 'contract' | 'metadata' | 'name' | 'tokenId' + 'amount' | 'counterparty' | 'metadata' | 'asset' > }): JSX.Element => { // memos const asset: IconAsset = React.useMemo(() => { return { chainId: network.chainId, - contractAddress: transfer.contract.address, - isErc721: !!transfer.tokenId, - isNft: !!transfer.tokenId, + contractAddress: transfer.asset.address, + isErc721: !!transfer.asset.tokenId, + isNft: !!transfer.asset.tokenId, logo: transfer.metadata.rawImageUrl || '', - name: transfer.name, - symbol: transfer.name, - tokenId: transfer.tokenId || '' + name: transfer.asset.name, + symbol: transfer.asset.symbol, + tokenId: transfer.asset.tokenId || '' } }, [transfer, network.chainId]) @@ -206,8 +206,8 @@ export const NonFungibleErcTokenTransfer = ({ const isReceive = new Amount(transfer.amount.after) // .gt(transfer.amount.before) - const tokenIdNumber = transfer.tokenId - ? new Amount(transfer.tokenId).toNumber() + const tokenIdNumber = transfer.asset.tokenId + ? new Amount(transfer.asset.tokenId).toNumber() : undefined // render @@ -273,7 +273,7 @@ export const NonFungibleErcTokenTransfer = ({ @@ -297,7 +297,7 @@ export const ErcTokenApproval = ({ | { isERC20?: false isApprovalForAll?: boolean - approval: Pick + approval: Pick } | { isERC20: true @@ -314,7 +314,7 @@ export const ErcTokenApproval = ({ approval as | BraveWallet.BlowfishERC721ApprovalForAllData | BraveWallet.BlowfishERC721ApprovalData - )?.symbol ?? undefined + )?.asset.symbol ?? undefined // memos const beforeAmount = React.useMemo(() => {