Skip to content

Commit

Permalink
[Binance]: Add cosmos-sdk/MsgSideChainStakeMigration message type (#…
Browse files Browse the repository at this point in the history
…3783)

* feat(binance): Add `cosmos-sdk/MsgSideChainStakeMigration`

* feat(binance): Fix rustfmt
  • Loading branch information
satoshiotomakan authored Apr 11, 2024
1 parent df60b69 commit 70f8636
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 1 deletion.
9 changes: 9 additions & 0 deletions rust/chains/tw_binance/src/transaction/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub enum BinanceMessageEnum {
SideDelegateOrder(side_chain_delegate::SideDelegateOrder),
SideRedelegateOrder(side_chain_delegate::SideRedelegateOrder),
SideUndelegateOrder(side_chain_delegate::SideUndelegateOrder),
StakeMigrationOrder(side_chain_delegate::StakeMigrationOrder),
TimeLockOrder(time_lock_order::TimeLockOrder),
TimeRelockOrder(time_lock_order::TimeRelockOrder),
TimeUnlockOrder(time_lock_order::TimeUnlockOrder),
Expand Down Expand Up @@ -133,6 +134,10 @@ impl TWBinanceProto for BinanceMessageEnum {
time_lock_order::TimeUnlockOrder::from_tw_proto(coin, order)
.map(BinanceMessageEnum::TimeUnlockOrder)
},
BinanceMessageProto::side_stake_migration_order(ref order) => {
side_chain_delegate::StakeMigrationOrder::from_tw_proto(coin, order)
.map(BinanceMessageEnum::StakeMigrationOrder)
},
BinanceMessageProto::None => Err(SigningError(SigningErrorType::Error_invalid_params)),
}
}
Expand All @@ -159,6 +164,9 @@ impl TWBinanceProto for BinanceMessageEnum {
BinanceMessageEnum::SideUndelegateOrder(m) => {
BinanceMessageProto::side_undelegate_order(m.to_tw_proto())
},
BinanceMessageEnum::StakeMigrationOrder(m) => {
BinanceMessageProto::side_stake_migration_order(m.to_tw_proto())
},
BinanceMessageEnum::TimeLockOrder(m) => {
BinanceMessageProto::time_lock_order(m.to_tw_proto())
},
Expand Down Expand Up @@ -207,6 +215,7 @@ impl<'a> AsRef<dyn BinanceMessage + 'a> for BinanceMessageEnum {
BinanceMessageEnum::SideDelegateOrder(m) => m,
BinanceMessageEnum::SideRedelegateOrder(m) => m,
BinanceMessageEnum::SideUndelegateOrder(m) => m,
BinanceMessageEnum::StakeMigrationOrder(m) => m,
BinanceMessageEnum::TimeLockOrder(m) => m,
BinanceMessageEnum::TimeRelockOrder(m) => m,
BinanceMessageEnum::TimeUnlockOrder(m) => m,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize};
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::CoinAddress;
use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult};
use tw_evm::address::Address as EthereumAddress;
use tw_memory::Data;
use tw_misc::serde::Typed;
use tw_proto::Binance::Proto;
Expand Down Expand Up @@ -204,3 +205,71 @@ impl TWBinanceProto for SideUndelegateOrder {
}
}
}

pub type StakeMigrationOrder = Typed<StakeMigrationOrderValue>;

/// https://github.com/bnb-chain/bnc-cosmos-sdk/blob/cf3ab19af300ccd6a6381287c3fae6bf6ac12f5e/x/stake/types/stake_migration.go#L29-L35
#[derive(Deserialize, Serialize)]
pub struct StakeMigrationOrderValue {
#[serde(serialize_with = "Token::serialize_with_string_amount")]
pub amount: Token,
pub delegator_addr: EthereumAddress,
pub refund_addr: BinanceAddress,
pub validator_dst_addr: EthereumAddress,
pub validator_src_addr: BinanceAddress,
}

impl StakeMigrationOrderValue {
/// cbindgen:ignore
/// https://github.com/bnb-chain/javascript-sdk/blob/442286ac2923fdfd7cb4fb2299f722ec263c714c/src/types/tx/stdTx.ts#L68
pub const PREFIX: [u8; 4] = [0x38, 0x58, 0x91, 0x96];
/// cbindgen:ignore
pub const MESSAGE_TYPE: &'static str = "cosmos-sdk/MsgSideChainStakeMigration";
}

impl BinanceMessage for StakeMigrationOrder {
fn to_amino_protobuf(&self) -> SigningResult<Data> {
Ok(AminoEncoder::new(&StakeMigrationOrderValue::PREFIX)
.extend_with_msg(&self.to_tw_proto())?
.encode())
}
}

impl TWBinanceProto for StakeMigrationOrder {
type Proto<'a> = Proto::SideChainStakeMigration<'a>;

fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult<Self> {
let delegator_addr = EthereumAddress::try_from(msg.delegator_addr.as_ref())?;
let refund_addr = BinanceAddress::from_key_hash_with_coin(coin, msg.refund_addr.to_vec())?;
let validator_dst_addr = EthereumAddress::try_from(msg.validator_dst_addr.as_ref())?;
let validator_src_addr =
BinanceAddress::new_validator_addr(msg.validator_src_addr.to_vec())?;

let amount = msg
.amount
.as_ref()
.ok_or(SigningError(SigningErrorType::Error_invalid_params))?;

let value = StakeMigrationOrderValue {
amount: Token::from_tw_proto(amount),
delegator_addr,
refund_addr,
validator_dst_addr,
validator_src_addr,
};
Ok(Typed {
ty: StakeMigrationOrderValue::MESSAGE_TYPE.to_string(),
value,
})
}

fn to_tw_proto(&self) -> Self::Proto<'static> {
Proto::SideChainStakeMigration {
delegator_addr: self.value.delegator_addr.data().into(),
validator_src_addr: self.value.validator_src_addr.data().into(),
validator_dst_addr: self.value.validator_dst_addr.data().into(),
refund_addr: self.value.refund_addr.data().into(),
amount: Some(self.value.amount.to_tw_proto()),
}
}
}
50 changes: 49 additions & 1 deletion rust/tw_any_coin/tests/chains/binance/binance_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use crate::chains::binance::{
make_token, ACCOUNT_12_PRIVATE_KEY, ACCOUNT_15_PRIVATE_KEY, ACCOUNT_16_PRIVATE_KEY,
ACCOUNT_19_PRIVATE_KEY,
ACCOUNT_19_PRIVATE_KEY, ACCOUNT_91147_PRIVATE_KEY,
};
use tw_any_coin::test_utils::sign_utils::AnySignerHelper;
use tw_coin_registry::coin_type::CoinType;
Expand Down Expand Up @@ -681,6 +681,54 @@ fn test_binance_sign_side_chain_undelegate_order() {
assert_eq!(output.signature_json, expected_signature_json);
}

#[test]
fn test_binance_sign_side_chain_stake_migration_order() {
// Current staking delegator:
// tbnb1rr74uvz8rcvl5dqn43jkwdufx5aksp4zwzszvs
let refund_addr_key_hash = "18fd5e30471e19fa3413ac65673789353b6806a2";
// Where the staking amount will be re-delegated:
// 0xCAAc3DAf661b6cEFF18DB1C8fCC2C2fDA1B73893
let delegator_key_hash = "CAAc3DAf661b6cEFF18DB1C8fCC2C2fDA1B73893";
// BNB Beacon Chain - ARARAT validator has the following addresses:
// 1. bva1p7s26ervsmv3w83k5696glautc9sm5rchz5f5e (if using `bva` validator HRP);
// 2. tbnb1p7s26ervsmv3w83k5696glautc9sm5rcetua2v (if using `tbnb` testnet HRP).
let validator_src_key_hash = "0fa0ad646c86d9171e36a68ba47fbc5e0b0dd078";
// Binance Smart Chain - ARARAT validator has the following address:
// 0x341e228f22D4ec16297DD05A9d6347C74c125F66.
let validator_dst_key_hash = "0x341e228f22D4ec16297DD05A9d6347C74c125F66";

let stake_migration = Proto::SideChainStakeMigration {
delegator_addr: delegator_key_hash.decode_hex().unwrap().into(),
validator_src_addr: validator_src_key_hash.decode_hex().unwrap().into(),
validator_dst_addr: validator_dst_key_hash.decode_hex().unwrap().into(),
refund_addr: refund_addr_key_hash.decode_hex().unwrap().into(),
// 3.21 BNB
amount: Some(make_token("BNB", 321000000)),
};

let input = Proto::SigningInput {
chain_id: "Binance-Chain-Ganges".into(),
account_number: 91147,
sequence: 11,
private_key: ACCOUNT_91147_PRIVATE_KEY.decode_hex().unwrap().into(),
order_oneof: OrderEnum::side_stake_migration_order(stake_migration),
..Proto::SigningInput::default()
};

let mut signer = AnySignerHelper::<Proto::SigningOutput>::default();
let output = signer.sign(CoinType::TBinance, input);

assert_eq!(output.error, SigningError::OK);
// Successfully broadcasted tx hash: 94E2FC487D93F9744EB0C6BEED7AE1E5F5AF7D6B756B5EEE9AB1E6CB02C7D015
// Note it's not indexed by the explorer for some reason.
assert_eq!(output.encoded.to_hex(), "e101f0625dee0a69385891960a140fa0ad646c86d9171e36a68ba47fbc5e0b0dd0781214341e228f22d4ec16297dd05a9d6347c74c125f661a14caac3daf661b6ceff18db1c8fcc2c2fda1b73893221418fd5e30471e19fa3413ac65673789353b6806a22a0b0a03424e4210c0a488990112700a26eb5ae987210243107052476885baddb4fad4df3fa07f4df807eac64daf6035e25232180f8c6a124065a45c4001488121e8199c3d87bc3dbcf1612ba690f0c474cc94f9a060a9e1bc302943dca5cd9240bba1c78a1691700e23d538efad79cee856a34595247249b6188bc805200b");

let expected_signature = "65a45c4001488121e8199c3d87bc3dbcf1612ba690f0c474cc94f9a060a9e1bc302943dca5cd9240bba1c78a1691700e23d538efad79cee856a34595247249b6";
assert_eq!(output.signature.to_hex(), expected_signature);
let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AkMQcFJHaIW63bT61N8/oH9N+Afqxk2vYDXiUjIYD4xq"},"signature":"ZaRcQAFIgSHoGZw9h7w9vPFhK6aQ8MR0zJT5oGCp4bwwKUPcpc2SQLuhx4oWkXAOI9U47615zuhWo0WVJHJJtg=="}"#;
assert_eq!(output.signature_json, expected_signature_json);
}

#[test]
fn test_binance_sign_time_lock_order() {
let from_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1";
Expand Down
3 changes: 3 additions & 0 deletions rust/tw_any_coin/tests/chains/binance/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const ACCOUNT_15_PRIVATE_KEY: &str =
"eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d";
const ACCOUNT_16_PRIVATE_KEY: &str =
"851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a";
/// tbnb1rr74uvz8rcvl5dqn43jkwdufx5aksp4zwzszvs
const ACCOUNT_91147_PRIVATE_KEY: &str =
"56b1253d944956c7f5b7668892509a44290f1fd149edecbe4fd44f69ba04b84c";

fn make_token(denom: &str, amount: i64) -> Proto::mod_SendOrder::Token {
Proto::mod_SendOrder::Token {
Expand Down
9 changes: 9 additions & 0 deletions rust/tw_evm/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ impl FromStr for Address {
}
}

impl<'a> TryFrom<&'a [u8]> for Address {
type Error = AddressError;

fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
let bytes = H160::try_from(bytes).map_err(|_| AddressError::InvalidInput)?;
Ok(Address { bytes })
}
}

impl EvmAddress for Address {}

/// Implement `str` -> `PrivateKey` conversion for test purposes.
Expand Down
11 changes: 11 additions & 0 deletions src/proto/Binance.proto
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,16 @@ message SideChainUndelegate {
string chain_id = 4;
}

// Message for BNB Beacon Chain -> BSC Stake Migration.
// https://github.com/bnb-chain/javascript-sdk/blob/26f6db8b67326e6214e74203ff90c89777b592a1/src/types/msg/stake/stakeMigrationMsg.ts#L13-L18
message SideChainStakeMigration {
bytes validator_src_addr = 1;
bytes validator_dst_addr = 2;
bytes delegator_addr = 3;
bytes refund_addr = 4;
SendOrder.Token amount = 5;
}

// Message for TimeLock order
message TimeLockOrder {
// owner address
Expand Down Expand Up @@ -369,6 +379,7 @@ message SigningInput {
TimeLockOrder time_lock_order = 24;
TimeRelockOrder time_relock_order = 25;
TimeUnlockOrder time_unlock_order = 26;
SideChainStakeMigration side_stake_migration_order = 27;
}
}

Expand Down

0 comments on commit 70f8636

Please sign in to comment.