//! Resource Id Update Proposal.
use crate::{ProposalHeader, ResourceId};
use cosmwasm_std::{from_slice, to_binary};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// Resource Id Update Proposal.
///
/// The [`ResourceIdUpdateProposal`] maps a new resource Id to a handler
/// address.
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ResourceIdUpdateProposal {
header: ProposalHeader,
new_resource_id: ResourceId,
handler_address: String,
}
impl ResourceIdUpdateProposal {
/// Creates a new resource id update proposal.
#[must_use]
pub const fn new(
header: ProposalHeader,
new_resource_id: ResourceId,
handler_address: String,
) -> Self {
Self {
header,
new_resource_id,
handler_address,
}
}
/// Get the proposal header.
#[must_use]
pub const fn header(&self) -> ProposalHeader {
self.header
}
/// Get the new resource id.
#[must_use]
pub const fn new_resource_id(&self) -> ResourceId {
self.new_resource_id
}
/// Get the handler address.
#[must_use]
pub fn handler_address(&self) -> String {
self.handler_address.clone()
}
/// Get the execution address.
#[must_use]
pub fn execution_address(&self) -> [u8; 20] {
self.new_resource_id()
.target_system()
.into_contract_address_or_default()
}
/// Get the proposal as a bytes
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = vec![];
bytes.extend_from_slice(&self.header.to_bytes());
let message = to_binary(&ResourceIdUpdateData {
resource_id: self.header.resource_id().to_bytes(),
function_sig: self.header.function_signature().to_bytes(),
nonce: self.header.nonce().0,
new_resource_id: self.new_resource_id.to_bytes(),
handler_addr: self.handler_address.clone(),
})
.unwrap();
bytes.extend_from_slice(message.as_slice());
bytes
}
/// Get the proposal as a bytes without copying.
#[must_use]
pub fn into_bytes(self) -> Vec<u8> {
self.to_bytes()
}
}
impl From<Vec<u8>> for ResourceIdUpdateProposal {
fn from(bytes: Vec<u8>) -> Self {
let f = 0usize;
let t = ProposalHeader::LENGTH;
let mut header_bytes = [0u8; ProposalHeader::LENGTH];
header_bytes.copy_from_slice(&bytes[f..t]);
let header = ProposalHeader::from(header_bytes);
let f = t;
let decoded_msg: ResourceIdUpdateData =
from_slice(&bytes[f..]).unwrap();
Self {
header,
new_resource_id: decoded_msg.new_resource_id.into(),
handler_address: decoded_msg.handler_addr,
}
}
}
impl From<ResourceIdUpdateProposal> for Vec<u8> {
fn from(proposal: ResourceIdUpdateProposal) -> Self {
proposal.to_bytes()
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
struct ResourceIdUpdateData {
pub resource_id: [u8; 32],
pub function_sig: [u8; 4],
pub nonce: u32,
pub new_resource_id: [u8; 32],
pub handler_addr: String,
}
#[cfg(test)]
mod tests {
use crate::{
cosmwasm::cosmos_address_to_target_address, FunctionSignature, Nonce,
ResourceId, TargetSystem, TypedChainId,
};
use super::*;
const TARGET_CONTRACT_ADDR: &str =
"juno1hset4pny4h8xm4s4lek57msq7j4zwfqwjf7zxqjt4npxyv0lrgnsp8qy9j";
const NEW_TARGET_CONTRACT_ADDR: &str =
"juno18edfd7nquv9zfgprt5lpzcykczw0llwqcc40f5lkxq2vdw7nnp7qr7a22a";
const HANDLER_ADDR: &str =
"juno1u235cpgju5vvlzp4w53vu0z5x3etytdpeh78ffekctfcmfc8ezhs9p248h";
#[test]
fn encode() {
let target_addr =
cosmos_address_to_target_address(TARGET_CONTRACT_ADDR);
let target_system = TargetSystem::ContractAddress(target_addr);
let target_chain = TypedChainId::Cosmos(4);
let resource_id = ResourceId::new(target_system, target_chain);
let function_signature =
FunctionSignature::new(hex_literal::hex!("00000000"));
let nonce = Nonce::from(0x0001);
let header =
ProposalHeader::new(resource_id, function_signature, nonce);
let new_target_addr =
cosmos_address_to_target_address(NEW_TARGET_CONTRACT_ADDR);
let new_target_system = TargetSystem::ContractAddress(new_target_addr);
let new_resource_id = ResourceId::new(new_target_system, target_chain);
let handler_address = HANDLER_ADDR.to_string();
let proposal = ResourceIdUpdateProposal::new(
header,
new_resource_id,
handler_address,
);
let bytes = proposal.to_bytes();
let expected = hex_literal::hex!(
"000000000000b37383a2ad2de9e68da75f583e7d0ef2eae1184f04000000000400000000000000017b227265736f757263655f6964223a5b302c302c302c302c302c302c3137392c3131352c3133312c3136322c3137332c34352c3233332c3233302c3134312c3136372c39352c38382c36322c3132352c31342c3234322c3233342c3232352c32342c37392c342c302c302c302c302c345d2c2266756e6374696f6e5f736967223a5b302c302c302c305d2c226e6f6e6365223a312c226e65775f7265736f757263655f6964223a5b302c302c302c302c302c302c39382c3139332c3130352c3130362c3134392c3138312c33392c3134362c32332c3133392c3233352c3233382c34312c33312c3132372c39372c3135322c31372c39372c3136332c342c302c302c302c302c345d2c2268616e646c65725f61646472223a226a756e6f31753233356370676a753576766c7a70347735337675307a357833657479746470656837386666656b637466636d666338657a6873397032343868227d"
);
assert_eq!(bytes, expected);
}
#[test]
fn decode() {
// do the reverse of encode
let bytes = hex_literal::hex!(
"000000000000b37383a2ad2de9e68da75f583e7d0ef2eae1184f04000000000400000000000000017b227265736f757263655f6964223a5b302c302c302c302c302c302c3137392c3131352c3133312c3136322c3137332c34352c3233332c3233302c3134312c3136372c39352c38382c36322c3132352c31342c3234322c3233342c3232352c32342c37392c342c302c302c302c302c345d2c2266756e6374696f6e5f736967223a5b302c302c302c305d2c226e6f6e6365223a312c226e65775f7265736f757263655f6964223a5b302c302c302c302c302c302c39382c3139332c3130352c3130362c3134392c3138312c33392c3134362c32332c3133392c3233352c3233382c34312c33312c3132372c39372c3135322c31372c39372c3136332c342c302c302c302c302c345d2c2268616e646c65725f61646472223a226a756e6f31753233356370676a753576766c7a70347735337675307a357833657479746470656837386666656b637466636d666338657a6873397032343868227d"
);
let proposal = ResourceIdUpdateProposal::from(bytes.to_vec());
let target_addr =
cosmos_address_to_target_address(TARGET_CONTRACT_ADDR);
let target_system = TargetSystem::ContractAddress(target_addr);
let target_chain = TypedChainId::Cosmos(4);
let resource_id = ResourceId::new(target_system, target_chain);
let function_signature =
FunctionSignature::new(hex_literal::hex!("00000000"));
let nonce = Nonce::from(0x0001);
let header =
ProposalHeader::new(resource_id, function_signature, nonce);
let new_target_addr =
cosmos_address_to_target_address(NEW_TARGET_CONTRACT_ADDR);
let new_target_system = TargetSystem::ContractAddress(new_target_addr);
let new_resource_id = ResourceId::new(new_target_system, target_chain);
let handler_address = HANDLER_ADDR.to_string();
let expected_proposal = ResourceIdUpdateProposal::new(
header,
new_resource_id,
handler_address,
);
assert_eq!(proposal, expected_proposal);
}
}