Skip to content

Commit 4e269eb

Browse files
authored
Merge pull request #198 from BitGo/BTC-3062.dot-wasm-proxy-deposit-cost
feat: add get_proxy_deposit_cost metadata constant extraction
2 parents 19fa250 + f1b0d71 commit 4e269eb

5 files changed

Lines changed: 163 additions & 0 deletions

File tree

packages/wasm-dot/js/parser.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,22 @@ export function parseTransaction(tx: DotTransaction, context?: ParseContext): Pa
3939
return ParserNamespace.parseFromTransaction(tx.wasm, ctx) as ParsedTransaction;
4040
}
4141

42+
/**
43+
* Get the proxy deposit cost from runtime metadata.
44+
*
45+
* Returns `ProxyDepositBase + ProxyDepositFactor` from the Proxy pallet,
46+
* which represents the cost of adding or removing a proxy.
47+
*
48+
* This replaces the legacy account-lib `getAddProxyCost()` / `getRemoveProxyCost()`
49+
* without requiring any polkadot-js dependencies.
50+
*
51+
* @param metadataHex - Runtime metadata as a hex string (0x-prefixed or bare)
52+
* @returns Proxy deposit cost in planck as bigint
53+
*/
54+
export function getProxyDepositCost(metadataHex: string): bigint {
55+
return BigInt(ParserNamespace.getProxyDepositCost(metadataHex));
56+
}
57+
4258
/**
4359
* Create a ParseContextJs from ParseContext
4460
*/

packages/wasm-dot/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
pub mod address;
1515
pub mod builder;
1616
pub mod error;
17+
pub mod metadata_constants;
1718
pub mod parser;
1819
pub mod transaction;
1920
pub mod types;
@@ -22,6 +23,7 @@ pub mod wasm;
2223
// Re-export main types for convenience
2324
pub use address::{decode_ss58, encode_ss58, validate_address};
2425
pub use error::WasmDotError;
26+
pub use metadata_constants::get_proxy_deposit_cost;
2527
pub use parser::{parse_transaction, ParsedTransaction};
2628
pub use transaction::Transaction;
2729
pub use types::{Material, ParseContext, Validity};
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//! Extract pallet constants from runtime metadata.
2+
//!
3+
//! Provides functions to read SCALE-encoded constants from chain metadata
4+
//! without requiring any polkadot-js dependencies. Used by the WASM path
5+
//! to compute values like proxy deposit costs.
6+
7+
use crate::error::WasmDotError;
8+
use crate::transaction::decode_metadata;
9+
10+
/// Extract the proxy deposit cost from runtime metadata.
11+
///
12+
/// Returns `ProxyDepositBase + ProxyDepositFactor` from the Proxy pallet
13+
/// constants, which represents the cost of adding/removing a proxy.
14+
/// This matches the legacy account-lib `getAddProxyCost()` / `getRemoveProxyCost()`.
15+
///
16+
/// Both constants are u128 values (SCALE-encoded as 16 bytes LE).
17+
///
18+
/// # Arguments
19+
/// * `metadata_hex` - Runtime metadata as a hex string (0x-prefixed or bare),
20+
/// matching the `state_getMetadata` RPC wire format.
21+
///
22+
/// # Returns
23+
/// The sum as a decimal string (suitable for BigInt conversion in JS).
24+
pub fn get_proxy_deposit_cost(metadata_hex: &str) -> Result<u128, WasmDotError> {
25+
let metadata = decode_metadata(metadata_hex)?;
26+
27+
let proxy_pallet = metadata.pallet_by_name("Proxy").ok_or_else(|| {
28+
WasmDotError::InvalidInput("Proxy pallet not found in metadata".to_string())
29+
})?;
30+
31+
let base = proxy_pallet
32+
.constant_by_name("ProxyDepositBase")
33+
.ok_or_else(|| {
34+
WasmDotError::InvalidInput(
35+
"ProxyDepositBase constant not found in Proxy pallet".to_string(),
36+
)
37+
})?;
38+
39+
let factor = proxy_pallet
40+
.constant_by_name("ProxyDepositFactor")
41+
.ok_or_else(|| {
42+
WasmDotError::InvalidInput(
43+
"ProxyDepositFactor constant not found in Proxy pallet".to_string(),
44+
)
45+
})?;
46+
47+
let base_value = decode_u128_le(base.value(), "ProxyDepositBase")?;
48+
let factor_value = decode_u128_le(factor.value(), "ProxyDepositFactor")?;
49+
50+
Ok(base_value + factor_value)
51+
}
52+
53+
/// Decode a SCALE-encoded u128 from little-endian bytes.
54+
fn decode_u128_le(bytes: &[u8], name: &str) -> Result<u128, WasmDotError> {
55+
if bytes.len() < 16 {
56+
return Err(WasmDotError::ScaleDecodeError(format!(
57+
"{} constant has {} bytes, expected 16 for u128",
58+
name,
59+
bytes.len()
60+
)));
61+
}
62+
let mut buf = [0u8; 16];
63+
buf.copy_from_slice(&bytes[..16]);
64+
Ok(u128::from_le_bytes(buf))
65+
}
66+
67+
#[cfg(test)]
68+
mod tests {
69+
use super::*;
70+
71+
/// Test with real Westend metadata fixture.
72+
///
73+
/// Extracts ProxyDepositBase + ProxyDepositFactor from the Proxy pallet.
74+
/// The exact values depend on the metadata version in the fixture.
75+
#[test]
76+
fn test_get_proxy_deposit_cost_westend() {
77+
let metadata_bytes = include_bytes!("../test-fixtures/westend_metadata.scale");
78+
let metadata_hex = format!("0x{}", hex::encode(metadata_bytes));
79+
80+
let cost = get_proxy_deposit_cost(&metadata_hex).unwrap();
81+
// Verify the function returns a non-zero value and doesn't error
82+
assert!(cost > 0, "proxy deposit cost should be positive");
83+
// The Westend metadata in this fixture yields 1_002_050_000_000 planck
84+
// (ProxyDepositBase + ProxyDepositFactor for this runtime version)
85+
assert_eq!(cost, 1_002_050_000_000u128);
86+
}
87+
88+
/// Test individual constant extraction to verify correctness.
89+
#[test]
90+
fn test_proxy_deposit_individual_constants() {
91+
let metadata_bytes = include_bytes!("../test-fixtures/westend_metadata.scale");
92+
let metadata_hex = format!("0x{}", hex::encode(metadata_bytes));
93+
let metadata = decode_metadata(&metadata_hex).unwrap();
94+
95+
let proxy_pallet = metadata.pallet_by_name("Proxy").unwrap();
96+
97+
let base = proxy_pallet.constant_by_name("ProxyDepositBase").unwrap();
98+
let factor = proxy_pallet.constant_by_name("ProxyDepositFactor").unwrap();
99+
100+
let base_value = decode_u128_le(base.value(), "ProxyDepositBase").unwrap();
101+
let factor_value = decode_u128_le(factor.value(), "ProxyDepositFactor").unwrap();
102+
103+
// Both should be positive
104+
assert!(base_value > 0);
105+
assert!(factor_value > 0);
106+
// Base should be larger than factor (deposit base > per-proxy factor)
107+
assert!(base_value > factor_value);
108+
// Sum should match get_proxy_deposit_cost
109+
assert_eq!(base_value + factor_value, 1_002_050_000_000u128);
110+
}
111+
112+
#[test]
113+
fn test_get_proxy_deposit_cost_invalid_metadata() {
114+
let result = get_proxy_deposit_cost("0xdeadbeef");
115+
assert!(result.is_err());
116+
}
117+
118+
#[test]
119+
fn test_decode_u128_le_valid() {
120+
// 42 as u128 LE
121+
let bytes = 42u128.to_le_bytes();
122+
assert_eq!(decode_u128_le(&bytes, "test").unwrap(), 42);
123+
}
124+
125+
#[test]
126+
fn test_decode_u128_le_too_short() {
127+
let result = decode_u128_le(&[1, 2, 3], "test");
128+
assert!(result.is_err());
129+
}
130+
}

packages/wasm-dot/src/wasm/parser.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ impl ParserNamespace {
6868
let parsed = parse_from_transaction(tx.inner(), ctx.as_ref())?;
6969
to_js_value(&parsed)
7070
}
71+
72+
/// Get the proxy deposit cost from runtime metadata.
73+
///
74+
/// Returns `ProxyDepositBase + ProxyDepositFactor` from the Proxy pallet
75+
/// as a decimal string (for BigInt conversion).
76+
///
77+
/// This matches the legacy account-lib `getAddProxyCost()` / `getRemoveProxyCost()`.
78+
///
79+
/// @param metadataHex - Runtime metadata hex string (0x-prefixed or bare)
80+
/// @returns Proxy deposit cost as decimal string
81+
#[wasm_bindgen(js_name = getProxyDepositCost)]
82+
pub fn get_proxy_deposit_cost(metadata_hex: &str) -> Result<String, JsValue> {
83+
let cost = crate::metadata_constants::get_proxy_deposit_cost(metadata_hex)?;
84+
Ok(cost.to_string())
85+
}
7186
}
7287

7388
/// Convert ParsedTransaction to JsValue using serde_wasm_bindgen (JSON-compatible mode).
293 KB
Binary file not shown.

0 commit comments

Comments
 (0)