I want to make a library for the subaccount&account later.
impl FromStr for ICRCAccount {
type Err = ICRCAccountError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let n = s.len();
if n == 0 {
return Err(ICRCAccountError::Malformed("empty".into()));
}
let last_dash = s.rfind('-');
let dot = s.find('.');
match last_dash {
None => {
return Err(ICRCAccountError::Malformed(
"expected at least one dash ('-') character".into(),
));
}
Some(last_dash) => {
if let Some(dot) = dot {
// There is a subaccount
let num_subaccount_digits = n - dot - 1;
if num_subaccount_digits > 64 {
return Err(ICRCAccountError::Malformed(
"the subaccount is too long (expected at most 64 characters)".into(),
));
};
if dot < last_dash {
return Err(ICRCAccountError::Malformed(
"the subaccount separator does not follow the checksum separator"
.into(),
));
};
if dot - last_dash - 1 != 7 {
return Err(ICRCAccountError::BadChecksum);
};
// The encoding ends with a dot, the subaccount is empty.
if dot == n - 1 {
return Err(ICRCAccountError::NotCanonical);
};
// The first digit after the dot must not be a zero.
if s.chars().nth(dot + 1).unwrap() == '0' {
return Err(ICRCAccountError::NotCanonical);
};
let principal_text = &s[..last_dash];
let owner = Principal::from_text(principal_text)
.map_err(|e| ICRCAccountError::InvalidPrincipal(e.to_string()))?;
let hex_str = &s[dot + 1..];
// Check that the subaccount is not the default.
if hex_str.chars().all(|c| c == '0') {
return Err(ICRCAccountError::NotCanonical);
};
let subaccount = Subaccount::from_hex(&hex_str)
.map_err(|e| ICRCAccountError::InvalidSubaccount(e.to_string()))?;
// Check that the checksum matches the subaccount.
let checksum = &s[last_dash + 1..dot];
let expected_checksum = base32_encode(
&ICRCAccount {
owner,
subaccount: Some(subaccount.clone()),
}
.compute_checksum(),
);
if checksum != expected_checksum {
return Err(ICRCAccountError::BadChecksum);
};
Ok(ICRCAccount {
owner,
subaccount: Some(subaccount),
})
} else {
// There is no subaccount, so it's just a Principal
let owner = Principal::from_text(s)
.map_err(|e| ICRCAccountError::InvalidPrincipal(e.to_string()))?;
Ok(ICRCAccount {
owner,
subaccount: None,
})
}
}
}
}
}
and test result
let account_1 = ICRCAccount::from_text(
"k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae",
)
.unwrap();
let expected_1 = ICRCAccount {
owner: Principal::from_text(
"k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae",
)
.unwrap(),
subaccount: None,
};
assert_eq!(account_1, expected_1,);
let account_2 = "k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae"
.parse::<ICRCAccount>()
.unwrap();
let expected_2 = ICRCAccount {
owner: Principal::from_text(
"k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae",
)
.unwrap(),
subaccount: Some(Subaccount([0u8; 32])),
};
assert_eq!(account_2, expected_2);
let account_3 = ICRCAccount::from_text(
"k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae-7s4rpcq.101010101010101010101010101010101010101010101010101010101010101"
).unwrap();
let expected_3 = ICRCAccount {
owner: Principal::from_text(
"k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae",
)
.unwrap(),
subaccount: Some(Subaccount([1u8; 32])),
};
assert_eq!(account_3, expected_3);
let mut slices = [0u8; 32];
slices[31] = 0x01;
let account_4 = ICRCAccount {
owner: Principal::from_text(
"k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae",
)
.unwrap(),
subaccount: Some(Subaccount(slices)),
};
assert_eq!(
account_4,
"k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae-6cc627i.1"
.parse::<ICRCAccount>()
.unwrap()
);
let slices = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
0x1d, 0x1e, 0x1f, 0x20,
];
let account_5 = ICRCAccount {
owner: Principal::from_text(
"k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae",
)
.unwrap(),
subaccount: Some(Subaccount(slices)),
};
assert_eq!(
account_5,
"k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae-dfxgiyy.102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"
.parse::<ICRCAccount>()
.unwrap()
);
`