[Polkadot] Parse assets pallet chain metadata. (#36419)
* [Polkadot] Parse assets pallet chain metadata. Check whether assets pallet exists and parse transfer calls. Resolves https://github.com/brave/brave-browser/issues/55503
This commit is contained in:
@@ -771,6 +771,7 @@ source_set("unit_tests") {
|
||||
"polkadot/polkadot_keyring_unittest.cc",
|
||||
"polkadot/polkadot_metadata_provider_unittest.cc",
|
||||
"polkadot/polkadot_substrate_rpc_unittest.cc",
|
||||
"polkadot/polkadot_test_utils_unittest.cc",
|
||||
"polkadot/polkadot_transaction_status_task_unittest.cc",
|
||||
"polkadot/polkadot_transaction_unittest.cc",
|
||||
"polkadot/polkadot_tx_manager_unittest.cc",
|
||||
|
||||
@@ -196,10 +196,9 @@ fn parse_pallet(input: &mut &[u8], has_pallet_docs: bool) -> Result<PalletInfo,
|
||||
let name: String = decode_scale(input)?;
|
||||
|
||||
// storage: Option<PalletStorageMetadata>
|
||||
let _storage = decode_option(input, |input| {
|
||||
decode_option(input, |input| {
|
||||
let _: String = decode_scale(input)?; // prefix
|
||||
let _ = decode_vec(input, parse_storage_entry)?;
|
||||
Ok(())
|
||||
decode_vec(input, parse_storage_entry)
|
||||
})?;
|
||||
|
||||
// calls: Option<PalletCallMetadata { ty: u32 }>
|
||||
@@ -356,6 +355,8 @@ fn parse_extrinsic_metadata(input: &mut &[u8], version: u8) -> Result<bool, Erro
|
||||
/// - `transfer_allow_death` call index
|
||||
/// - `System.SS58Prefix`
|
||||
/// - `System.Version.spec_version`
|
||||
/// - `Assets` pallet index and transfer call indexes when the pallet is
|
||||
/// present
|
||||
/// - whether `ChargeAssetTxPayment` is a signed extension
|
||||
///
|
||||
/// References:
|
||||
@@ -396,6 +397,19 @@ fn parse_chain_metadata_fields(bytes: &[u8]) -> Result<CxxPolkadotChainMetadata,
|
||||
let transfer_all_call_index =
|
||||
get_call_index(&portable_registry, balances_pallet, "transferall")?;
|
||||
|
||||
let mut has_assets_pallet = false;
|
||||
let mut assets_pallet_index = 0;
|
||||
let mut assets_transfer_all_call_index = 0;
|
||||
let mut assets_transfer_keep_alive_call_index = 0;
|
||||
if let Some(assets_pallet) = pallets.iter().find(|p| normalize_ident(&p.name) == "assets") {
|
||||
has_assets_pallet = true;
|
||||
assets_pallet_index = assets_pallet.index;
|
||||
assets_transfer_all_call_index =
|
||||
get_call_index(&portable_registry, assets_pallet, "transferall")?;
|
||||
assets_transfer_keep_alive_call_index =
|
||||
get_call_index(&portable_registry, assets_pallet, "transferkeepalive")?;
|
||||
}
|
||||
|
||||
let system_pallet = pallets
|
||||
.iter()
|
||||
.find(|p| normalize_ident(&p.name) == "system")
|
||||
@@ -430,6 +444,10 @@ fn parse_chain_metadata_fields(bytes: &[u8]) -> Result<CxxPolkadotChainMetadata,
|
||||
transfer_allow_death_call_index,
|
||||
transfer_keep_alive_call_index,
|
||||
transfer_all_call_index,
|
||||
assets_pallet_index,
|
||||
assets_transfer_all_call_index,
|
||||
assets_transfer_keep_alive_call_index,
|
||||
has_assets_pallet,
|
||||
ss58_prefix,
|
||||
spec_version,
|
||||
asset_tx_payment,
|
||||
|
||||
@@ -55,6 +55,10 @@ mod ffi {
|
||||
pub transfer_allow_death_call_index: u8,
|
||||
pub transfer_keep_alive_call_index: u8,
|
||||
pub transfer_all_call_index: u8,
|
||||
pub assets_pallet_index: u8,
|
||||
pub assets_transfer_all_call_index: u8,
|
||||
pub assets_transfer_keep_alive_call_index: u8,
|
||||
pub has_assets_pallet: bool,
|
||||
pub ss58_prefix: u16,
|
||||
pub spec_version: u32,
|
||||
pub asset_tx_payment: bool,
|
||||
@@ -62,6 +66,7 @@ mod ffi {
|
||||
|
||||
extern "Rust" {
|
||||
fn compact_scale_encode_u32(x: u32) -> Vec<u8>;
|
||||
fn scale_encode_string(value: &[u8]) -> Vec<u8>;
|
||||
|
||||
type CxxPolkadotDecodeUnsignedTransferResult;
|
||||
type CxxPolkadotChainMetadataResult;
|
||||
@@ -521,6 +526,10 @@ fn compact_scale_encode_u32(x: u32) -> Vec<u8> {
|
||||
Compact(x).encode()
|
||||
}
|
||||
|
||||
fn scale_encode_string(value: &[u8]) -> Vec<u8> {
|
||||
value.encode()
|
||||
}
|
||||
|
||||
fn was_extrinsic_successful(
|
||||
events: &[u8],
|
||||
extrinsic_idx: u32,
|
||||
|
||||
@@ -48,15 +48,23 @@ PolkadotChainMetadata PolkadotChainMetadata::FromFields(
|
||||
uint8_t transfer_all_call_index,
|
||||
uint16_t ss58_prefix,
|
||||
uint32_t spec_version,
|
||||
bool asset_tx_payment) {
|
||||
bool asset_tx_payment,
|
||||
bool has_assets_pallet,
|
||||
uint8_t assets_pallet_index,
|
||||
uint8_t assets_transfer_all_call_index,
|
||||
uint8_t assets_transfer_keep_alive_call_index) {
|
||||
PolkadotChainMetadata metadata;
|
||||
|
||||
metadata->system_pallet_index = system_pallet_index;
|
||||
metadata->balances_pallet_index = balances_pallet_index;
|
||||
metadata->transaction_payment_pallet_index = transaction_payment_pallet_index;
|
||||
metadata->transfer_allow_death_call_index = transfer_allow_death_call_index;
|
||||
metadata->transfer_keep_alive_call_index = transfer_keep_alive_call_index;
|
||||
metadata->transfer_all_call_index = transfer_all_call_index;
|
||||
metadata->assets_pallet_index = assets_pallet_index;
|
||||
metadata->assets_transfer_all_call_index = assets_transfer_all_call_index;
|
||||
metadata->assets_transfer_keep_alive_call_index =
|
||||
assets_transfer_keep_alive_call_index;
|
||||
metadata->has_assets_pallet = has_assets_pallet;
|
||||
metadata->ss58_prefix = ss58_prefix;
|
||||
metadata->spec_version = spec_version;
|
||||
metadata->asset_tx_payment = asset_tx_payment;
|
||||
|
||||
@@ -47,7 +47,11 @@ class PolkadotChainMetadata {
|
||||
uint8_t transfer_all_call_index,
|
||||
uint16_t ss58_prefix,
|
||||
uint32_t spec_version,
|
||||
bool asset_tx_payment);
|
||||
bool asset_tx_payment,
|
||||
bool has_assets_pallet,
|
||||
uint8_t assets_pallet_index,
|
||||
uint8_t assets_transfer_all_call_index,
|
||||
uint8_t assets_transfer_keep_alive_call_index);
|
||||
|
||||
// Obtain a reference to the underlying opaque type so that it can be passed
|
||||
// to Rust routines.
|
||||
|
||||
@@ -24,6 +24,11 @@ constexpr char kTransferAllowDeathCallIndex[] =
|
||||
"transfer_allow_death_call_index";
|
||||
constexpr char kTransferKeepAliveCallIndex[] = "transfer_keep_alive_call_index";
|
||||
constexpr char kTransferAllCallIndex[] = "transfer_all_call_index";
|
||||
constexpr char kHasAssetsPallet[] = "has_assets_pallet";
|
||||
constexpr char kAssetsPalletIndex[] = "assets_pallet_index";
|
||||
constexpr char kAssetsTransferAllCallIndex[] = "assets_transfer_all_call_index";
|
||||
constexpr char kAssetsTransferKeepAliveCallIndex[] =
|
||||
"assets_transfer_keep_alive_call_index";
|
||||
constexpr char kAssetTxPayment[] = "asset_tx_payment";
|
||||
constexpr char kSs58Prefix[] = "ss58_prefix";
|
||||
constexpr char kSpecVersion[] = "spec_version";
|
||||
@@ -39,6 +44,10 @@ constexpr char kVersionField[] = "version";
|
||||
// "transfer_allow_death_call_index": int, // u8
|
||||
// "transfer_keep_alive_call_index": int, // u8
|
||||
// "transfer_all_call_index": int, // u8
|
||||
// "has_assets_pallet": bool, // bool
|
||||
// "assets_pallet_index": int, // u8
|
||||
// "assets_transfer_all_call_index": int, // u8
|
||||
// "assets_transfer_keep_alive_call_index": int, // u8
|
||||
// "asset_tx_payment": bool, // bool
|
||||
// "ss58_prefix": int, // u16
|
||||
// "spec_version": int // u32
|
||||
@@ -128,6 +137,26 @@ PolkadotChainMetadataPrefs::GetChainMetadata(std::string_view chain_id) const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!ReadBool(*chain_metadata, kHasAssetsPallet,
|
||||
&metadata->has_assets_pallet)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!ReadUint(*chain_metadata, kAssetsPalletIndex,
|
||||
&metadata->assets_pallet_index)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!ReadUint(*chain_metadata, kAssetsTransferAllCallIndex,
|
||||
&metadata->assets_transfer_all_call_index)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!ReadUint(*chain_metadata, kAssetsTransferKeepAliveCallIndex,
|
||||
&metadata->assets_transfer_keep_alive_call_index)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!ReadBool(*chain_metadata, kAssetTxPayment,
|
||||
&metadata->asset_tx_payment)) {
|
||||
return std::nullopt;
|
||||
@@ -154,6 +183,9 @@ bool PolkadotChainMetadataPrefs::SetChainMetadata(
|
||||
int transfer_allow_death_call_index = 0;
|
||||
int transfer_keep_alive_call_index = 0;
|
||||
int transfer_all_call_index = 0;
|
||||
int assets_pallet_index = 0;
|
||||
int assets_transfer_all_call_index = 0;
|
||||
int assets_transfer_keep_alive_call_index = 0;
|
||||
int ss58_prefix = 0;
|
||||
int spec_version = 0;
|
||||
if (!base::CheckedNumeric<int>(metadata->system_pallet_index)
|
||||
@@ -168,6 +200,13 @@ bool PolkadotChainMetadataPrefs::SetChainMetadata(
|
||||
.AssignIfValid(&transfer_keep_alive_call_index) ||
|
||||
!base::CheckedNumeric<int>(metadata->transfer_all_call_index)
|
||||
.AssignIfValid(&transfer_all_call_index) ||
|
||||
!base::CheckedNumeric<int>(metadata->assets_pallet_index)
|
||||
.AssignIfValid(&assets_pallet_index) ||
|
||||
!base::CheckedNumeric<int>(metadata->assets_transfer_all_call_index)
|
||||
.AssignIfValid(&assets_transfer_all_call_index) ||
|
||||
!base::CheckedNumeric<int>(
|
||||
metadata->assets_transfer_keep_alive_call_index)
|
||||
.AssignIfValid(&assets_transfer_keep_alive_call_index) ||
|
||||
!base::CheckedNumeric<int>(metadata->ss58_prefix)
|
||||
.AssignIfValid(&ss58_prefix) ||
|
||||
!base::CheckedNumeric<int>(metadata->spec_version)
|
||||
@@ -181,6 +220,11 @@ bool PolkadotChainMetadataPrefs::SetChainMetadata(
|
||||
value.Set(kTransferAllowDeathCallIndex, transfer_allow_death_call_index);
|
||||
value.Set(kTransferKeepAliveCallIndex, transfer_keep_alive_call_index);
|
||||
value.Set(kTransferAllCallIndex, transfer_all_call_index);
|
||||
value.Set(kHasAssetsPallet, metadata->has_assets_pallet);
|
||||
value.Set(kAssetsPalletIndex, assets_pallet_index);
|
||||
value.Set(kAssetsTransferAllCallIndex, assets_transfer_all_call_index);
|
||||
value.Set(kAssetsTransferKeepAliveCallIndex,
|
||||
assets_transfer_keep_alive_call_index);
|
||||
value.Set(kAssetTxPayment, metadata->asset_tx_payment);
|
||||
value.Set(kSs58Prefix, ss58_prefix);
|
||||
value.Set(kSpecVersion, spec_version);
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace brave_wallet {
|
||||
|
||||
class PolkadotChainMetadataPrefs {
|
||||
public:
|
||||
static constexpr int kVersion = 1;
|
||||
static constexpr int kVersion = 2;
|
||||
|
||||
explicit PolkadotChainMetadataPrefs(PrefService& profile_prefs);
|
||||
virtual ~PolkadotChainMetadataPrefs();
|
||||
|
||||
@@ -24,6 +24,11 @@ constexpr char kTransferAllowDeathCallIndex[] =
|
||||
"transfer_allow_death_call_index";
|
||||
constexpr char kTransferKeepAliveCallIndex[] = "transfer_keep_alive_call_index";
|
||||
constexpr char kTransferAllCallIndex[] = "transfer_all_call_index";
|
||||
constexpr char kHasAssetsPallet[] = "has_assets_pallet";
|
||||
constexpr char kAssetsPalletIndex[] = "assets_pallet_index";
|
||||
constexpr char kAssetsTransferAllCallIndex[] = "assets_transfer_all_call_index";
|
||||
constexpr char kAssetsTransferKeepAliveCallIndex[] =
|
||||
"assets_transfer_keep_alive_call_index";
|
||||
constexpr char kAssetTxPayment[] = "asset_tx_payment";
|
||||
constexpr char kSs58Prefix[] = "ss58_prefix";
|
||||
constexpr char kVersionField[] = "version";
|
||||
@@ -48,6 +53,10 @@ TEST_F(PolkadotChainMetadataPrefsUnitTest, SetAndGetChainMetadataRoundTrip) {
|
||||
uint8_t transfer_allow_death_call_index;
|
||||
uint8_t transfer_keep_alive_call_index;
|
||||
uint8_t transfer_all_call_index;
|
||||
bool has_assets_pallet;
|
||||
uint8_t assets_pallet_index;
|
||||
uint8_t assets_transfer_all_call_index;
|
||||
uint8_t assets_transfer_keep_alive_call_index;
|
||||
uint16_t ss58_prefix;
|
||||
uint32_t spec_version;
|
||||
bool asset_tx_payment;
|
||||
@@ -60,6 +69,10 @@ TEST_F(PolkadotChainMetadataPrefsUnitTest, SetAndGetChainMetadataRoundTrip) {
|
||||
/*transfer_allow_death_call_index=*/2,
|
||||
/*transfer_keep_alive_call_index=*/4,
|
||||
/*transfer_all_call_index=*/5,
|
||||
/*has_assets_pallet=*/false,
|
||||
/*assets_pallet_index=*/0,
|
||||
/*assets_transfer_all_call_index=*/0,
|
||||
/*assets_transfer_keep_alive_call_index=*/0,
|
||||
/*ss58_prefix=*/42,
|
||||
/*spec_version=*/1234, /*asset_tx_payment=*/false},
|
||||
{"AssetHub Polkadot Metadata",
|
||||
@@ -68,6 +81,10 @@ TEST_F(PolkadotChainMetadataPrefsUnitTest, SetAndGetChainMetadataRoundTrip) {
|
||||
/*transfer_allow_death_call_index=*/2,
|
||||
/*transfer_keep_alive_call_index=*/4,
|
||||
/*transfer_all_call_index=*/255,
|
||||
/*has_assets_pallet=*/true,
|
||||
/*assets_pallet_index=*/50,
|
||||
/*assets_transfer_all_call_index=*/10,
|
||||
/*assets_transfer_keep_alive_call_index=*/9,
|
||||
/*ss58_prefix=*/std::numeric_limits<uint16_t>::max(),
|
||||
/*spec_version=*/12344321, /*asset_tx_payment=*/true}};
|
||||
|
||||
@@ -78,7 +95,10 @@ TEST_F(PolkadotChainMetadataPrefsUnitTest, SetAndGetChainMetadataRoundTrip) {
|
||||
tc.system_pallet_index, tc.balances_pallet_index,
|
||||
tc.transaction_payment_pallet_index, tc.transfer_allow_death_call_index,
|
||||
tc.transfer_keep_alive_call_index, tc.transfer_all_call_index,
|
||||
tc.ss58_prefix, tc.spec_version, tc.asset_tx_payment);
|
||||
tc.ss58_prefix, tc.spec_version, tc.asset_tx_payment,
|
||||
tc.has_assets_pallet, tc.assets_pallet_index,
|
||||
tc.assets_transfer_all_call_index,
|
||||
tc.assets_transfer_keep_alive_call_index);
|
||||
|
||||
PolkadotChainMetadataPrefs prefs = MakePrefs();
|
||||
EXPECT_TRUE(prefs.SetChainMetadata(mojom::kPolkadotMainnet, metadata));
|
||||
@@ -111,6 +131,10 @@ TEST_F(PolkadotChainMetadataPrefsUnitTest, InvalidRangeRejected) {
|
||||
value.Set(kTransferAllowDeathCallIndex, 1);
|
||||
value.Set(kTransferKeepAliveCallIndex, 1);
|
||||
value.Set(kTransferAllCallIndex, 1);
|
||||
value.Set(kHasAssetsPallet, false);
|
||||
value.Set(kAssetsPalletIndex, 0);
|
||||
value.Set(kAssetsTransferAllCallIndex, 0);
|
||||
value.Set(kAssetsTransferKeepAliveCallIndex, 0);
|
||||
value.Set(kAssetTxPayment, false);
|
||||
value.Set(kSs58Prefix, 0);
|
||||
value.Set(kSpecVersion, 100);
|
||||
@@ -133,6 +157,10 @@ TEST_F(PolkadotChainMetadataPrefsUnitTest, NegativeValueRejected) {
|
||||
-1); // Negative should be rejected.
|
||||
value.Set(kTransferKeepAliveCallIndex, 1);
|
||||
value.Set(kTransferAllCallIndex, 1);
|
||||
value.Set(kHasAssetsPallet, false);
|
||||
value.Set(kAssetsPalletIndex, 0);
|
||||
value.Set(kAssetsTransferAllCallIndex, 0);
|
||||
value.Set(kAssetsTransferKeepAliveCallIndex, 0);
|
||||
value.Set(kAssetTxPayment, false);
|
||||
value.Set(kSs58Prefix, 0);
|
||||
value.Set(kSpecVersion, 100);
|
||||
@@ -154,6 +182,10 @@ TEST_F(PolkadotChainMetadataPrefsUnitTest, MissingRequiredFieldRejected) {
|
||||
value.Set(kTransferAllowDeathCallIndex, 1);
|
||||
value.Set(kTransferKeepAliveCallIndex, 4);
|
||||
value.Set(kTransferAllCallIndex, 1);
|
||||
value.Set(kHasAssetsPallet, false);
|
||||
value.Set(kAssetsPalletIndex, 0);
|
||||
value.Set(kAssetsTransferAllCallIndex, 0);
|
||||
value.Set(kAssetsTransferKeepAliveCallIndex, 0);
|
||||
value.Set(kAssetTxPayment, false);
|
||||
value.Set(kSs58Prefix, 42);
|
||||
// Missing spec version should reject persisted value.
|
||||
@@ -175,6 +207,10 @@ TEST_F(PolkadotChainMetadataPrefsUnitTest, MissingRequiredFieldRejected) {
|
||||
value.Set(kTransferAllowDeathCallIndex, 1);
|
||||
value.Set(kTransferKeepAliveCallIndex, 4);
|
||||
value.Set(kTransferAllCallIndex, 1);
|
||||
value.Set(kHasAssetsPallet, false);
|
||||
value.Set(kAssetsPalletIndex, 0);
|
||||
value.Set(kAssetsTransferAllCallIndex, 0);
|
||||
value.Set(kAssetsTransferKeepAliveCallIndex, 0);
|
||||
value.Set(kSs58Prefix, 42);
|
||||
value.Set(kSpecVersion, 1234);
|
||||
update->Set(mojom::kPolkadotMainnet, std::move(value));
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
#include "brave/components/brave_wallet/browser/polkadot/polkadot_chain_metadata.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "base/base_paths.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/test/values_test_util.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "brave/components/brave_wallet/browser/polkadot/polkadot_test_utils.h"
|
||||
#include "brave/components/brave_wallet/common/hex_utils.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
@@ -18,7 +18,11 @@ namespace brave_wallet {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kResult[] = "result";
|
||||
std::string ScaleEncodeString(std::string_view value) {
|
||||
auto bytes = base::as_byte_span(value);
|
||||
auto encoded = scale_encode_string(::rust::Slice(bytes));
|
||||
return std::string(encoded.begin(), encoded.end());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -30,7 +34,11 @@ TEST(PolkadotChainMetadataUnitTest, FromFields) {
|
||||
/*transfer_keep_alive_call_index=*/4,
|
||||
/*transfer_all_call_index=*/5,
|
||||
/*ss58_prefix=*/42, /*spec_version=*/1'234'567,
|
||||
/*asset_tx_payment=*/true);
|
||||
/*asset_tx_payment=*/true,
|
||||
/*has_assets_pallet=*/true,
|
||||
/*assets_pallet_index=*/50,
|
||||
/*assets_transfer_all_call_index=*/10,
|
||||
/*assets_transfer_keep_alive_call_index=*/9);
|
||||
|
||||
EXPECT_EQ(metadata->system_pallet_index, 0u);
|
||||
EXPECT_EQ(metadata->balances_pallet_index, 7u);
|
||||
@@ -38,6 +46,10 @@ TEST(PolkadotChainMetadataUnitTest, FromFields) {
|
||||
EXPECT_EQ(metadata->transfer_allow_death_call_index, 2u);
|
||||
EXPECT_EQ(metadata->transfer_keep_alive_call_index, 4u);
|
||||
EXPECT_EQ(metadata->transfer_all_call_index, 5u);
|
||||
EXPECT_TRUE(metadata->has_assets_pallet);
|
||||
EXPECT_EQ(metadata->assets_pallet_index, 50u);
|
||||
EXPECT_EQ(metadata->assets_transfer_all_call_index, 10u);
|
||||
EXPECT_EQ(metadata->assets_transfer_keep_alive_call_index, 9u);
|
||||
EXPECT_EQ(metadata->ss58_prefix, 42u);
|
||||
EXPECT_EQ(metadata->spec_version, 1'234'567u);
|
||||
EXPECT_TRUE(metadata->asset_tx_payment);
|
||||
@@ -51,7 +63,11 @@ TEST(PolkadotChainMetadataUnitTest, EqualityOperator) {
|
||||
/*transfer_keep_alive_call_index=*/4,
|
||||
/*transfer_all_call_index=*/5,
|
||||
/*ss58_prefix=*/42, /*spec_version=*/1'234'567,
|
||||
/*asset_tx_payment=*/false);
|
||||
/*asset_tx_payment=*/false,
|
||||
/*has_assets_pallet=*/false,
|
||||
/*assets_pallet_index=*/0,
|
||||
/*assets_transfer_all_call_index=*/0,
|
||||
/*assets_transfer_keep_alive_call_index=*/0);
|
||||
|
||||
PolkadotChainMetadata metadata_b = metadata_a;
|
||||
|
||||
@@ -61,9 +77,23 @@ TEST(PolkadotChainMetadataUnitTest, EqualityOperator) {
|
||||
PolkadotChainMetadata metadata_d = metadata_a;
|
||||
metadata_d->asset_tx_payment = true;
|
||||
|
||||
auto metadata_e = PolkadotChainMetadata::FromFields(
|
||||
/*system_pallet_index=*/0, /*balances_pallet_index=*/7,
|
||||
/*transaction_payment_pallet_index=*/0x20,
|
||||
/*transfer_allow_death_call_index=*/2,
|
||||
/*transfer_keep_alive_call_index=*/4,
|
||||
/*transfer_all_call_index=*/5,
|
||||
/*ss58_prefix=*/42, /*spec_version=*/1'234'567,
|
||||
/*asset_tx_payment=*/false,
|
||||
/*has_assets_pallet=*/true,
|
||||
/*assets_pallet_index=*/50,
|
||||
/*assets_transfer_all_call_index=*/10,
|
||||
/*assets_transfer_keep_alive_call_index=*/9);
|
||||
|
||||
EXPECT_EQ(metadata_a, metadata_b);
|
||||
EXPECT_NE(metadata_a, metadata_c);
|
||||
EXPECT_NE(metadata_a, metadata_d);
|
||||
EXPECT_NE(metadata_a, metadata_e);
|
||||
}
|
||||
|
||||
TEST(PolkadotChainMetadataUnitTest, FromBytesInvalid) {
|
||||
@@ -77,29 +107,46 @@ TEST(PolkadotChainMetadataUnitTest, FromBytesInvalid) {
|
||||
EXPECT_FALSE(PolkadotChainMetadata::FromBytes(invalid_short));
|
||||
}
|
||||
|
||||
TEST(PolkadotChainMetadataUnitTest,
|
||||
ParseMetadataWithoutAssetsPalletLeavesAssetsFieldsUnset) {
|
||||
std::vector<uint8_t> metadata_bytes =
|
||||
ReadMetadataFixture("state_getMetadata_polkadot.json");
|
||||
ASSERT_FALSE(metadata_bytes.empty());
|
||||
|
||||
auto metadata = PolkadotChainMetadata::FromBytes(metadata_bytes);
|
||||
ASSERT_TRUE(metadata);
|
||||
|
||||
EXPECT_FALSE((*metadata)->has_assets_pallet);
|
||||
EXPECT_EQ((*metadata)->assets_pallet_index, 0u);
|
||||
EXPECT_EQ((*metadata)->assets_transfer_all_call_index, 0u);
|
||||
EXPECT_EQ((*metadata)->assets_transfer_keep_alive_call_index, 0u);
|
||||
}
|
||||
|
||||
TEST(PolkadotChainMetadataUnitTest,
|
||||
ParseMetadataWithAssetsPalletMissingMethodFails) {
|
||||
std::vector<uint8_t> metadata_bytes =
|
||||
ReadMetadataFixture("state_getMetadata_assethub_polkadot.json");
|
||||
ASSERT_FALSE(metadata_bytes.empty());
|
||||
|
||||
// The second transfer_keep_alive occurrence is the Assets pallet call
|
||||
// variant; the first belongs to Balances.
|
||||
const std::string transfer_keep_alive =
|
||||
ScaleEncodeString("transfer_keep_alive");
|
||||
const std::string transfer_dead_alive =
|
||||
ScaleEncodeString("transfer_dead_alive");
|
||||
ASSERT_TRUE(ReplaceNthOccurrence(metadata_bytes, transfer_keep_alive,
|
||||
transfer_dead_alive, /*occurrence=*/1));
|
||||
|
||||
EXPECT_FALSE(PolkadotChainMetadata::FromBytes(metadata_bytes));
|
||||
}
|
||||
|
||||
TEST(PolkadotChainMetadataUnitTest, ParseRealStateGetMetadataResponsePolkadot) {
|
||||
// Refreshed with:
|
||||
// curl -sS -H 'Content-Type: application/json' \
|
||||
// -d '{"id":1,"jsonrpc":"2.0","method":"state_getMetadata","params":[]}' \
|
||||
// https://rpc.polkadot.io/
|
||||
const auto fixture_path =
|
||||
base::PathService::CheckedGet(base::DIR_SRC_TEST_DATA_ROOT)
|
||||
.AppendASCII("brave")
|
||||
.AppendASCII("components")
|
||||
.AppendASCII("test")
|
||||
.AppendASCII("data")
|
||||
.AppendASCII("brave_wallet")
|
||||
.AppendASCII("polkadot")
|
||||
.AppendASCII("chain_metadata")
|
||||
.AppendASCII("state_getMetadata_polkadot.json");
|
||||
const base::DictValue json = base::test::ParseJsonDictFromFile(fixture_path);
|
||||
|
||||
const std::string* metadata_hex = json.FindString(kResult);
|
||||
ASSERT_TRUE(metadata_hex);
|
||||
ASSERT_FALSE(metadata_hex->empty());
|
||||
|
||||
std::vector<uint8_t> metadata_bytes;
|
||||
ASSERT_TRUE(PrefixedHexStringToBytes(*metadata_hex, &metadata_bytes));
|
||||
std::vector<uint8_t> metadata_bytes =
|
||||
ReadMetadataFixture("state_getMetadata_polkadot.json");
|
||||
|
||||
auto metadata = PolkadotChainMetadata::FromBytes(metadata_bytes);
|
||||
ASSERT_TRUE(metadata);
|
||||
@@ -111,7 +158,11 @@ TEST(PolkadotChainMetadataUnitTest, ParseRealStateGetMetadataResponsePolkadot) {
|
||||
/*transfer_keep_alive_call_index=*/3,
|
||||
/*transfer_all_call_index=*/4,
|
||||
/*ss58_prefix=*/0, /*spec_version=*/2'000'007,
|
||||
/*asset_tx_payment=*/false);
|
||||
/*asset_tx_payment=*/false,
|
||||
/*has_assets_pallet=*/false,
|
||||
/*assets_pallet_index=*/0,
|
||||
/*assets_transfer_all_call_index=*/0,
|
||||
/*assets_transfer_keep_alive_call_index=*/0);
|
||||
EXPECT_EQ(*metadata, expected);
|
||||
|
||||
auto metadata2 = PolkadotMetadataFromChainName("Polkadot");
|
||||
@@ -127,24 +178,8 @@ TEST(PolkadotChainMetadataUnitTest,
|
||||
// curl -sS -H 'Content-Type: application/json' \
|
||||
// -d '{"id":1,"jsonrpc":"2.0","method":"state_getMetadata","params":[]}' \
|
||||
// https://polkadot-asset-hub-rpc.polkadot.io
|
||||
const auto fixture_path =
|
||||
base::PathService::CheckedGet(base::DIR_SRC_TEST_DATA_ROOT)
|
||||
.AppendASCII("brave")
|
||||
.AppendASCII("components")
|
||||
.AppendASCII("test")
|
||||
.AppendASCII("data")
|
||||
.AppendASCII("brave_wallet")
|
||||
.AppendASCII("polkadot")
|
||||
.AppendASCII("chain_metadata")
|
||||
.AppendASCII("state_getMetadata_assethub_polkadot.json");
|
||||
const base::DictValue json = base::test::ParseJsonDictFromFile(fixture_path);
|
||||
|
||||
const std::string* metadata_hex = json.FindString(kResult);
|
||||
ASSERT_TRUE(metadata_hex);
|
||||
ASSERT_FALSE(metadata_hex->empty());
|
||||
|
||||
std::vector<uint8_t> metadata_bytes;
|
||||
ASSERT_TRUE(PrefixedHexStringToBytes(*metadata_hex, &metadata_bytes));
|
||||
std::vector<uint8_t> metadata_bytes =
|
||||
ReadMetadataFixture("state_getMetadata_assethub_polkadot.json");
|
||||
|
||||
auto metadata = PolkadotChainMetadata::FromBytes(metadata_bytes);
|
||||
ASSERT_TRUE(metadata);
|
||||
@@ -156,7 +191,12 @@ TEST(PolkadotChainMetadataUnitTest,
|
||||
/*transfer_keep_alive_call_index=*/3,
|
||||
/*transfer_all_call_index=*/4,
|
||||
/*ss58_prefix=*/0, /*spec_version=*/2'002'001,
|
||||
/*asset_tx_payment=*/true);
|
||||
/*asset_tx_payment=*/true,
|
||||
/*has_assets_pallet=*/true,
|
||||
/*assets_pallet_index=*/50,
|
||||
/*assets_transfer_all_call_index=*/32,
|
||||
/*assets_transfer_keep_alive_call_index=*/9);
|
||||
EXPECT_EQ(*metadata, expected);
|
||||
|
||||
auto metadata2 = PolkadotMetadataFromChainName("Polkadot Asset Hub");
|
||||
ASSERT_TRUE(metadata2);
|
||||
@@ -170,24 +210,8 @@ TEST(PolkadotChainMetadataUnitTest, ParseRealStateGetMetadataResponseWestend) {
|
||||
// curl -sS -H 'Content-Type: application/json' \
|
||||
// -d '{"id":1,"jsonrpc":"2.0","method":"state_getMetadata","params":[]}' \
|
||||
// https://westend-rpc.polkadot.io/
|
||||
const auto fixture_path =
|
||||
base::PathService::CheckedGet(base::DIR_SRC_TEST_DATA_ROOT)
|
||||
.AppendASCII("brave")
|
||||
.AppendASCII("components")
|
||||
.AppendASCII("test")
|
||||
.AppendASCII("data")
|
||||
.AppendASCII("brave_wallet")
|
||||
.AppendASCII("polkadot")
|
||||
.AppendASCII("chain_metadata")
|
||||
.AppendASCII("state_getMetadata_westend.json");
|
||||
const base::DictValue json = base::test::ParseJsonDictFromFile(fixture_path);
|
||||
|
||||
const std::string* metadata_hex = json.FindString(kResult);
|
||||
ASSERT_TRUE(metadata_hex);
|
||||
ASSERT_FALSE(metadata_hex->empty());
|
||||
|
||||
std::vector<uint8_t> metadata_bytes;
|
||||
ASSERT_TRUE(PrefixedHexStringToBytes(*metadata_hex, &metadata_bytes));
|
||||
std::vector<uint8_t> metadata_bytes =
|
||||
ReadMetadataFixture("state_getMetadata_westend.json");
|
||||
|
||||
auto metadata = PolkadotChainMetadata::FromBytes(metadata_bytes);
|
||||
ASSERT_TRUE(metadata);
|
||||
@@ -199,7 +223,11 @@ TEST(PolkadotChainMetadataUnitTest, ParseRealStateGetMetadataResponseWestend) {
|
||||
/*transfer_keep_alive_call_index=*/3,
|
||||
/*transfer_all_call_index=*/4,
|
||||
/*ss58_prefix=*/42, /*spec_version=*/1'022'000,
|
||||
/*asset_tx_payment=*/false);
|
||||
/*asset_tx_payment=*/false,
|
||||
/*has_assets_pallet=*/false,
|
||||
/*assets_pallet_index=*/0,
|
||||
/*assets_transfer_all_call_index=*/0,
|
||||
/*assets_transfer_keep_alive_call_index=*/0);
|
||||
EXPECT_EQ(*metadata, expected);
|
||||
|
||||
auto metadata2 = PolkadotMetadataFromChainName("Westend");
|
||||
@@ -215,24 +243,8 @@ TEST(PolkadotChainMetadataUnitTest,
|
||||
// curl -sS -H 'Content-Type: application/json' \
|
||||
// -d '{"id":1,"jsonrpc":"2.0","method":"state_getMetadata","params":[]}' \
|
||||
// https://westend-asset-hub-rpc.polkadot.io/
|
||||
const auto fixture_path =
|
||||
base::PathService::CheckedGet(base::DIR_SRC_TEST_DATA_ROOT)
|
||||
.AppendASCII("brave")
|
||||
.AppendASCII("components")
|
||||
.AppendASCII("test")
|
||||
.AppendASCII("data")
|
||||
.AppendASCII("brave_wallet")
|
||||
.AppendASCII("polkadot")
|
||||
.AppendASCII("chain_metadata")
|
||||
.AppendASCII("state_getMetadata_assethub_westend.json");
|
||||
const base::DictValue json = base::test::ParseJsonDictFromFile(fixture_path);
|
||||
|
||||
const std::string* metadata_hex = json.FindString(kResult);
|
||||
ASSERT_TRUE(metadata_hex);
|
||||
ASSERT_FALSE(metadata_hex->empty());
|
||||
|
||||
std::vector<uint8_t> metadata_bytes;
|
||||
ASSERT_TRUE(PrefixedHexStringToBytes(*metadata_hex, &metadata_bytes));
|
||||
std::vector<uint8_t> metadata_bytes =
|
||||
ReadMetadataFixture("state_getMetadata_assethub_westend.json");
|
||||
|
||||
auto metadata = PolkadotChainMetadata::FromBytes(metadata_bytes);
|
||||
ASSERT_TRUE(metadata);
|
||||
@@ -244,7 +256,12 @@ TEST(PolkadotChainMetadataUnitTest,
|
||||
/*transfer_keep_alive_call_index=*/3,
|
||||
/*transfer_all_call_index=*/4,
|
||||
/*ss58_prefix=*/42, /*spec_version=*/1'022'005,
|
||||
/*asset_tx_payment=*/true);
|
||||
/*asset_tx_payment=*/true,
|
||||
/*has_assets_pallet=*/true,
|
||||
/*assets_pallet_index=*/50,
|
||||
/*assets_transfer_all_call_index=*/32,
|
||||
/*assets_transfer_keep_alive_call_index=*/9);
|
||||
EXPECT_EQ(*metadata, expected);
|
||||
|
||||
auto metadata2 = PolkadotMetadataFromChainName("Westend Asset Hub");
|
||||
ASSERT_TRUE(metadata2);
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
#include "brave/components/brave_wallet/browser/polkadot/polkadot_test_utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/base_paths.h"
|
||||
#include "base/check.h"
|
||||
#include "base/containers/map_util.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/json/json_writer.h"
|
||||
@@ -85,6 +89,40 @@ std::vector<uint8_t> ReadMetadataFixture(std::string_view file_name) {
|
||||
return metadata_bytes;
|
||||
}
|
||||
|
||||
bool ReplaceNthOccurrence(std::vector<uint8_t>& bytes,
|
||||
std::string_view needle,
|
||||
std::string_view replacement,
|
||||
size_t occurrence) {
|
||||
DCHECK(!needle.empty());
|
||||
if (needle.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto needle_bytes = base::as_byte_span(needle);
|
||||
const auto replacement_bytes = base::as_byte_span(replacement);
|
||||
auto it = bytes.begin();
|
||||
size_t num_found = 0;
|
||||
|
||||
while (it != bytes.end()) {
|
||||
auto match = std::ranges::search(it, bytes.end(), needle_bytes.begin(),
|
||||
needle_bytes.end());
|
||||
if (match.begin() == bytes.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (num_found == occurrence) {
|
||||
auto pos = bytes.erase(match.begin(), match.end());
|
||||
bytes.insert(pos, replacement_bytes.begin(), replacement_bytes.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
++num_found;
|
||||
it = match.end();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<PolkadotChainMetadata> PolkadotMetadataFromChainName(
|
||||
std::string_view chain_name) {
|
||||
// spec_version is unknown when constructing from chain name alone; callers
|
||||
@@ -103,7 +141,11 @@ std::optional<PolkadotChainMetadata> PolkadotMetadataFromChainName(
|
||||
/*transfer_keep_alive_call_index=*/3,
|
||||
/*transfer_all_call_index=*/4,
|
||||
/*ss58_prefix=*/42, kUnknownSpecVersion,
|
||||
/*asset_tx_payment=*/false);
|
||||
/*asset_tx_payment=*/false,
|
||||
/*has_assets_pallet=*/false,
|
||||
/*assets_pallet_index=*/0,
|
||||
/*assets_transfer_all_call_index=*/0,
|
||||
/*assets_transfer_keep_alive_call_index=*/0);
|
||||
}
|
||||
|
||||
// https://github.com/polkadot-js/api/blob/f45dfc72ec320cab7d69f08010c9921d2a21065f/packages/types-support/src/metadata/v15/asset-hub-kusama-json.json#L969
|
||||
@@ -116,7 +158,11 @@ std::optional<PolkadotChainMetadata> PolkadotMetadataFromChainName(
|
||||
/*transfer_keep_alive_call_index=*/3,
|
||||
/*transfer_all_call_index=*/4,
|
||||
/*ss58_prefix=*/42, kUnknownSpecVersion,
|
||||
/*asset_tx_payment=*/true);
|
||||
/*asset_tx_payment=*/true,
|
||||
/*has_assets_pallet=*/true,
|
||||
/*assets_pallet_index=*/50,
|
||||
/*assets_transfer_all_call_index=*/32,
|
||||
/*assets_transfer_keep_alive_call_index=*/9);
|
||||
}
|
||||
|
||||
// https://github.com/polkadot-js/api/blob/f45dfc72ec320cab7d69f08010c9921d2a21065f/packages/types-support/src/metadata/v15/polkadot-json.json#L1096
|
||||
@@ -129,7 +175,11 @@ std::optional<PolkadotChainMetadata> PolkadotMetadataFromChainName(
|
||||
/*transfer_keep_alive_call_index=*/3,
|
||||
/*transfer_all_call_index=*/4,
|
||||
/*ss58_prefix=*/0, kUnknownSpecVersion,
|
||||
/*asset_tx_payment=*/false);
|
||||
/*asset_tx_payment=*/false,
|
||||
/*has_assets_pallet=*/false,
|
||||
/*assets_pallet_index=*/0,
|
||||
/*assets_transfer_all_call_index=*/0,
|
||||
/*assets_transfer_keep_alive_call_index=*/0);
|
||||
}
|
||||
|
||||
// https://github.com/polkadot-js/api/blob/f45dfc72ec320cab7d69f08010c9921d2a21065f/packages/types-support/src/metadata/v15/asset-hub-polkadot-json.json#L969
|
||||
@@ -142,7 +192,11 @@ std::optional<PolkadotChainMetadata> PolkadotMetadataFromChainName(
|
||||
/*transfer_keep_alive_call_index=*/3,
|
||||
/*transfer_all_call_index=*/4,
|
||||
/*ss58_prefix=*/0, kUnknownSpecVersion,
|
||||
/*asset_tx_payment=*/true);
|
||||
/*asset_tx_payment=*/true,
|
||||
/*has_assets_pallet=*/true,
|
||||
/*assets_pallet_index=*/50,
|
||||
/*assets_transfer_all_call_index=*/32,
|
||||
/*assets_transfer_keep_alive_call_index=*/9);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
#define BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_POLKADOT_POLKADOT_TEST_UTILS_H_
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "base/values.h"
|
||||
@@ -25,6 +27,19 @@ base::DictValue RequestBodyToJsonDict(const network::ResourceRequest& req);
|
||||
std::string ReadMetadataFixtureJson(std::string_view file_name);
|
||||
std::vector<uint8_t> ReadMetadataFixture(std::string_view file_name);
|
||||
|
||||
// Replaces the zero-based nth occurrence of `needle` in `bytes` with
|
||||
// `replacement`. Returns false if `needle` is empty or if the requested
|
||||
// occurrence is not found.
|
||||
//
|
||||
// Examples:
|
||||
// ReplaceNthOccurrence("a b a b", "a", "x", 0) -> "x b a b"
|
||||
// ReplaceNthOccurrence("a b a b", "a", "x", 1) -> "a b x b"
|
||||
// ReplaceNthOccurrence("a b", "z", "x", 0) -> false
|
||||
bool ReplaceNthOccurrence(std::vector<uint8_t>& bytes,
|
||||
std::string_view needle,
|
||||
std::string_view replacement,
|
||||
size_t occurrence);
|
||||
|
||||
struct PolkadotMockRpc {
|
||||
public:
|
||||
PolkadotMockRpc(network::TestURLLoaderFactory* url_loader_factory,
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "brave/components/brave_wallet/browser/polkadot/polkadot_test_utils.h"
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/span.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace brave_wallet {
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<uint8_t> BytesForTest(std::string_view value) {
|
||||
auto bytes = base::as_byte_span(value);
|
||||
return std::vector(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(PolkadotTestUtilsUnitTest,
|
||||
ReplaceNthOccurrenceReplacesRequestedOccurrence) {
|
||||
auto bytes = BytesForTest("transfer transfer transfer");
|
||||
|
||||
EXPECT_TRUE(ReplaceNthOccurrence(bytes, "transfer", "replaced",
|
||||
/*occurrence=*/1));
|
||||
|
||||
EXPECT_EQ(bytes, BytesForTest("transfer replaced transfer"));
|
||||
}
|
||||
|
||||
TEST(PolkadotTestUtilsUnitTest,
|
||||
ReplaceNthOccurrenceSupportsDifferentLengthReplacement) {
|
||||
auto bytes = BytesForTest("before transfer after");
|
||||
|
||||
EXPECT_TRUE(ReplaceNthOccurrence(bytes, "transfer", "x",
|
||||
/*occurrence=*/0));
|
||||
|
||||
EXPECT_EQ(bytes, BytesForTest("before x after"));
|
||||
}
|
||||
|
||||
TEST(PolkadotTestUtilsUnitTest, ReplaceNthOccurrenceMissingNeedleFails) {
|
||||
auto bytes = BytesForTest("before transfer after");
|
||||
const auto original = bytes;
|
||||
|
||||
EXPECT_FALSE(ReplaceNthOccurrence(bytes, "missing", "x",
|
||||
/*occurrence=*/0));
|
||||
|
||||
EXPECT_EQ(bytes, original);
|
||||
}
|
||||
|
||||
TEST(PolkadotTestUtilsUnitTest, ReplaceNthOccurrenceOutOfRangeFails) {
|
||||
auto bytes = BytesForTest("transfer transfer");
|
||||
const auto original = bytes;
|
||||
|
||||
EXPECT_FALSE(ReplaceNthOccurrence(bytes, "transfer", "replaced",
|
||||
/*occurrence=*/2));
|
||||
|
||||
EXPECT_EQ(bytes, original);
|
||||
}
|
||||
|
||||
} // namespace brave_wallet
|
||||
Reference in New Issue
Block a user