From bba86113c0dfabcfd1e17077abe4018cb23a9da0 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Mon, 29 May 2023 13:43:35 +0545 Subject: [PATCH 01/37] Balanced_HubToken: rlp library added --- libraries/rust/common/src/rlp/error.rs | 51 +++ libraries/rust/common/src/rlp/impls.rs | 266 ++++++++++++ libraries/rust/common/src/rlp/mod.rs | 126 ++++++ libraries/rust/common/src/rlp/nullable.rs | 44 ++ libraries/rust/common/src/rlp/rlpin.rs | 461 +++++++++++++++++++++ libraries/rust/common/src/rlp/stream.rs | 466 ++++++++++++++++++++++ libraries/rust/common/src/rlp/traits.rs | 31 ++ 7 files changed, 1445 insertions(+) create mode 100644 libraries/rust/common/src/rlp/error.rs create mode 100644 libraries/rust/common/src/rlp/impls.rs create mode 100644 libraries/rust/common/src/rlp/mod.rs create mode 100644 libraries/rust/common/src/rlp/nullable.rs create mode 100644 libraries/rust/common/src/rlp/rlpin.rs create mode 100644 libraries/rust/common/src/rlp/stream.rs create mode 100644 libraries/rust/common/src/rlp/traits.rs diff --git a/libraries/rust/common/src/rlp/error.rs b/libraries/rust/common/src/rlp/error.rs new file mode 100644 index 0000000..558b274 --- /dev/null +++ b/libraries/rust/common/src/rlp/error.rs @@ -0,0 +1,51 @@ +// Copyright 2020 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::fmt; +#[cfg(feature = "std")] +use std::error::Error as StdError; + +#[derive(Debug, PartialEq, Eq, Clone)] +/// Error concerning the RLP decoder. +pub enum DecoderError { + /// Data has additional bytes at the end of the valid RLP fragment. + RlpIsTooBig, + /// Data has too few bytes for valid RLP. + RlpIsTooShort, + /// Expect an encoded list, RLP was something else. + RlpExpectedToBeList, + /// Expect encoded data, RLP was something else. + RlpExpectedToBeData, + /// Expected a different size list. + RlpIncorrectListLen, + /// Data length number has a prefixed zero byte, invalid for numbers. + RlpDataLenWithZeroPrefix, + /// List length number has a prefixed zero byte, invalid for numbers. + RlpListLenWithZeroPrefix, + /// Non-canonical (longer than necessary) representation used for data or list. + RlpInvalidIndirection, + /// Declared length is inconsistent with data specified after. + RlpInconsistentLengthAndData, + /// Declared length is invalid and results in overflow + RlpInvalidLength, + /// Custom rlp decoding error. + Custom(&'static str), +} + +#[cfg(feature = "std")] +impl StdError for DecoderError { + fn description(&self) -> &str { + "builder error" + } +} + +impl fmt::Display for DecoderError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self, f) + } +} diff --git a/libraries/rust/common/src/rlp/impls.rs b/libraries/rust/common/src/rlp/impls.rs new file mode 100644 index 0000000..a01f526 --- /dev/null +++ b/libraries/rust/common/src/rlp/impls.rs @@ -0,0 +1,266 @@ +// Copyright 2020 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use bytes::{Bytes, BytesMut}; +use core::{ + iter::{empty, once}, + mem, str, +}; + +use crate::rlp::{ + error::DecoderError, + rlpin::Rlp, + stream::RlpStream, + traits::{Decodable, Encodable}, +}; + +pub fn decode_usize(bytes: &[u8]) -> Result { + match bytes.len() { + l if l <= mem::size_of::() => { + if bytes[0] == 0 { + return Err(DecoderError::RlpInvalidIndirection); + } + let mut res = 0usize; + for (i, byte) in bytes.iter().enumerate().take(l) { + let shift = (l - 1 - i) * 8; + res += (*byte as usize) << shift; + } + Ok(res) + } + _ => Err(DecoderError::RlpIsTooBig), + } +} + +impl Encodable for Box { + fn rlp_append(&self, s: &mut RlpStream) { + Encodable::rlp_append(&**self, s) + } +} + +impl Decodable for Box { + fn decode(rlp: &Rlp) -> Result { + T::decode(rlp).map(Box::new) + } +} + +impl Encodable for bool { + fn rlp_append(&self, s: &mut RlpStream) { + let as_uint = u8::from(*self); + Encodable::rlp_append(&as_uint, s); + } +} + +impl Decodable for bool { + fn decode(rlp: &Rlp) -> Result { + let as_uint = ::decode(rlp)?; + match as_uint { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(DecoderError::Custom("invalid boolean value")), + } + } +} + +impl<'a> Encodable for &'a [u8] { + fn rlp_append(&self, s: &mut RlpStream) { + s.encoder().encode_value(self); + } +} + +impl Encodable for Vec { + fn rlp_append(&self, s: &mut RlpStream) { + s.encoder().encode_value(self); + } +} + +impl Decodable for Vec { + fn decode(rlp: &Rlp) -> Result { + rlp.decoder().decode_value(|bytes| Ok(bytes.to_vec())) + } +} + +impl Encodable for Bytes { + fn rlp_append(&self, s: &mut RlpStream) { + s.encoder().encode_value(self); + } +} + +impl Decodable for Bytes { + fn decode(rlp: &Rlp) -> Result { + rlp.decoder() + .decode_value(|bytes| Ok(Bytes::copy_from_slice(bytes))) + } +} + +impl Encodable for BytesMut { + fn rlp_append(&self, s: &mut RlpStream) { + s.encoder().encode_value(self); + } +} + +impl Decodable for BytesMut { + fn decode(rlp: &Rlp) -> Result { + rlp.decoder().decode_value(|bytes| Ok(bytes.into())) + } +} + +impl Encodable for Option +where + T: Encodable, +{ + fn rlp_append(&self, s: &mut RlpStream) { + match *self { + None => { + s.begin_list(0); + } + Some(ref value) => { + s.begin_list(1); + s.append(value); + } + } + } +} + +impl Decodable for Option +where + T: Decodable, +{ + fn decode(rlp: &Rlp) -> Result { + let items = rlp.item_count()?; + match items { + 1 => rlp.val_at(0).map(Some), + 0 => Ok(None), + _ => Err(DecoderError::RlpIncorrectListLen), + } + } +} + +impl Encodable for u8 { + fn rlp_append(&self, s: &mut RlpStream) { + s.encoder().encode_iter(once(*self)); + } +} + +impl Decodable for u8 { + fn decode(rlp: &Rlp) -> Result { + rlp.decoder().decode_value(|bytes| match bytes.len() { + 1 => Ok(bytes[0]), + 0 => Ok(0), + _ => Err(DecoderError::RlpIsTooBig), + }) + } +} + +macro_rules! impl_encodable_for_u { + ($name: ident) => { + impl Encodable for $name { + fn rlp_append(&self, s: &mut RlpStream) { + let bytes = |mut v: $name| -> Vec { + if v == 0 { + vec![0] + } else { + let mut buffer: Vec = vec![0_u8; 16]; + for i in (0..=15).rev() { + let b: u8 = (v & 0xff) as u8; + buffer[i] = b; + v >>= 8; + if v == 0 && (b & 0x80) == 0 { + return buffer[i..].to_vec(); + } + } + buffer + } + }(*self); + s.encoder().encode_value(&bytes); + } + } + }; +} + +macro_rules! impl_decodable_for_u { + ($name: ident) => { + impl Decodable for $name { + fn decode(rlp: &Rlp) -> Result { + rlp.decoder().decode_value(|bytes| match bytes.len() { + 0 | 1 => u8::decode(rlp).map(|v| v as $name), + l if l <= mem::size_of::<$name>() => { + let mut res = 0 as $name; + for (i, byte) in bytes.iter().enumerate().take(l) { + let shift = (l - 1 - i) * 8; + res += (*byte as $name) << shift; + } + Ok(res) + } + _ => Err(DecoderError::RlpIsTooBig), + }) + } + } + }; +} + +impl_encodable_for_u!(u16); +impl_encodable_for_u!(u32); +impl_encodable_for_u!(u64); +impl_encodable_for_u!(u128); + +impl_decodable_for_u!(u16); +impl_decodable_for_u!(u32); +impl_decodable_for_u!(u64); +impl_decodable_for_u!(u128); + +impl Encodable for usize { + fn rlp_append(&self, s: &mut RlpStream) { + (*self as u64).rlp_append(s); + } +} + +impl Decodable for usize { + fn decode(rlp: &Rlp) -> Result { + u64::decode(rlp).map(|value| value as usize) + } +} + +impl<'a> Encodable for &'a str { + fn rlp_append(&self, s: &mut RlpStream) { + s.encoder().encode_value(self.as_bytes()); + } +} + +impl Encodable for String { + fn rlp_append(&self, s: &mut RlpStream) { + s.encoder().encode_value(self.as_bytes()); + } +} + +impl Decodable for String { + fn decode(rlp: &Rlp) -> Result { + rlp.decoder().decode_value(|bytes| { + match str::from_utf8(bytes) { + Ok(s) => Ok(s.to_owned()), + // consider better error type here + Err(_err) => Err(DecoderError::RlpExpectedToBeData), + } + }) + } +} + +impl Encodable for i8 { + fn rlp_append(&self, s: &mut RlpStream) { + Encodable::rlp_append(&(*self as u8), s); + } +} + +impl Decodable for i8 { + fn decode(rlp: &Rlp) -> Result { + rlp.decoder() + .decode_value(|bytes| match bytes.len() as u32 { + len if len == u8::BITS / 8 => Ok(bytes[0] as i8), + _ => Err(DecoderError::RlpInvalidLength), + }) + } +} diff --git a/libraries/rust/common/src/rlp/mod.rs b/libraries/rust/common/src/rlp/mod.rs new file mode 100644 index 0000000..d2d1b67 --- /dev/null +++ b/libraries/rust/common/src/rlp/mod.rs @@ -0,0 +1,126 @@ +#![allow(unused)] +// Copyright 2020 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Recursive Length Prefix serialization crate. +//! +//! Allows encoding, decoding, and view onto rlp-slice +//! +//! # What should you use when? +//! +//! ### Use `encode` function when: +//! * You want to encode something inline. +//! * You do not work on big set of data. +//! * You want to encode whole data structure at once. +//! +//! ### Use `decode` function when: +//! * You want to decode something inline. +//! * You do not work on big set of data. +//! * You want to decode whole rlp at once. +//! +//! ### Use `RlpStream` when: +//! * You want to encode something in portions. +//! * You encode a big set of data. +//! +//! ### Use `Rlp` when: +//! * You need to handle data corruption errors. +//! * You are working on input data. +//! * You want to get view onto rlp-slice. +//! * You don't want to decode whole rlp at once. + +mod error; +mod impls; +mod nullable; +mod rlpin; +mod stream; +mod traits; + +use bytes::BytesMut; +use core::borrow::Borrow; + +pub use self::{ + error::DecoderError, + rlpin::{PayloadInfo, Prototype, Rlp, RlpIterator}, + stream::RlpStream, + traits::{Decodable, Encodable}, +}; + +/// The RLP encoded empty data (used to mean "null value"). +pub const NULL_RLP: [u8; 2] = [0xf8, 0x00]; +/// The RLP encoded empty list. +pub const EMPTY_LIST_RLP: [u8; 1] = [0xC0; 1]; +pub use nullable::Nullable; +/// Shortcut function to decode trusted rlp +/// +/// ``` +/// use common::rlp::{self}; +/// let data = vec![0x83, b'c', b'a', b't']; +/// let animal: String = rlp::decode(&data).expect("could not decode"); +/// assert_eq!(animal, "cat".to_owned()); +/// ``` +pub fn decode(bytes: &[u8]) -> Result +where + T: Decodable, +{ + let rlp = Rlp::new(bytes); + rlp.as_val() +} + +pub fn decode_list(bytes: &[u8]) -> Vec +where + T: Decodable, +{ + let rlp = Rlp::new(bytes); + rlp.as_list().expect("trusted rlp should be valid") +} + +/// Shortcut function to encode structure into rlp. +/// +/// ``` +/// use common::rlp::{self}; +/// let animal = "cat"; +/// let out = rlp::encode(&animal); +/// assert_eq!(out, vec![0x83, b'c', b'a', b't']); +/// ``` +pub fn encode(object: &E) -> BytesMut +where + E: Encodable, +{ + let mut stream = RlpStream::new(); + stream.append(object); + stream.out() +} + +pub fn encode_list(object: &[K]) -> BytesMut +where + E: Encodable, + K: Borrow, +{ + let mut stream = RlpStream::new(); + stream.append_list(object); + stream.out() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_solidity_encoding() { + let expected = "00"; + let zero: u64 = 0; + let tag: u64 = 128; + let false_val = false; + let result = super::encode(&zero).to_vec(); + assert_eq!(expected, hex::encode(result)); + let result = super::encode(&false_val).to_vec(); + assert_eq!(expected, hex::encode(result)); + let result = super::encode(&tag).to_vec(); + assert_eq!("820080", hex::encode(result)); + } +} diff --git a/libraries/rust/common/src/rlp/nullable.rs b/libraries/rust/common/src/rlp/nullable.rs new file mode 100644 index 0000000..b124232 --- /dev/null +++ b/libraries/rust/common/src/rlp/nullable.rs @@ -0,0 +1,44 @@ +use serde::{Deserialize, Serialize}; + +use super::*; + +#[derive(Clone, Default, PartialEq, Eq, Debug, Serialize, Deserialize)] +pub struct Nullable(pub Option); + +impl Nullable { + pub fn new(item: Option) -> Self { + Self(item) + } + + pub fn is_some(&self) -> bool { + self.0.is_some() + } + + pub fn is_none(&self) -> bool { + self.0.is_none() + } + + pub fn get(&self) -> Result<&T, &'static str> { + self.0.as_ref().ok_or("object is null") + } +} + +impl Decodable for Nullable { + fn decode(rlp: &Rlp) -> Result { + if rlp.is_null() { + Ok(Self(None)) + } else { + Ok(Self(Some(rlp.as_val()?))) + } + } +} + +impl Encodable for Nullable { + fn rlp_append(&self, stream: &mut RlpStream) { + if self.is_none() { + stream.append_null_internal(); + } else { + stream.append_internal(self.get().unwrap()); + } + } +} diff --git a/libraries/rust/common/src/rlp/rlpin.rs b/libraries/rust/common/src/rlp/rlpin.rs new file mode 100644 index 0000000..f8abba5 --- /dev/null +++ b/libraries/rust/common/src/rlp/rlpin.rs @@ -0,0 +1,461 @@ +// Copyright 2020 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::{cell::Cell, fmt}; + +use rustc_hex::ToHex; + +use crate::rlp::{error::DecoderError, impls::decode_usize, traits::Decodable}; + +/// rlp offset +#[derive(Copy, Clone, Debug)] +struct OffsetCache { + index: usize, + offset: usize, +} + +impl OffsetCache { + const fn new(index: usize, offset: usize) -> OffsetCache { + OffsetCache { index, offset } + } +} + +#[derive(Debug)] +/// RLP prototype +pub enum Prototype { + /// Empty + Null, + /// Value + Data(usize), + /// List + List(usize), +} + +/// Stores basic information about item +#[derive(Debug)] +pub struct PayloadInfo { + /// Header length in bytes + pub header_len: usize, + /// Value length in bytes + pub value_len: usize, +} + +fn calculate_payload_info( + header_bytes: &[u8], + len_of_len: usize, +) -> Result { + let header_len = 1 + len_of_len; + match header_bytes.get(1) { + Some(&0) => return Err(DecoderError::RlpDataLenWithZeroPrefix), + None => return Err(DecoderError::RlpIsTooShort), + _ => (), + } + if header_bytes.len() < header_len { + return Err(DecoderError::RlpIsTooShort); + } + let value_len = decode_usize(&header_bytes[1..header_len])?; + if value_len <= 55 { + return Err(DecoderError::RlpInvalidIndirection); + } + Ok(PayloadInfo::new(header_len, value_len)) +} + +impl PayloadInfo { + const fn new(header_len: usize, value_len: usize) -> PayloadInfo { + PayloadInfo { + header_len, + value_len, + } + } + + /// Total size of the RLP. + pub fn total(&self) -> usize { + self.header_len + self.value_len + } + + /// Create a new object from the given bytes RLP. The bytes + pub fn from(header_bytes: &[u8]) -> Result { + let l = *header_bytes.first().ok_or(DecoderError::RlpIsTooShort)?; + + if l <= 0x7f { + Ok(PayloadInfo::new(0, 1)) + } else if l <= 0xb7 { + Ok(PayloadInfo::new(1, l as usize - 0x80)) + } else if l <= 0xbf { + let len_of_len = l as usize - 0xb7; + calculate_payload_info(header_bytes, len_of_len) + } else if l <= 0xf7 { + Ok(PayloadInfo::new(1, l as usize - 0xc0)) + } else if l == 0xf8 && header_bytes[1] == 0 { + Ok(PayloadInfo::new(1, 1)) + } else { + let len_of_len = l as usize - 0xf7; + calculate_payload_info(header_bytes, len_of_len) + } + } +} + +/// Data-oriented view onto rlp-slice. +/// +/// This is an immutable structure. No operations change it. +/// +/// Should be used in places where, error handling is required, +/// eg. on input +#[derive(Debug, Clone)] +pub struct Rlp<'a> { + bytes: &'a [u8], + offset_cache: Cell>, + count_cache: Cell>, +} + +impl<'a> fmt::Display for Rlp<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self.prototype() { + Ok(Prototype::Null) => write!(f, "null"), + Ok(Prototype::Data(_)) => { + write!(f, "\"0x{}\"", self.data().unwrap().to_hex::()) + } + Ok(Prototype::List(len)) => { + write!(f, "[")?; + for i in 0..len - 1 { + write!(f, "{}, ", self.at(i).unwrap())?; + } + write!(f, "{}", self.at(len - 1).unwrap())?; + write!(f, "]") + } + Err(err) => write!(f, "{err:?}"), + } + } +} + +impl<'a> Rlp<'a> { + pub const fn new(bytes: &'a [u8]) -> Rlp<'a> { + Rlp { + bytes, + offset_cache: Cell::new(None), + count_cache: Cell::new(None), + } + } + + pub fn as_raw<'view>(&'view self) -> &'a [u8] + where + 'a: 'view, + { + self.bytes + } + + pub fn prototype(&self) -> Result { + // optimize? && return appropriate errors + if self.is_data() { + Ok(Prototype::Data(self.size())) + } else if self.is_list() { + self.item_count().map(Prototype::List) + } else { + Ok(Prototype::Null) + } + } + + pub fn payload_info(&self) -> Result { + BasicDecoder::payload_info(self.bytes) + } + + pub fn data<'view>(&'view self) -> Result<&'a [u8], DecoderError> + where + 'a: 'view, + { + let pi = BasicDecoder::payload_info(self.bytes)?; + Ok(&self.bytes[pi.header_len..(pi.header_len + pi.value_len)]) + } + + pub fn item_count(&self) -> Result { + if self.is_list() { + match self.count_cache.get() { + Some(c) => Ok(c), + None => { + let c = self.iter().count(); + self.count_cache.set(Some(c)); + Ok(c) + } + } + } else { + Err(DecoderError::RlpExpectedToBeList) + } + } + + pub fn size(&self) -> usize { + if self.is_data() { + // TODO: No panic on malformed data, but ideally would Err on no PayloadInfo. + BasicDecoder::payload_info(self.bytes) + .map(|b| b.value_len) + .unwrap_or(0) + } else { + 0 + } + } + + /// Returns an Rlp item in a list at the given index. + /// + /// Returns an error if this Rlp is not a list or if the index is out of range. + pub fn at<'view>(&'view self, index: usize) -> Result, DecoderError> + where + 'a: 'view, + { + let (rlp, _offset) = self.at_with_offset(index)?; + Ok(rlp) + } + + /// Returns an Rlp item in a list at the given index along with the byte offset into the + /// raw data slice. + /// + /// Returns an error if this Rlp is not a list or if the index is out of range. + pub fn at_with_offset<'view>( + &'view self, + index: usize, + ) -> Result<(Rlp<'a>, usize), DecoderError> + where + 'a: 'view, + { + if !self.is_list() { + return Err(DecoderError::RlpExpectedToBeList); + } + + // move to cached position if its index is less or equal to + // current search index, otherwise move to beginning of list + let cache = self.offset_cache.get(); + let (bytes, indexes_to_skip, bytes_consumed) = match cache { + Some(ref cache) if cache.index <= index => ( + Rlp::consume(self.bytes, cache.offset)?, + index - cache.index, + cache.offset, + ), + _ => { + let (bytes, consumed) = self.consume_list_payload()?; + (bytes, index, consumed) + } + }; + + // skip up to x items + let (bytes, consumed) = Rlp::consume_items(bytes, indexes_to_skip)?; + + // update the cache + let offset = bytes_consumed + consumed; + self.offset_cache.set(Some(OffsetCache::new(index, offset))); + + // construct new rlp + let found = BasicDecoder::payload_info(bytes)?; + Ok(( + Rlp::new(&bytes[0..found.header_len + found.value_len]), + offset, + )) + } + + pub fn is_null(&self) -> bool { + self.bytes.is_empty() || (self.bytes[0] == 0xf8 && self.bytes[1] == 0) + } + + pub fn is_empty(&self) -> bool { + !self.is_null() && (self.bytes[0] == 0xc0 || self.bytes[0] == 0x80) + } + + pub fn is_list(&self) -> bool { + !self.is_null() && self.bytes[0] >= 0xc0 + } + + pub fn is_data(&self) -> bool { + !self.is_null() && self.bytes[0] < 0xc0 + } + + pub fn is_int(&self) -> bool { + if self.is_null() { + return false; + } + + match self.bytes[0] { + 0..=0x80 => true, + 0x81..=0xb7 => self.bytes[1] != 0, + b @ 0xb8..=0xbf => { + let payload_idx = 1 + b as usize - 0xb7; + payload_idx < self.bytes.len() && self.bytes[payload_idx] != 0 + } + _ => false, + } + } + + pub fn iter<'view>(&'view self) -> RlpIterator<'a, 'view> + where + 'a: 'view, + { + self.into_iter() + } + + pub fn as_val(&self) -> Result + where + T: Decodable, + { + T::decode(self) + } + + pub fn as_list(&self) -> Result, DecoderError> + where + T: Decodable, + { + self.iter().map(|rlp| rlp.as_val()).collect() + } + + pub fn val_at(&self, index: usize) -> Result + where + T: Decodable, + { + self.at(index)?.as_val() + } + + pub fn list_at(&self, index: usize) -> Result, DecoderError> + where + T: Decodable, + { + self.at(index)?.as_list() + } + + pub fn decoder(&self) -> BasicDecoder { + BasicDecoder::new(self.bytes) + } + + /// consumes first found prefix + fn consume_list_payload(&self) -> Result<(&'a [u8], usize), DecoderError> { + let item = BasicDecoder::payload_info(self.bytes)?; + if self.bytes.len() < (item.header_len + item.value_len) { + return Err(DecoderError::RlpIsTooShort); + } + Ok(( + &self.bytes[item.header_len..item.header_len + item.value_len], + item.header_len, + )) + } + + /// consumes fixed number of items + fn consume_items(bytes: &'a [u8], items: usize) -> Result<(&'a [u8], usize), DecoderError> { + let mut result = bytes; + let mut consumed = 0; + for _ in 0..items { + let i = BasicDecoder::payload_info(result)?; + let to_consume = i.header_len + i.value_len; + result = Rlp::consume(result, to_consume)?; + consumed += to_consume; + } + Ok((result, consumed)) + } + + /// consumes slice prefix of length `len` + fn consume(bytes: &'a [u8], len: usize) -> Result<&'a [u8], DecoderError> { + if bytes.len() >= len { + Ok(&bytes[len..]) + } else { + Err(DecoderError::RlpIsTooShort) + } + } +} + +/// Iterator over rlp-slice list elements. +pub struct RlpIterator<'a, 'view> +where + 'a: 'view, +{ + rlp: &'view Rlp<'a>, + index: usize, +} + +impl<'a, 'view> IntoIterator for &'view Rlp<'a> +where + 'a: 'view, +{ + type Item = Rlp<'a>; + type IntoIter = RlpIterator<'a, 'view>; + + fn into_iter(self) -> Self::IntoIter { + RlpIterator { + rlp: self, + index: 0, + } + } +} + +impl<'a, 'view> Iterator for RlpIterator<'a, 'view> { + type Item = Rlp<'a>; + + fn next(&mut self) -> Option> { + let index = self.index; + let result = self.rlp.at(index).ok(); + self.index += 1; + result + } +} + +impl<'a, 'view> ExactSizeIterator for RlpIterator<'a, 'view> { + fn len(&self) -> usize { + self.rlp.item_count().unwrap_or(0) + } +} + +pub struct BasicDecoder<'a> { + rlp: &'a [u8], +} + +impl<'a> BasicDecoder<'a> { + pub const fn new(rlp: &'a [u8]) -> BasicDecoder<'a> { + BasicDecoder { rlp } + } + + /// Return first item info. + fn payload_info(bytes: &[u8]) -> Result { + let item = PayloadInfo::from(bytes)?; + match item.header_len.checked_add(item.value_len) { + Some(x) if x <= bytes.len() => Ok(item), + _ => Err(DecoderError::RlpIsTooShort), + } + } + + pub fn decode_value(&self, f: F) -> Result + where + F: Fn(&[u8]) -> Result, + { + let bytes = self.rlp; + + let l = *bytes.first().ok_or(DecoderError::RlpIsTooShort)?; + + if l <= 0x7f { + Ok(f(&[l])?) + } else if l <= 0xb7 { + let last_index_of = 1 + l as usize - 0x80; + if bytes.len() < last_index_of { + return Err(DecoderError::RlpInconsistentLengthAndData); + } + let d = &bytes[1..last_index_of]; + if l == 0x81 && d[0] < 0x80 { + return Err(DecoderError::RlpInvalidIndirection); + } + Ok(f(d)?) + } else if l <= 0xbf { + let len_of_len = l as usize - 0xb7; + let begin_of_value = 1_usize + len_of_len; + if bytes.len() < begin_of_value { + return Err(DecoderError::RlpInconsistentLengthAndData); + } + let len = decode_usize(&bytes[1..begin_of_value])?; + + let last_index_of_value = begin_of_value + .checked_add(len) + .ok_or(DecoderError::RlpInvalidLength)?; + if bytes.len() < last_index_of_value { + return Err(DecoderError::RlpInconsistentLengthAndData); + } + Ok(f(&bytes[begin_of_value..last_index_of_value])?) + } else { + Err(DecoderError::RlpExpectedToBeData) + } + } +} diff --git a/libraries/rust/common/src/rlp/stream.rs b/libraries/rust/common/src/rlp/stream.rs new file mode 100644 index 0000000..b9c5a25 --- /dev/null +++ b/libraries/rust/common/src/rlp/stream.rs @@ -0,0 +1,466 @@ +// Copyright 2020 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use bytes::{BufMut, BytesMut}; +use core::borrow::Borrow; + +use crate::rlp::traits::Encodable; + +#[derive(Debug, Copy, Clone)] +struct ListInfo { + position: usize, + current: usize, + max: Option, +} + +impl ListInfo { + fn new(position: usize, max: Option) -> ListInfo { + ListInfo { + position, + current: 0, + max, + } + } +} + +/// Appendable rlp encoder. +pub struct RlpStream { + unfinished_lists: Vec, + start_pos: usize, + buffer: BytesMut, + finished_list: bool, +} + +impl Default for RlpStream { + fn default() -> Self { + RlpStream::new() + } +} + +impl RlpStream { + /// Initializes instance of empty `Stream`. + pub fn new() -> Self { + Self::new_with_buffer(BytesMut::with_capacity(1024)) + } + + /// Initializes the `Stream` as a list. + pub fn new_list(len: usize) -> Self { + Self::new_list_with_buffer(BytesMut::with_capacity(1024), len) + } + + /// Initializes instance of empty `Stream`. + pub fn new_with_buffer(buffer: BytesMut) -> Self { + RlpStream { + unfinished_lists: Vec::with_capacity(16), + start_pos: buffer.len(), + buffer, + finished_list: false, + } + } + + /// Initializes the `Stream` as a list. + pub fn new_list_with_buffer(buffer: BytesMut, len: usize) -> Self { + let mut stream = RlpStream::new_with_buffer(buffer); + stream.begin_list(len); + stream + } + + fn total_written(&self) -> usize { + self.buffer.len() - self.start_pos + } + + /// Apends null to the end of stream, chainable. + /// + /// ``` + /// use common::rlp::{RlpStream}; + /// let mut stream = RlpStream::new_list(2); + /// stream.append_null().append_null(); + /// let out = stream.out(); + /// println!("{:?}", b"\xc4\xf8\0\xf8\0"); + /// assert_eq!(out, vec![0xc4, 0xf8, 0, 0xf8, 0]); + /// ``` + pub fn append_null(&mut self) -> &mut Self { + // self push raw item + self.buffer.put_u8(0xf8); + self.buffer.put_u8(0x00); + // try to finish and prepend the length + self.note_appended(1); + + // return chainable self + self + } + + pub fn append_null_internal(&mut self) -> &mut Self { + // self push raw item + self.buffer.put_u8(0xf8); + self.buffer.put_u8(0x00); + // return chainable self + self + } + + /// Apends null to the end of stream, chainable. + /// + /// ``` + /// use common::rlp::RlpStream; + /// let mut stream = RlpStream::new_list(2); + /// stream.append_empty_data().append_empty_data(); + /// let out = stream.out(); + /// assert_eq!(out, vec![0xc2, 0x80, 0x80]); + /// ``` + pub fn append_empty_data(&mut self) -> &mut Self { + // self push raw item + self.buffer.put_u8(0x80); + + // try to finish and prepend the length + self.note_appended(1); + + // return chainable self + self + } + + /// Appends raw (pre-serialised) RLP data. Use with caution. Chainable. + pub fn append_raw(&mut self, bytes: &[u8], item_count: usize) -> &mut Self { + // push raw items + self.buffer.extend_from_slice(bytes); + + // try to finish and prepend the length + self.note_appended(item_count); + + // return chainable self + self + } + + /// Appends value to the end of stream, chainable. + /// + /// ``` + /// use common::rlp::{RlpStream}; + /// let mut stream = RlpStream::new_list(2); + /// stream.append(&"cat").append(&"dog"); + /// let out = stream.out(); + /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); + /// ``` + pub fn append(&mut self, value: &E) -> &mut Self + where + E: Encodable, + { + self.finished_list = false; + value.rlp_append(self); + if !self.finished_list { + self.note_appended(1); + } + self + } + + /// Appends iterator to the end of stream, chainable. + /// + /// ``` + /// use common::rlp::{RlpStream}; + /// let mut stream = RlpStream::new_list(2); + /// stream.append(&"cat").append_iter("dog".as_bytes().iter().cloned()); + /// let out = stream.out(); + /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); + /// ``` + pub fn append_iter(&mut self, value: I) -> &mut Self + where + I: IntoIterator, + { + self.finished_list = false; + self.encoder().encode_iter(value); + if !self.finished_list { + self.note_appended(1); + } + self + } + + /// Appends list of values to the end of stream, chainable. + pub fn append_list(&mut self, values: &[K]) -> &mut Self + where + E: Encodable, + K: Borrow, + { + self.begin_list(values.len()); + for value in values { + self.append(value.borrow()); + } + self + } + + /// Appends value to the end of stream, but do not count it as an appended item. + /// It's useful for wrapper types + pub fn append_internal(&mut self, value: &E) -> &mut Self + where + E: Encodable, + { + value.rlp_append(self); + self + } + + /// Declare appending the list of given size, chainable. + /// + /// ``` + /// use common::rlp::{RlpStream}; + /// let mut stream = RlpStream::new_list(2); + /// stream.begin_list(2).append(&"cat").append(&"dog"); + /// stream.append(&""); + /// let out = stream.out(); + /// assert_eq!(out, vec![0xca, 0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g', 0x80]); + /// ``` + pub fn begin_list(&mut self, len: usize) -> &mut RlpStream { + self.finished_list = false; + match len { + 0 => { + // we may finish, if the appended list len is equal 0 + self.buffer.put_u8(0xc0u8); + self.note_appended(1); + self.finished_list = true; + } + _ => { + // payload is longer than 1 byte only for lists > 55 bytes + // by pushing always this 1 byte we may avoid unnecessary shift of data + self.buffer.put_u8(0); + + let position = self.total_written(); + self.unfinished_lists + .push(ListInfo::new(position, Some(len))); + } + } + + // return chainable self + self + } + + /// Declare appending the list of unknown size, chainable. + pub fn begin_unbounded_list(&mut self) -> &mut RlpStream { + self.finished_list = false; + // payload is longer than 1 byte only for lists > 55 bytes + // by pushing always this 1 byte we may avoid unnecessary shift of data + self.buffer.put_u8(0); + let position = self.total_written(); + self.unfinished_lists.push(ListInfo::new(position, None)); + // return chainable self + self + } + + /// Appends raw (pre-serialised) RLP data. Checks for size overflow. + pub fn append_raw_checked(&mut self, bytes: &[u8], item_count: usize, max_size: usize) -> bool { + if self.estimate_size(bytes.len()) > max_size { + return false; + } + self.append_raw(bytes, item_count); + true + } + + /// Calculate total RLP size for appended payload. + pub fn estimate_size(&self, add: usize) -> usize { + let total_size = self.total_written() + add; + let mut base_size = total_size; + for list in &self.unfinished_lists[..] { + let len = total_size - list.position; + if len > 55 { + let leading_empty_bytes = (len as u64).leading_zeros() as usize / 8; + let size_bytes = 8 - leading_empty_bytes; + base_size += size_bytes; + } + } + base_size + } + + /// Returns current RLP size in bytes for the data pushed into the list. + pub fn len(&self) -> usize { + self.estimate_size(0) + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Clear the output stream so far. + /// + /// ``` + /// use common::rlp::{RlpStream}; + /// let mut stream = RlpStream::new_list(3); + /// stream.append(&"cat"); + /// stream.clear(); + /// stream.append(&"dog"); + /// let out = stream.out(); + /// assert_eq!(out, vec![0x83, b'd', b'o', b'g']); + /// ``` + pub fn clear(&mut self) { + // clear bytes + self.buffer.truncate(self.start_pos); + + // clear lists + self.unfinished_lists.clear(); + } + + /// Returns true if stream doesnt expect any more items. + /// + /// ``` + /// use common::rlp::{RlpStream}; + /// let mut stream = RlpStream::new_list(2); + /// stream.append(&"cat"); + /// assert_eq!(stream.is_finished(), false); + /// stream.append(&"dog"); + /// assert_eq!(stream.is_finished(), true); + /// let out = stream.out(); + /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); + /// ``` + pub fn is_finished(&self) -> bool { + self.unfinished_lists.is_empty() + } + + /// Get raw encoded bytes + pub fn as_raw(&self) -> &[u8] { + //&self.encoder.bytes + &self.buffer + } + + /// Streams out encoded bytes. + /// + /// panic! if stream is not finished. + pub fn out(self) -> BytesMut { + if self.is_finished() { + self.buffer + } else { + panic!() + } + } + + /// Try to finish lists + fn note_appended(&mut self, inserted_items: usize) { + if self.unfinished_lists.is_empty() { + return; + } + + let back = self.unfinished_lists.len() - 1; + let should_finish = match self.unfinished_lists.get_mut(back) { + None => false, + Some(ref mut x) => { + x.current += inserted_items; + match x.max { + Some(ref max) if x.current > *max => { + panic!("You cannot append more items than you expect!") + } + Some(ref max) => x.current == *max, + _ => false, + } + } + }; + if should_finish { + let x = self.unfinished_lists.pop().unwrap(); + let len = self.total_written() - x.position; + self.encoder().insert_list_payload(len, x.position); + self.note_appended(1); + } + self.finished_list = should_finish; + } + + pub fn encoder(&mut self) -> BasicEncoder { + BasicEncoder::new(self, self.start_pos) + } + + /// Finalize current unbounded list. Panics if no unbounded list has been opened. + pub fn finalize_unbounded_list(&mut self) { + let list = self.unfinished_lists.pop().expect("No open list."); + if list.max.is_some() { + panic!("List type mismatch."); + } + let len = self.total_written() - list.position; + self.encoder().insert_list_payload(len, list.position); + self.note_appended(1); + self.finished_list = true; + } +} + +pub struct BasicEncoder<'a> { + buffer: &'a mut BytesMut, + start_pos: usize, +} + +impl<'a> BasicEncoder<'a> { + fn new(stream: &'a mut RlpStream, start_pos: usize) -> Self { + BasicEncoder { + buffer: &mut stream.buffer, + start_pos, + } + } + + fn total_written(&self) -> usize { + self.buffer.len() - self.start_pos + } + + fn insert_size(&mut self, size: usize, position: usize) -> u8 { + let size = size as u32; + let leading_empty_bytes = size.leading_zeros() as usize / 8; + let size_bytes = 4 - leading_empty_bytes as u8; + let buffer: [u8; 4] = size.to_be_bytes(); + assert!(position <= self.total_written()); + + self.buffer + .extend_from_slice(&buffer[leading_empty_bytes..]); + self.buffer[self.start_pos + position..].rotate_right(size_bytes as usize); + size_bytes + } + + /// Inserts list prefix at given position + fn insert_list_payload(&mut self, len: usize, pos: usize) { + // 1 byte was already reserved for payload earlier + match len { + 0..=55 => { + self.buffer[self.start_pos + pos - 1] = 0xc0u8 + len as u8; + } + _ => { + let inserted_bytes = self.insert_size(len, pos); + self.buffer[self.start_pos + pos - 1] = 0xf7u8 + inserted_bytes; + } + }; + } + + pub fn encode_value(&mut self, value: &[u8]) { + self.encode_iter(value.iter().cloned()); + } + + /// Pushes encoded value to the end of buffer + pub fn encode_iter(&mut self, value: I) + where + I: IntoIterator, + { + let mut value = value.into_iter(); + let len = match value.size_hint() { + (lower, Some(upper)) if lower == upper => lower, + _ => { + let value = value.collect::>(); + return self.encode_iter(value); + } + }; + match len { + // just 0 + 0 => self.buffer.put_u8(0x80u8), + len @ 1..=55 => { + let first = value.next().expect("iterator length is higher than 1"); + if len == 1 && first < 0x80 { + // byte is its own encoding if < 0x80 + self.buffer.put_u8(first); + } else { + // (prefix + length), followed by the string + self.buffer.put_u8(0x80u8 + len as u8); + self.buffer.put_u8(first); + self.buffer.extend(value); + } + } + // (prefix + length of length), followed by the length, followd by the string + len => { + self.buffer.put_u8(0); + let position = self.total_written(); + let inserted_bytes = self.insert_size(len, position); + self.buffer[self.start_pos + position - 1] = 0xb7 + inserted_bytes; + self.buffer.extend(value); + } + } + } +} diff --git a/libraries/rust/common/src/rlp/traits.rs b/libraries/rust/common/src/rlp/traits.rs new file mode 100644 index 0000000..0e00b87 --- /dev/null +++ b/libraries/rust/common/src/rlp/traits.rs @@ -0,0 +1,31 @@ +// Copyright 2020 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Common RLP traits +use bytes::BytesMut; + +use crate::rlp::{error::DecoderError, rlpin::Rlp, stream::RlpStream}; + +/// RLP decodable trait +pub trait Decodable: Sized { + /// Decode a value from RLP bytes + fn decode(rlp: &Rlp) -> Result; +} + +/// Structure encodable to RLP +pub trait Encodable { + /// Append a value to the stream + fn rlp_append(&self, s: &mut RlpStream); + + /// Get rlp-encoded bytes for this instance + fn rlp_bytes(&self) -> BytesMut { + let mut s = RlpStream::new(); + self.rlp_append(&mut s); + s.out() + } +} From 0b706b6c9bdfe805b85c04565ed7f93f410d180a Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Mon, 29 May 2023 13:44:43 +0545 Subject: [PATCH 02/37] Balanced_HubToken: structure for additional libraries --- libraries/rust/common/Cargo.toml | 15 ++++++++ libraries/rust/common/src/icallservice.rs | 23 ++++++++++++ libraries/rust/common/src/lib.rs | 4 ++ libraries/rust/common/src/strings.rs | 45 +++++++++++++++++++++++ libraries/rust/common/src/types.rs | 13 +++++++ 5 files changed, 100 insertions(+) create mode 100644 libraries/rust/common/Cargo.toml create mode 100644 libraries/rust/common/src/icallservice.rs create mode 100644 libraries/rust/common/src/lib.rs create mode 100644 libraries/rust/common/src/strings.rs create mode 100644 libraries/rust/common/src/types.rs diff --git a/libraries/rust/common/Cargo.toml b/libraries/rust/common/Cargo.toml new file mode 100644 index 0000000..fe9feb3 --- /dev/null +++ b/libraries/rust/common/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "common" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bytes = { version = "1.4.0", default-features = false } +rlp-derive = { version = "0.1.0", default-features = false } +cw20 = { version = "1.0.1", default-features = false } +rustc-hex = { version = "2.1.0", default-features = false } +serde = { version = "1.0.156", default-features = false,features = ["derive"] } +hex ={ version = "0.4.3", default-features = false } +cosmwasm-std = { version = "1.2.5", default-features = false } diff --git a/libraries/rust/common/src/icallservice.rs b/libraries/rust/common/src/icallservice.rs new file mode 100644 index 0000000..a61c2e1 --- /dev/null +++ b/libraries/rust/common/src/icallservice.rs @@ -0,0 +1,23 @@ +use cosmwasm_std::{ + Env, MessageInfo, Response, StdError, StdResult, Storage, +}; + +#[derive(Clone)] +pub struct ICallService {} + +impl ICallService { + pub fn get_btp_address(&self, _env: Env) -> StdResult { + Ok("BTP_ADDRESS".to_string()) + } + + pub fn send_call_message( + &self, + _env: Env, + _to: String, + _data: Vec, + _rollback: Vec, + ) -> StdResult { + + Ok(Response::default()) + } +} diff --git a/libraries/rust/common/src/lib.rs b/libraries/rust/common/src/lib.rs new file mode 100644 index 0000000..c014f22 --- /dev/null +++ b/libraries/rust/common/src/lib.rs @@ -0,0 +1,4 @@ +pub mod rlp; +pub mod types; +pub mod icallservice; +pub mod strings; \ No newline at end of file diff --git a/libraries/rust/common/src/strings.rs b/libraries/rust/common/src/strings.rs new file mode 100644 index 0000000..58263b7 --- /dev/null +++ b/libraries/rust/common/src/strings.rs @@ -0,0 +1,45 @@ +pub mod strings { + pub fn bytes_to_hex(buffer: &[u8]) -> String { + if buffer.is_empty() { + return "0x".to_string(); + } + + let converted: Vec = buffer + .iter() + .flat_map(|byte| vec![byte / 16, byte % 16]) + .collect(); + + let base: Vec = b"0123456789abcdef".to_vec(); + + let hex_string: String = converted + .iter() + .map(|byte| base[*byte as usize] as char) + .collect(); + + format!("0x{}", hex_string) + } + + pub fn concat(base: &str, value: &str) -> String { + format!("{}{}", base, value) + } + + pub fn index_of(base: &str, value: &str) -> Option { + base.find(value) + } + + pub fn length(base: &str) -> usize { + base.len() + } + + pub fn split(base: &str, value: &str) -> Vec { + base.split(value).map(|s| s.to_string()).collect() + } + + pub fn compare_to(base: &str, value: &str) -> bool { + base == value + } + + pub fn lower(base: &str) -> String { + base.to_lowercase() + } +} diff --git a/libraries/rust/common/src/types.rs b/libraries/rust/common/src/types.rs new file mode 100644 index 0000000..ca32a8a --- /dev/null +++ b/libraries/rust/common/src/types.rs @@ -0,0 +1,13 @@ +pub mod types { + pub struct CrossTransfer { + pub from: String, + pub to: String, + pub value: u128, + pub data: Vec, + } + + pub struct CrossTransferRevert { + pub from: String, + pub value: u128, + } +} From d7607b01da34969ccdbf5e584f5b98d03c336f15 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Mon, 29 May 2023 13:45:17 +0545 Subject: [PATCH 03/37] Balanced_HubToken: cw20-base contract added --- libraries/cw20-base/.cargo/config | 6 + libraries/cw20-base/Cargo.toml | 33 + libraries/cw20-base/README.md | 48 + libraries/cw20-base/src/allowances.rs | 879 ++++++++++ libraries/cw20-base/src/bin/schema.rs | 11 + libraries/cw20-base/src/contract.rs | 2236 +++++++++++++++++++++++++ libraries/cw20-base/src/enumerable.rs | 319 ++++ libraries/cw20-base/src/error.rs | 43 + libraries/cw20-base/src/lib.rs | 24 + libraries/cw20-base/src/msg.rs | 175 ++ libraries/cw20-base/src/state.rs | 36 + 11 files changed, 3810 insertions(+) create mode 100644 libraries/cw20-base/.cargo/config create mode 100644 libraries/cw20-base/Cargo.toml create mode 100644 libraries/cw20-base/README.md create mode 100644 libraries/cw20-base/src/allowances.rs create mode 100644 libraries/cw20-base/src/bin/schema.rs create mode 100644 libraries/cw20-base/src/contract.rs create mode 100644 libraries/cw20-base/src/enumerable.rs create mode 100644 libraries/cw20-base/src/error.rs create mode 100644 libraries/cw20-base/src/lib.rs create mode 100644 libraries/cw20-base/src/msg.rs create mode 100644 libraries/cw20-base/src/state.rs diff --git a/libraries/cw20-base/.cargo/config b/libraries/cw20-base/.cargo/config new file mode 100644 index 0000000..f517478 --- /dev/null +++ b/libraries/cw20-base/.cargo/config @@ -0,0 +1,6 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +wasm-debug = "build --lib --target wasm32-unknown-unknown" +unit-test = "test --lib" +integration-test = "test --test integration" +schema = "run --bin schema" diff --git a/libraries/cw20-base/Cargo.toml b/libraries/cw20-base/Cargo.toml new file mode 100644 index 0000000..8cf5e08 --- /dev/null +++ b/libraries/cw20-base/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "cw20-base" +version = "1.0.1" +authors = ["Ethan Frey "] +edition = "2021" +description = "Basic implementation of a CosmWasm-20 compliant token" +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/cw-plus" +homepage = "https://cosmwasm.com" +documentation = "https://docs.cosmwasm.com" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +cosmwasm-schema = { version = "1.1.0" } +cw-utils = "1.0.1" +cw2 = { version = "1.0.1" } +cw20 = { version = "1.0.1" } +cw-storage-plus = "1.0.1" +cosmwasm-std = { version = "1.1.0" } +schemars = "0.8.1" +semver = "1" +serde = { version = "1.0.103", default-features = false, features = ["derive"] } +thiserror = { version = "1.0.23" } + +[dev-dependencies] +cw-multi-test = "0.16.1" diff --git a/libraries/cw20-base/README.md b/libraries/cw20-base/README.md new file mode 100644 index 0000000..01db9e0 --- /dev/null +++ b/libraries/cw20-base/README.md @@ -0,0 +1,48 @@ +# CW20 Basic + +This is a basic implementation of a cw20 contract. It implements +the [CW20 spec](../../packages/cw20/README.md) and is designed to +be deployed as is, or imported into other contracts to easily build +cw20-compatible tokens with custom logic. + +Implements: + +- [x] CW20 Base +- [x] Mintable extension +- [x] Allowances extension + +## Running this contract + +You will need Rust 1.44.1+ with `wasm32-unknown-unknown` target installed. + +You can run unit tests on this via: + +`cargo test` + +Once you are happy with the content, you can compile it to wasm via: + +``` +RUSTFLAGS='-C link-arg=-s' cargo wasm +cp ../../target/wasm32-unknown-unknown/release/cw20_base.wasm . +ls -l cw20_base.wasm +sha256sum cw20_base.wasm +``` + +Or for a production-ready (optimized) build, run a build command in the +the repository root: https://github.com/CosmWasm/cw-plus#compiling. + +## Importing this contract + +You can also import much of the logic of this contract to build another +ERC20-contract, such as a bonding curve, overiding or extending what you +need. + +Basically, you just need to write your handle function and import +`cw20_base::contract::handle_transfer`, etc and dispatch to them. +This allows you to use custom `ExecuteMsg` and `QueryMsg` with your additional +calls, but then use the underlying implementation for the standard cw20 +messages you want to support. The same with `QueryMsg`. You *could* reuse `instantiate` +as it, but it is likely you will want to change it. And it is rather simple. + +Look at [`cw20-staking`](https://github.com/CosmWasm/cw-tokens/tree/main/contracts/cw20-staking) for an example of how to "inherit" +all this token functionality and combine it with custom logic. diff --git a/libraries/cw20-base/src/allowances.rs b/libraries/cw20-base/src/allowances.rs new file mode 100644 index 0000000..38b36da --- /dev/null +++ b/libraries/cw20-base/src/allowances.rs @@ -0,0 +1,879 @@ +use cosmwasm_std::{ + attr, Addr, Binary, BlockInfo, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, + Storage, Uint128, +}; +use cw20::{AllowanceResponse, Cw20ReceiveMsg, Expiration}; + +use crate::error::ContractError; +use crate::state::{ALLOWANCES, ALLOWANCES_SPENDER, BALANCES, TOKEN_INFO}; + +pub fn execute_increase_allowance( + deps: DepsMut, + env: Env, + info: MessageInfo, + spender: String, + amount: Uint128, + expires: Option, +) -> Result { + let spender_addr = deps.api.addr_validate(&spender)?; + if spender_addr == info.sender { + return Err(ContractError::CannotSetOwnAccount {}); + } + + let update_fn = |allow: Option| -> Result<_, _> { + let mut val = allow.unwrap_or_default(); + if let Some(exp) = expires { + if exp.is_expired(&env.block) { + return Err(ContractError::InvalidExpiration {}); + } + val.expires = exp; + } + val.allowance += amount; + Ok(val) + }; + ALLOWANCES.update(deps.storage, (&info.sender, &spender_addr), update_fn)?; + ALLOWANCES_SPENDER.update(deps.storage, (&spender_addr, &info.sender), update_fn)?; + + let res = Response::new().add_attributes(vec![ + attr("action", "increase_allowance"), + attr("owner", info.sender), + attr("spender", spender), + attr("amount", amount), + ]); + Ok(res) +} + +pub fn execute_decrease_allowance( + deps: DepsMut, + env: Env, + info: MessageInfo, + spender: String, + amount: Uint128, + expires: Option, +) -> Result { + let spender_addr = deps.api.addr_validate(&spender)?; + if spender_addr == info.sender { + return Err(ContractError::CannotSetOwnAccount {}); + } + + let key = (&info.sender, &spender_addr); + + fn reverse<'a>(t: (&'a Addr, &'a Addr)) -> (&'a Addr, &'a Addr) { + (t.1, t.0) + } + + // load value and delete if it hits 0, or update otherwise + let mut allowance = ALLOWANCES.load(deps.storage, key)?; + if amount < allowance.allowance { + // update the new amount + allowance.allowance = allowance + .allowance + .checked_sub(amount) + .map_err(StdError::overflow)?; + if let Some(exp) = expires { + if exp.is_expired(&env.block) { + return Err(ContractError::InvalidExpiration {}); + } + allowance.expires = exp; + } + ALLOWANCES.save(deps.storage, key, &allowance)?; + ALLOWANCES_SPENDER.save(deps.storage, reverse(key), &allowance)?; + } else { + ALLOWANCES.remove(deps.storage, key); + ALLOWANCES_SPENDER.remove(deps.storage, reverse(key)); + } + + let res = Response::new().add_attributes(vec![ + attr("action", "decrease_allowance"), + attr("owner", info.sender), + attr("spender", spender), + attr("amount", amount), + ]); + Ok(res) +} + +// this can be used to update a lower allowance - call bucket.update with proper keys +pub fn deduct_allowance( + storage: &mut dyn Storage, + owner: &Addr, + spender: &Addr, + block: &BlockInfo, + amount: Uint128, +) -> Result { + let update_fn = |current: Option| -> _ { + match current { + Some(mut a) => { + if a.expires.is_expired(block) { + Err(ContractError::Expired {}) + } else { + // deduct the allowance if enough + a.allowance = a + .allowance + .checked_sub(amount) + .map_err(StdError::overflow)?; + Ok(a) + } + } + None => Err(ContractError::NoAllowance {}), + } + }; + ALLOWANCES.update(storage, (owner, spender), update_fn)?; + ALLOWANCES_SPENDER.update(storage, (spender, owner), update_fn) +} + +pub fn execute_transfer_from( + deps: DepsMut, + env: Env, + info: MessageInfo, + owner: String, + recipient: String, + amount: Uint128, +) -> Result { + let rcpt_addr = deps.api.addr_validate(&recipient)?; + let owner_addr = deps.api.addr_validate(&owner)?; + + // deduct allowance before doing anything else have enough allowance + deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?; + + BALANCES.update( + deps.storage, + &owner_addr, + |balance: Option| -> StdResult<_> { + Ok(balance.unwrap_or_default().checked_sub(amount)?) + }, + )?; + BALANCES.update( + deps.storage, + &rcpt_addr, + |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, + )?; + + let res = Response::new().add_attributes(vec![ + attr("action", "transfer_from"), + attr("from", owner), + attr("to", recipient), + attr("by", info.sender), + attr("amount", amount), + ]); + Ok(res) +} + +pub fn execute_burn_from( + deps: DepsMut, + + env: Env, + info: MessageInfo, + owner: String, + amount: Uint128, +) -> Result { + let owner_addr = deps.api.addr_validate(&owner)?; + + // deduct allowance before doing anything else have enough allowance + deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?; + + // lower balance + BALANCES.update( + deps.storage, + &owner_addr, + |balance: Option| -> StdResult<_> { + Ok(balance.unwrap_or_default().checked_sub(amount)?) + }, + )?; + // reduce total_supply + TOKEN_INFO.update(deps.storage, |mut meta| -> StdResult<_> { + meta.total_supply = meta.total_supply.checked_sub(amount)?; + Ok(meta) + })?; + + let res = Response::new().add_attributes(vec![ + attr("action", "burn_from"), + attr("from", owner), + attr("by", info.sender), + attr("amount", amount), + ]); + Ok(res) +} + +pub fn execute_send_from( + deps: DepsMut, + env: Env, + info: MessageInfo, + owner: String, + contract: String, + amount: Uint128, + msg: Binary, +) -> Result { + let rcpt_addr = deps.api.addr_validate(&contract)?; + let owner_addr = deps.api.addr_validate(&owner)?; + + // deduct allowance before doing anything else have enough allowance + deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?; + + // move the tokens to the contract + BALANCES.update( + deps.storage, + &owner_addr, + |balance: Option| -> StdResult<_> { + Ok(balance.unwrap_or_default().checked_sub(amount)?) + }, + )?; + BALANCES.update( + deps.storage, + &rcpt_addr, + |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, + )?; + + let attrs = vec![ + attr("action", "send_from"), + attr("from", &owner), + attr("to", &contract), + attr("by", &info.sender), + attr("amount", amount), + ]; + + // create a send message + let msg = Cw20ReceiveMsg { + sender: info.sender.into(), + amount, + msg, + } + .into_cosmos_msg(contract)?; + + let res = Response::new().add_message(msg).add_attributes(attrs); + Ok(res) +} + +pub fn query_allowance(deps: Deps, owner: String, spender: String) -> StdResult { + let owner_addr = deps.api.addr_validate(&owner)?; + let spender_addr = deps.api.addr_validate(&spender)?; + let allowance = ALLOWANCES + .may_load(deps.storage, (&owner_addr, &spender_addr))? + .unwrap_or_default(); + Ok(allowance) +} + +#[cfg(test)] +mod tests { + use super::*; + + use cosmwasm_std::testing::{mock_dependencies_with_balance, mock_env, mock_info}; + use cosmwasm_std::{coins, CosmosMsg, SubMsg, Timestamp, WasmMsg}; + use cw20::{Cw20Coin, TokenInfoResponse}; + + use crate::contract::{execute, instantiate, query_balance, query_token_info}; + use crate::msg::{ExecuteMsg, InstantiateMsg}; + + fn get_balance>(deps: Deps, address: T) -> Uint128 { + query_balance(deps, address.into()).unwrap().balance + } + + // this will set up the instantiation for other tests + fn do_instantiate>( + mut deps: DepsMut, + addr: T, + amount: Uint128, + ) -> TokenInfoResponse { + let instantiate_msg = InstantiateMsg { + name: "Auto Gen".to_string(), + symbol: "AUTO".to_string(), + decimals: 3, + initial_balances: vec![Cw20Coin { + address: addr.into(), + amount, + }], + mint: None, + marketing: None, + }; + let info = mock_info("creator", &[]); + let env = mock_env(); + instantiate(deps.branch(), env, info, instantiate_msg).unwrap(); + query_token_info(deps.as_ref()).unwrap() + } + + #[test] + fn increase_decrease_allowances() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + + let owner = String::from("addr0001"); + let spender = String::from("addr0002"); + let info = mock_info(owner.as_ref(), &[]); + let env = mock_env(); + do_instantiate(deps.as_mut(), owner.clone(), Uint128::new(12340000)); + + // no allowance to start + let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); + assert_eq!(allowance, AllowanceResponse::default()); + + // set allowance with height expiration + let allow1 = Uint128::new(7777); + let expires = Expiration::AtHeight(123_456); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow1, + expires: Some(expires), + }; + execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + + // ensure it looks good + let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); + assert_eq!( + allowance, + AllowanceResponse { + allowance: allow1, + expires + } + ); + + // decrease it a bit with no expire set - stays the same + let lower = Uint128::new(4444); + let allow2 = allow1.checked_sub(lower).unwrap(); + let msg = ExecuteMsg::DecreaseAllowance { + spender: spender.clone(), + amount: lower, + expires: None, + }; + execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); + assert_eq!( + allowance, + AllowanceResponse { + allowance: allow2, + expires + } + ); + + // increase it some more and override the expires + let raise = Uint128::new(87654); + let allow3 = allow2 + raise; + let new_expire = Expiration::AtTime(Timestamp::from_seconds(8888888888)); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: raise, + expires: Some(new_expire), + }; + execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); + assert_eq!( + allowance, + AllowanceResponse { + allowance: allow3, + expires: new_expire + } + ); + + // decrease it below 0 + let msg = ExecuteMsg::DecreaseAllowance { + spender: spender.clone(), + amount: Uint128::new(99988647623876347), + expires: None, + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + let allowance = query_allowance(deps.as_ref(), owner, spender).unwrap(); + assert_eq!(allowance, AllowanceResponse::default()); + } + + #[test] + fn allowances_independent() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + + let owner = String::from("addr0001"); + let spender = String::from("addr0002"); + let spender2 = String::from("addr0003"); + let info = mock_info(owner.as_ref(), &[]); + let env = mock_env(); + do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000)); + + // no allowance to start + assert_eq!( + query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(), + AllowanceResponse::default() + ); + assert_eq!( + query_allowance(deps.as_ref(), owner.clone(), spender2.clone()).unwrap(), + AllowanceResponse::default() + ); + assert_eq!( + query_allowance(deps.as_ref(), spender.clone(), spender2.clone()).unwrap(), + AllowanceResponse::default() + ); + + // set allowance with height expiration + let allow1 = Uint128::new(7777); + let expires = Expiration::AtHeight(123_456); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow1, + expires: Some(expires), + }; + execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + + // set other allowance with no expiration + let allow2 = Uint128::new(87654); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender2.clone(), + amount: allow2, + expires: None, + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // check they are proper + let expect_one = AllowanceResponse { + allowance: allow1, + expires, + }; + let expect_two = AllowanceResponse { + allowance: allow2, + expires: Expiration::Never {}, + }; + assert_eq!( + query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(), + expect_one + ); + assert_eq!( + query_allowance(deps.as_ref(), owner.clone(), spender2.clone()).unwrap(), + expect_two + ); + assert_eq!( + query_allowance(deps.as_ref(), spender.clone(), spender2.clone()).unwrap(), + AllowanceResponse::default() + ); + + // also allow spender -> spender2 with no interference + let info = mock_info(spender.as_ref(), &[]); + let env = mock_env(); + let allow3 = Uint128::new(1821); + let expires3 = Expiration::AtTime(Timestamp::from_seconds(3767626296)); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender2.clone(), + amount: allow3, + expires: Some(expires3), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + let expect_three = AllowanceResponse { + allowance: allow3, + expires: expires3, + }; + assert_eq!( + query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(), + expect_one + ); + assert_eq!( + query_allowance(deps.as_ref(), owner, spender2.clone()).unwrap(), + expect_two + ); + assert_eq!( + query_allowance(deps.as_ref(), spender, spender2).unwrap(), + expect_three + ); + } + + #[test] + fn no_self_allowance() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + + let owner = String::from("addr0001"); + let info = mock_info(owner.as_ref(), &[]); + let env = mock_env(); + do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000)); + + // self-allowance + let msg = ExecuteMsg::IncreaseAllowance { + spender: owner.clone(), + amount: Uint128::new(7777), + expires: None, + }; + let err = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err(); + assert_eq!(err, ContractError::CannotSetOwnAccount {}); + + // decrease self-allowance + let msg = ExecuteMsg::DecreaseAllowance { + spender: owner, + amount: Uint128::new(7777), + expires: None, + }; + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::CannotSetOwnAccount {}); + } + + #[test] + fn transfer_from_respects_limits() { + let mut deps = mock_dependencies_with_balance(&[]); + let owner = String::from("addr0001"); + let spender = String::from("addr0002"); + let rcpt = String::from("addr0003"); + + let start = Uint128::new(999999); + do_instantiate(deps.as_mut(), &owner, start); + + // provide an allowance + let allow1 = Uint128::new(77777); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow1, + expires: None, + }; + let info = mock_info(owner.as_ref(), &[]); + let env = mock_env(); + execute(deps.as_mut(), env, info, msg).unwrap(); + + // valid transfer of part of the allowance + let transfer = Uint128::new(44444); + let msg = ExecuteMsg::TransferFrom { + owner: owner.clone(), + recipient: rcpt.clone(), + amount: transfer, + }; + let info = mock_info(spender.as_ref(), &[]); + let env = mock_env(); + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(res.attributes[0], attr("action", "transfer_from")); + + // make sure money arrived + assert_eq!( + get_balance(deps.as_ref(), owner.clone()), + start.checked_sub(transfer).unwrap() + ); + assert_eq!(get_balance(deps.as_ref(), rcpt.clone()), transfer); + + // ensure it looks good + let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); + let expect = AllowanceResponse { + allowance: allow1.checked_sub(transfer).unwrap(), + expires: Expiration::Never {}, + }; + assert_eq!(expect, allowance); + + // cannot send more than the allowance + let msg = ExecuteMsg::TransferFrom { + owner: owner.clone(), + recipient: rcpt.clone(), + amount: Uint128::new(33443), + }; + let info = mock_info(spender.as_ref(), &[]); + let env = mock_env(); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); + + // let us increase limit, but set the expiration to expire in the next block + let info = mock_info(owner.as_ref(), &[]); + let mut env = mock_env(); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: Uint128::new(1000), + expires: Some(Expiration::AtHeight(env.block.height + 1)), + }; + execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + env.block.height += 1; + + // we should now get the expiration error + let msg = ExecuteMsg::TransferFrom { + owner, + recipient: rcpt, + amount: Uint128::new(33443), + }; + let info = mock_info(spender.as_ref(), &[]); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::Expired {}); + } + + #[test] + fn burn_from_respects_limits() { + let mut deps = mock_dependencies_with_balance(&[]); + let owner = String::from("addr0001"); + let spender = String::from("addr0002"); + + let start = Uint128::new(999999); + do_instantiate(deps.as_mut(), &owner, start); + + // provide an allowance + let allow1 = Uint128::new(77777); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow1, + expires: None, + }; + let info = mock_info(owner.as_ref(), &[]); + let env = mock_env(); + execute(deps.as_mut(), env, info, msg).unwrap(); + + // valid burn of part of the allowance + let transfer = Uint128::new(44444); + let msg = ExecuteMsg::BurnFrom { + owner: owner.clone(), + amount: transfer, + }; + let info = mock_info(spender.as_ref(), &[]); + let env = mock_env(); + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(res.attributes[0], attr("action", "burn_from")); + + // make sure money burnt + assert_eq!( + get_balance(deps.as_ref(), owner.clone()), + start.checked_sub(transfer).unwrap() + ); + + // ensure it looks good + let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); + let expect = AllowanceResponse { + allowance: allow1.checked_sub(transfer).unwrap(), + expires: Expiration::Never {}, + }; + assert_eq!(expect, allowance); + + // cannot burn more than the allowance + let msg = ExecuteMsg::BurnFrom { + owner: owner.clone(), + amount: Uint128::new(33443), + }; + let info = mock_info(spender.as_ref(), &[]); + let env = mock_env(); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); + + // let us increase limit, but set the expiration to expire in the next block + let info = mock_info(owner.as_ref(), &[]); + let mut env = mock_env(); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: Uint128::new(1000), + expires: Some(Expiration::AtHeight(env.block.height + 1)), + }; + execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + // increase block height, so the limit is expired now + env.block.height += 1; + + // we should now get the expiration error + let msg = ExecuteMsg::BurnFrom { + owner, + amount: Uint128::new(33443), + }; + let info = mock_info(spender.as_ref(), &[]); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::Expired {}); + } + + #[test] + fn send_from_respects_limits() { + let mut deps = mock_dependencies_with_balance(&[]); + let owner = String::from("addr0001"); + let spender = String::from("addr0002"); + let contract = String::from("cool-dex"); + let send_msg = Binary::from(r#"{"some":123}"#.as_bytes()); + + let start = Uint128::new(999999); + do_instantiate(deps.as_mut(), &owner, start); + + // provide an allowance + let allow1 = Uint128::new(77777); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow1, + expires: None, + }; + let info = mock_info(owner.as_ref(), &[]); + let env = mock_env(); + execute(deps.as_mut(), env, info, msg).unwrap(); + + // valid send of part of the allowance + let transfer = Uint128::new(44444); + let msg = ExecuteMsg::SendFrom { + owner: owner.clone(), + amount: transfer, + contract: contract.clone(), + msg: send_msg.clone(), + }; + let info = mock_info(spender.as_ref(), &[]); + let env = mock_env(); + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(res.attributes[0], attr("action", "send_from")); + assert_eq!(1, res.messages.len()); + + // we record this as sent by the one who requested, not the one who was paying + let binary_msg = Cw20ReceiveMsg { + sender: spender.clone(), + amount: transfer, + msg: send_msg.clone(), + } + .into_binary() + .unwrap(); + assert_eq!( + res.messages[0], + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: contract.clone(), + msg: binary_msg, + funds: vec![], + })) + ); + + // make sure money sent + assert_eq!( + get_balance(deps.as_ref(), owner.clone()), + start.checked_sub(transfer).unwrap() + ); + assert_eq!(get_balance(deps.as_ref(), contract.clone()), transfer); + + // ensure it looks good + let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); + let expect = AllowanceResponse { + allowance: allow1.checked_sub(transfer).unwrap(), + expires: Expiration::Never {}, + }; + assert_eq!(expect, allowance); + + // cannot send more than the allowance + let msg = ExecuteMsg::SendFrom { + owner: owner.clone(), + amount: Uint128::new(33443), + contract: contract.clone(), + msg: send_msg.clone(), + }; + let info = mock_info(spender.as_ref(), &[]); + let env = mock_env(); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); + + // let us increase limit, but set the expiration to the next block + let info = mock_info(owner.as_ref(), &[]); + let mut env = mock_env(); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: Uint128::new(1000), + expires: Some(Expiration::AtHeight(env.block.height + 1)), + }; + execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + // increase block height, so the limit is expired now + env.block.height += 1; + + // we should now get the expiration error + let msg = ExecuteMsg::SendFrom { + owner, + amount: Uint128::new(33443), + contract, + msg: send_msg, + }; + let info = mock_info(spender.as_ref(), &[]); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::Expired {}); + } + + #[test] + fn no_past_expiration() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + + let owner = String::from("addr0001"); + let spender = String::from("addr0002"); + let info = mock_info(owner.as_ref(), &[]); + let env = mock_env(); + do_instantiate(deps.as_mut(), owner.clone(), Uint128::new(12340000)); + + // set allowance with height expiration at current block height + let expires = Expiration::AtHeight(env.block.height); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: Uint128::new(7777), + expires: Some(expires), + }; + + // ensure it is rejected + assert_eq!( + Err(ContractError::InvalidExpiration {}), + execute(deps.as_mut(), env.clone(), info.clone(), msg) + ); + + // set allowance with time expiration in the past + let expires = Expiration::AtTime(env.block.time.minus_seconds(1)); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: Uint128::new(7777), + expires: Some(expires), + }; + + // ensure it is rejected + assert_eq!( + Err(ContractError::InvalidExpiration {}), + execute(deps.as_mut(), env.clone(), info.clone(), msg) + ); + + // set allowance with height expiration at next block height + let expires = Expiration::AtHeight(env.block.height + 1); + let allow = Uint128::new(7777); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow, + expires: Some(expires), + }; + + execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + + // ensure it looks good + let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); + assert_eq!( + allowance, + AllowanceResponse { + allowance: allow, + expires + } + ); + + // set allowance with time expiration in the future + let expires = Expiration::AtTime(env.block.time.plus_seconds(10)); + let allow = Uint128::new(7777); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow, + expires: Some(expires), + }; + + execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + + // ensure it looks good + let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); + assert_eq!( + allowance, + AllowanceResponse { + allowance: allow + allow, // we increased twice + expires + } + ); + + // decrease with height expiration at current block height + let expires = Expiration::AtHeight(env.block.height); + let allow = Uint128::new(7777); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow, + expires: Some(expires), + }; + + // ensure it is rejected + assert_eq!( + Err(ContractError::InvalidExpiration {}), + execute(deps.as_mut(), env.clone(), info.clone(), msg) + ); + + // decrease with height expiration at next block height + let expires = Expiration::AtHeight(env.block.height + 1); + let allow = Uint128::new(7777); + let msg = ExecuteMsg::DecreaseAllowance { + spender: spender.clone(), + amount: allow, + expires: Some(expires), + }; + + execute(deps.as_mut(), env, info, msg).unwrap(); + + // ensure it looks good + let allowance = query_allowance(deps.as_ref(), owner, spender).unwrap(); + assert_eq!( + allowance, + AllowanceResponse { + allowance: allow, + expires + } + ); + } +} diff --git a/libraries/cw20-base/src/bin/schema.rs b/libraries/cw20-base/src/bin/schema.rs new file mode 100644 index 0000000..40d16fd --- /dev/null +++ b/libraries/cw20-base/src/bin/schema.rs @@ -0,0 +1,11 @@ +use cosmwasm_schema::write_api; + +use cw20_base::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg, + } +} diff --git a/libraries/cw20-base/src/contract.rs b/libraries/cw20-base/src/contract.rs new file mode 100644 index 0000000..7e3547b --- /dev/null +++ b/libraries/cw20-base/src/contract.rs @@ -0,0 +1,2236 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::Order::Ascending; +use cosmwasm_std::{ + to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Uint128, +}; + +use cw2::set_contract_version; +use cw20::{ + BalanceResponse, Cw20Coin, Cw20ReceiveMsg, DownloadLogoResponse, EmbeddedLogo, Logo, LogoInfo, + MarketingInfoResponse, MinterResponse, TokenInfoResponse, +}; +use cw_utils::ensure_from_older_version; + +use crate::allowances::{ + execute_burn_from, execute_decrease_allowance, execute_increase_allowance, execute_send_from, + execute_transfer_from, query_allowance, +}; +use crate::enumerable::{query_all_accounts, query_owner_allowances, query_spender_allowances}; +use crate::error::ContractError; +use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use crate::state::{ + MinterData, TokenInfo, ALLOWANCES, ALLOWANCES_SPENDER, BALANCES, LOGO, MARKETING_INFO, + TOKEN_INFO, +}; + +// version info for migration info +const CONTRACT_NAME: &str = "crates.io:cw20-base"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +const LOGO_SIZE_CAP: usize = 5 * 1024; + +/// Checks if data starts with XML preamble +fn verify_xml_preamble(data: &[u8]) -> Result<(), ContractError> { + // The easiest way to perform this check would be just match on regex, however regex + // compilation is heavy and probably not worth it. + + let preamble = data + .split_inclusive(|c| *c == b'>') + .next() + .ok_or(ContractError::InvalidXmlPreamble {})?; + + const PREFIX: &[u8] = b""; + + if !(preamble.starts_with(PREFIX) && preamble.ends_with(POSTFIX)) { + Err(ContractError::InvalidXmlPreamble {}) + } else { + Ok(()) + } + + // Additionally attributes format could be validated as they are well defined, as well as + // comments presence inside of preable, but it is probably not worth it. +} + +/// Validates XML logo +fn verify_xml_logo(logo: &[u8]) -> Result<(), ContractError> { + verify_xml_preamble(logo)?; + + if logo.len() > LOGO_SIZE_CAP { + Err(ContractError::LogoTooBig {}) + } else { + Ok(()) + } +} + +/// Validates png logo +fn verify_png_logo(logo: &[u8]) -> Result<(), ContractError> { + // PNG header format: + // 0x89 - magic byte, out of ASCII table to fail on 7-bit systems + // "PNG" ascii representation + // [0x0d, 0x0a] - dos style line ending + // 0x1a - dos control character, stop displaying rest of the file + // 0x0a - unix style line ending + const HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; + if logo.len() > LOGO_SIZE_CAP { + Err(ContractError::LogoTooBig {}) + } else if !logo.starts_with(&HEADER) { + Err(ContractError::InvalidPngHeader {}) + } else { + Ok(()) + } +} + +/// Checks if passed logo is correct, and if not, returns an error +fn verify_logo(logo: &Logo) -> Result<(), ContractError> { + match logo { + Logo::Embedded(EmbeddedLogo::Svg(logo)) => verify_xml_logo(logo), + Logo::Embedded(EmbeddedLogo::Png(logo)) => verify_png_logo(logo), + Logo::Url(_) => Ok(()), // Any reasonable url validation would be regex based, probably not worth it + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + mut deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + // check valid token info + msg.validate()?; + // create initial accounts + let total_supply = create_accounts(&mut deps, &msg.initial_balances)?; + + if let Some(limit) = msg.get_cap() { + if total_supply > limit { + return Err(StdError::generic_err("Initial supply greater than cap").into()); + } + } + + let mint = match msg.mint { + Some(m) => Some(MinterData { + minter: deps.api.addr_validate(&m.minter)?, + cap: m.cap, + }), + None => None, + }; + + // store token info + let data = TokenInfo { + name: msg.name, + symbol: msg.symbol, + decimals: msg.decimals, + total_supply, + mint, + }; + TOKEN_INFO.save(deps.storage, &data)?; + + if let Some(marketing) = msg.marketing { + let logo = if let Some(logo) = marketing.logo { + verify_logo(&logo)?; + LOGO.save(deps.storage, &logo)?; + + match logo { + Logo::Url(url) => Some(LogoInfo::Url(url)), + Logo::Embedded(_) => Some(LogoInfo::Embedded), + } + } else { + None + }; + + let data = MarketingInfoResponse { + project: marketing.project, + description: marketing.description, + marketing: marketing + .marketing + .map(|addr| deps.api.addr_validate(&addr)) + .transpose()?, + logo, + }; + MARKETING_INFO.save(deps.storage, &data)?; + } + + Ok(Response::default()) +} + +pub fn create_accounts( + deps: &mut DepsMut, + accounts: &[Cw20Coin], +) -> Result { + validate_accounts(accounts)?; + + let mut total_supply = Uint128::zero(); + for row in accounts { + let address = deps.api.addr_validate(&row.address)?; + BALANCES.save(deps.storage, &address, &row.amount)?; + total_supply += row.amount; + } + + Ok(total_supply) +} + +pub fn validate_accounts(accounts: &[Cw20Coin]) -> Result<(), ContractError> { + let mut addresses = accounts.iter().map(|c| &c.address).collect::>(); + addresses.sort(); + addresses.dedup(); + + if addresses.len() != accounts.len() { + Err(ContractError::DuplicateInitialBalanceAddresses {}) + } else { + Ok(()) + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::Transfer { recipient, amount } => { + execute_transfer(deps, env, info, recipient, amount) + } + ExecuteMsg::Burn { amount } => execute_burn(deps, env, info, amount), + ExecuteMsg::Send { + contract, + amount, + msg, + } => execute_send(deps, env, info, contract, amount, msg), + ExecuteMsg::Mint { recipient, amount } => execute_mint(deps, env, info, recipient, amount), + ExecuteMsg::IncreaseAllowance { + spender, + amount, + expires, + } => execute_increase_allowance(deps, env, info, spender, amount, expires), + ExecuteMsg::DecreaseAllowance { + spender, + amount, + expires, + } => execute_decrease_allowance(deps, env, info, spender, amount, expires), + ExecuteMsg::TransferFrom { + owner, + recipient, + amount, + } => execute_transfer_from(deps, env, info, owner, recipient, amount), + ExecuteMsg::BurnFrom { owner, amount } => execute_burn_from(deps, env, info, owner, amount), + ExecuteMsg::SendFrom { + owner, + contract, + amount, + msg, + } => execute_send_from(deps, env, info, owner, contract, amount, msg), + ExecuteMsg::UpdateMarketing { + project, + description, + marketing, + } => execute_update_marketing(deps, env, info, project, description, marketing), + ExecuteMsg::UploadLogo(logo) => execute_upload_logo(deps, env, info, logo), + ExecuteMsg::UpdateMinter { new_minter } => { + execute_update_minter(deps, env, info, new_minter) + } + } +} + +pub fn execute_transfer( + deps: DepsMut, + _env: Env, + info: MessageInfo, + recipient: String, + amount: Uint128, +) -> Result { + let rcpt_addr = deps.api.addr_validate(&recipient)?; + + BALANCES.update( + deps.storage, + &info.sender, + |balance: Option| -> StdResult<_> { + Ok(balance.unwrap_or_default().checked_sub(amount)?) + }, + )?; + BALANCES.update( + deps.storage, + &rcpt_addr, + |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, + )?; + + let res = Response::new() + .add_attribute("action", "transfer") + .add_attribute("from", info.sender) + .add_attribute("to", recipient) + .add_attribute("amount", amount); + Ok(res) +} + +pub fn execute_burn( + deps: DepsMut, + _env: Env, + info: MessageInfo, + amount: Uint128, +) -> Result { + // lower balance + BALANCES.update( + deps.storage, + &info.sender, + |balance: Option| -> StdResult<_> { + Ok(balance.unwrap_or_default().checked_sub(amount)?) + }, + )?; + // reduce total_supply + TOKEN_INFO.update(deps.storage, |mut info| -> StdResult<_> { + info.total_supply = info.total_supply.checked_sub(amount)?; + Ok(info) + })?; + + let res = Response::new() + .add_attribute("action", "burn") + .add_attribute("from", info.sender) + .add_attribute("amount", amount); + Ok(res) +} + +pub fn execute_mint( + deps: DepsMut, + _env: Env, + info: MessageInfo, + recipient: String, + amount: Uint128, +) -> Result { + let mut config = TOKEN_INFO + .may_load(deps.storage)? + .ok_or(ContractError::Unauthorized {})?; + + if config + .mint + .as_ref() + .ok_or(ContractError::Unauthorized {})? + .minter + != info.sender + { + return Err(ContractError::Unauthorized {}); + } + + // update supply and enforce cap + config.total_supply += amount; + if let Some(limit) = config.get_cap() { + if config.total_supply > limit { + return Err(ContractError::CannotExceedCap {}); + } + } + TOKEN_INFO.save(deps.storage, &config)?; + + // add amount to recipient balance + let rcpt_addr = deps.api.addr_validate(&recipient)?; + BALANCES.update( + deps.storage, + &rcpt_addr, + |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, + )?; + + let res = Response::new() + .add_attribute("action", "mint") + .add_attribute("to", recipient) + .add_attribute("amount", amount); + Ok(res) +} + +pub fn execute_send( + deps: DepsMut, + _env: Env, + info: MessageInfo, + contract: String, + amount: Uint128, + msg: Binary, +) -> Result { + let rcpt_addr = deps.api.addr_validate(&contract)?; + + // move the tokens to the contract + BALANCES.update( + deps.storage, + &info.sender, + |balance: Option| -> StdResult<_> { + Ok(balance.unwrap_or_default().checked_sub(amount)?) + }, + )?; + BALANCES.update( + deps.storage, + &rcpt_addr, + |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, + )?; + + let res = Response::new() + .add_attribute("action", "send") + .add_attribute("from", &info.sender) + .add_attribute("to", &contract) + .add_attribute("amount", amount) + .add_message( + Cw20ReceiveMsg { + sender: info.sender.into(), + amount, + msg, + } + .into_cosmos_msg(contract)?, + ); + Ok(res) +} + +pub fn execute_update_minter( + deps: DepsMut, + _env: Env, + info: MessageInfo, + new_minter: Option, +) -> Result { + let mut config = TOKEN_INFO + .may_load(deps.storage)? + .ok_or(ContractError::Unauthorized {})?; + + let mint = config.mint.as_ref().ok_or(ContractError::Unauthorized {})?; + if mint.minter != info.sender { + return Err(ContractError::Unauthorized {}); + } + + let minter_data = new_minter + .map(|new_minter| deps.api.addr_validate(&new_minter)) + .transpose()? + .map(|minter| MinterData { + minter, + cap: mint.cap, + }); + + config.mint = minter_data; + + TOKEN_INFO.save(deps.storage, &config)?; + + Ok(Response::default() + .add_attribute("action", "update_minter") + .add_attribute( + "new_minter", + config + .mint + .map(|m| m.minter.into_string()) + .unwrap_or_else(|| "None".to_string()), + )) +} + +pub fn execute_update_marketing( + deps: DepsMut, + _env: Env, + info: MessageInfo, + project: Option, + description: Option, + marketing: Option, +) -> Result { + let mut marketing_info = MARKETING_INFO + .may_load(deps.storage)? + .ok_or(ContractError::Unauthorized {})?; + + if marketing_info + .marketing + .as_ref() + .ok_or(ContractError::Unauthorized {})? + != &info.sender + { + return Err(ContractError::Unauthorized {}); + } + + match project { + Some(empty) if empty.trim().is_empty() => marketing_info.project = None, + Some(project) => marketing_info.project = Some(project), + None => (), + } + + match description { + Some(empty) if empty.trim().is_empty() => marketing_info.description = None, + Some(description) => marketing_info.description = Some(description), + None => (), + } + + match marketing { + Some(empty) if empty.trim().is_empty() => marketing_info.marketing = None, + Some(marketing) => marketing_info.marketing = Some(deps.api.addr_validate(&marketing)?), + None => (), + } + + if marketing_info.project.is_none() + && marketing_info.description.is_none() + && marketing_info.marketing.is_none() + && marketing_info.logo.is_none() + { + MARKETING_INFO.remove(deps.storage); + } else { + MARKETING_INFO.save(deps.storage, &marketing_info)?; + } + + let res = Response::new().add_attribute("action", "update_marketing"); + Ok(res) +} + +pub fn execute_upload_logo( + deps: DepsMut, + _env: Env, + info: MessageInfo, + logo: Logo, +) -> Result { + let mut marketing_info = MARKETING_INFO + .may_load(deps.storage)? + .ok_or(ContractError::Unauthorized {})?; + + verify_logo(&logo)?; + + if marketing_info + .marketing + .as_ref() + .ok_or(ContractError::Unauthorized {})? + != &info.sender + { + return Err(ContractError::Unauthorized {}); + } + + LOGO.save(deps.storage, &logo)?; + + let logo_info = match logo { + Logo::Url(url) => LogoInfo::Url(url), + Logo::Embedded(_) => LogoInfo::Embedded, + }; + + marketing_info.logo = Some(logo_info); + MARKETING_INFO.save(deps.storage, &marketing_info)?; + + let res = Response::new().add_attribute("action", "upload_logo"); + Ok(res) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Balance { address } => to_binary(&query_balance(deps, address)?), + QueryMsg::TokenInfo {} => to_binary(&query_token_info(deps)?), + QueryMsg::Minter {} => to_binary(&query_minter(deps)?), + QueryMsg::Allowance { owner, spender } => { + to_binary(&query_allowance(deps, owner, spender)?) + } + QueryMsg::AllAllowances { + owner, + start_after, + limit, + } => to_binary(&query_owner_allowances(deps, owner, start_after, limit)?), + QueryMsg::AllSpenderAllowances { + spender, + start_after, + limit, + } => to_binary(&query_spender_allowances( + deps, + spender, + start_after, + limit, + )?), + QueryMsg::AllAccounts { start_after, limit } => { + to_binary(&query_all_accounts(deps, start_after, limit)?) + } + QueryMsg::MarketingInfo {} => to_binary(&query_marketing_info(deps)?), + QueryMsg::DownloadLogo {} => to_binary(&query_download_logo(deps)?), + } +} + +pub fn query_balance(deps: Deps, address: String) -> StdResult { + let address = deps.api.addr_validate(&address)?; + let balance = BALANCES + .may_load(deps.storage, &address)? + .unwrap_or_default(); + Ok(BalanceResponse { balance }) +} + +pub fn query_token_info(deps: Deps) -> StdResult { + let info = TOKEN_INFO.load(deps.storage)?; + let res = TokenInfoResponse { + name: info.name, + symbol: info.symbol, + decimals: info.decimals, + total_supply: info.total_supply, + }; + Ok(res) +} + +pub fn query_minter(deps: Deps) -> StdResult> { + let meta = TOKEN_INFO.load(deps.storage)?; + let minter = match meta.mint { + Some(m) => Some(MinterResponse { + minter: m.minter.into(), + cap: m.cap, + }), + None => None, + }; + Ok(minter) +} + +pub fn query_marketing_info(deps: Deps) -> StdResult { + Ok(MARKETING_INFO.may_load(deps.storage)?.unwrap_or_default()) +} + +pub fn query_download_logo(deps: Deps) -> StdResult { + let logo = LOGO.load(deps.storage)?; + match logo { + Logo::Embedded(EmbeddedLogo::Svg(logo)) => Ok(DownloadLogoResponse { + mime_type: "image/svg+xml".to_owned(), + data: logo, + }), + Logo::Embedded(EmbeddedLogo::Png(logo)) => Ok(DownloadLogoResponse { + mime_type: "image/png".to_owned(), + data: logo, + }), + Logo::Url(_) => Err(StdError::not_found("logo")), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { + let original_version = + ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + if original_version < "0.14.0".parse::().unwrap() { + // Build reverse map of allowances per spender + let data = ALLOWANCES + .range(deps.storage, None, None, Ascending) + .collect::>>()?; + for ((owner, spender), allowance) in data { + ALLOWANCES_SPENDER.save(deps.storage, (&spender, &owner), &allowance)?; + } + } + Ok(Response::default()) +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::{ + mock_dependencies, mock_dependencies_with_balance, mock_env, mock_info, + }; + use cosmwasm_std::{coins, from_binary, Addr, CosmosMsg, StdError, SubMsg, WasmMsg}; + + use super::*; + use crate::msg::InstantiateMarketingInfo; + + fn get_balance>(deps: Deps, address: T) -> Uint128 { + query_balance(deps, address.into()).unwrap().balance + } + + // this will set up the instantiation for other tests + fn do_instantiate_with_minter( + deps: DepsMut, + addr: &str, + amount: Uint128, + minter: &str, + cap: Option, + ) -> TokenInfoResponse { + _do_instantiate( + deps, + addr, + amount, + Some(MinterResponse { + minter: minter.to_string(), + cap, + }), + ) + } + + // this will set up the instantiation for other tests + fn do_instantiate(deps: DepsMut, addr: &str, amount: Uint128) -> TokenInfoResponse { + _do_instantiate(deps, addr, amount, None) + } + + // this will set up the instantiation for other tests + fn _do_instantiate( + mut deps: DepsMut, + addr: &str, + amount: Uint128, + mint: Option, + ) -> TokenInfoResponse { + let instantiate_msg = InstantiateMsg { + name: "Auto Gen".to_string(), + symbol: "AUTO".to_string(), + decimals: 3, + initial_balances: vec![Cw20Coin { + address: addr.to_string(), + amount, + }], + mint: mint.clone(), + marketing: None, + }; + let info = mock_info("creator", &[]); + let env = mock_env(); + let res = instantiate(deps.branch(), env, info, instantiate_msg).unwrap(); + assert_eq!(0, res.messages.len()); + + let meta = query_token_info(deps.as_ref()).unwrap(); + assert_eq!( + meta, + TokenInfoResponse { + name: "Auto Gen".to_string(), + symbol: "AUTO".to_string(), + decimals: 3, + total_supply: amount, + } + ); + assert_eq!(get_balance(deps.as_ref(), addr), amount); + assert_eq!(query_minter(deps.as_ref()).unwrap(), mint,); + meta + } + + const PNG_HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; + + mod instantiate { + use super::*; + + #[test] + fn basic() { + let mut deps = mock_dependencies(); + let amount = Uint128::from(11223344u128); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![Cw20Coin { + address: String::from("addr0000"), + amount, + }], + mint: None, + marketing: None, + }; + let info = mock_info("creator", &[]); + let env = mock_env(); + let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); + assert_eq!(0, res.messages.len()); + + assert_eq!( + query_token_info(deps.as_ref()).unwrap(), + TokenInfoResponse { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + total_supply: amount, + } + ); + assert_eq!( + get_balance(deps.as_ref(), "addr0000"), + Uint128::new(11223344) + ); + } + + #[test] + fn mintable() { + let mut deps = mock_dependencies(); + let amount = Uint128::new(11223344); + let minter = String::from("asmodat"); + let limit = Uint128::new(511223344); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![Cw20Coin { + address: "addr0000".into(), + amount, + }], + mint: Some(MinterResponse { + minter: minter.clone(), + cap: Some(limit), + }), + marketing: None, + }; + let info = mock_info("creator", &[]); + let env = mock_env(); + let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); + assert_eq!(0, res.messages.len()); + + assert_eq!( + query_token_info(deps.as_ref()).unwrap(), + TokenInfoResponse { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + total_supply: amount, + } + ); + assert_eq!( + get_balance(deps.as_ref(), "addr0000"), + Uint128::new(11223344) + ); + assert_eq!( + query_minter(deps.as_ref()).unwrap(), + Some(MinterResponse { + minter, + cap: Some(limit), + }), + ); + } + + #[test] + fn mintable_over_cap() { + let mut deps = mock_dependencies(); + let amount = Uint128::new(11223344); + let minter = String::from("asmodat"); + let limit = Uint128::new(11223300); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![Cw20Coin { + address: String::from("addr0000"), + amount, + }], + mint: Some(MinterResponse { + minter, + cap: Some(limit), + }), + marketing: None, + }; + let info = mock_info("creator", &[]); + let env = mock_env(); + let err = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap_err(); + assert_eq!( + err, + StdError::generic_err("Initial supply greater than cap").into() + ); + } + + mod marketing { + use super::*; + + #[test] + fn basic() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("marketing".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + let env = mock_env(); + let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); + assert_eq!(0, res.messages.len()); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("marketing")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn invalid_marketing() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("m".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + let env = mock_env(); + instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap_err(); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + } + } + + #[test] + fn can_mint_by_minter() { + let mut deps = mock_dependencies(); + + let genesis = String::from("genesis"); + let amount = Uint128::new(11223344); + let minter = String::from("asmodat"); + let limit = Uint128::new(511223344); + do_instantiate_with_minter(deps.as_mut(), &genesis, amount, &minter, Some(limit)); + + // minter can mint coins to some winner + let winner = String::from("lucky"); + let prize = Uint128::new(222_222_222); + let msg = ExecuteMsg::Mint { + recipient: winner.clone(), + amount: prize, + }; + + let info = mock_info(minter.as_ref(), &[]); + let env = mock_env(); + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(0, res.messages.len()); + assert_eq!(get_balance(deps.as_ref(), genesis), amount); + assert_eq!(get_balance(deps.as_ref(), winner.clone()), prize); + + // Allows minting 0 + let msg = ExecuteMsg::Mint { + recipient: winner.clone(), + amount: Uint128::zero(), + }; + let info = mock_info(minter.as_ref(), &[]); + let env = mock_env(); + execute(deps.as_mut(), env, info, msg).unwrap(); + + // but if it exceeds cap (even over multiple rounds), it fails + // cap is enforced + let msg = ExecuteMsg::Mint { + recipient: winner, + amount: Uint128::new(333_222_222), + }; + let info = mock_info(minter.as_ref(), &[]); + let env = mock_env(); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::CannotExceedCap {}); + } + + #[test] + fn others_cannot_mint() { + let mut deps = mock_dependencies(); + do_instantiate_with_minter( + deps.as_mut(), + &String::from("genesis"), + Uint128::new(1234), + &String::from("minter"), + None, + ); + + let msg = ExecuteMsg::Mint { + recipient: String::from("lucky"), + amount: Uint128::new(222), + }; + let info = mock_info("anyone else", &[]); + let env = mock_env(); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + } + + #[test] + fn minter_can_update_minter_but_not_cap() { + let mut deps = mock_dependencies(); + let minter = String::from("minter"); + let cap = Some(Uint128::from(3000000u128)); + do_instantiate_with_minter( + deps.as_mut(), + &String::from("genesis"), + Uint128::new(1234), + &minter, + cap, + ); + + let new_minter = "new_minter"; + let msg = ExecuteMsg::UpdateMinter { + new_minter: Some(new_minter.to_string()), + }; + + let info = mock_info(&minter, &[]); + let env = mock_env(); + let res = execute(deps.as_mut(), env.clone(), info, msg); + assert!(res.is_ok()); + let query_minter_msg = QueryMsg::Minter {}; + let res = query(deps.as_ref(), env, query_minter_msg); + let mint: MinterResponse = from_binary(&res.unwrap()).unwrap(); + + // Minter cannot update cap. + assert!(mint.cap == cap); + assert!(mint.minter == new_minter) + } + + #[test] + fn others_cannot_update_minter() { + let mut deps = mock_dependencies(); + let minter = String::from("minter"); + do_instantiate_with_minter( + deps.as_mut(), + &String::from("genesis"), + Uint128::new(1234), + &minter, + None, + ); + + let msg = ExecuteMsg::UpdateMinter { + new_minter: Some("new_minter".to_string()), + }; + + let info = mock_info("not the minter", &[]); + let env = mock_env(); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + } + + #[test] + fn unset_minter() { + let mut deps = mock_dependencies(); + let minter = String::from("minter"); + let cap = None; + do_instantiate_with_minter( + deps.as_mut(), + &String::from("genesis"), + Uint128::new(1234), + &minter, + cap, + ); + + let msg = ExecuteMsg::UpdateMinter { new_minter: None }; + + let info = mock_info(&minter, &[]); + let env = mock_env(); + let res = execute(deps.as_mut(), env.clone(), info, msg); + assert!(res.is_ok()); + let query_minter_msg = QueryMsg::Minter {}; + let res = query(deps.as_ref(), env, query_minter_msg); + let mint: Option = from_binary(&res.unwrap()).unwrap(); + + // Check that mint information was removed. + assert_eq!(mint, None); + + // Check that old minter can no longer mint. + let msg = ExecuteMsg::Mint { + recipient: String::from("lucky"), + amount: Uint128::new(222), + }; + let info = mock_info("minter", &[]); + let env = mock_env(); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + } + + #[test] + fn no_one_mints_if_minter_unset() { + let mut deps = mock_dependencies(); + do_instantiate(deps.as_mut(), &String::from("genesis"), Uint128::new(1234)); + + let msg = ExecuteMsg::Mint { + recipient: String::from("lucky"), + amount: Uint128::new(222), + }; + let info = mock_info("genesis", &[]); + let env = mock_env(); + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(err, ContractError::Unauthorized {}); + } + + #[test] + fn instantiate_multiple_accounts() { + let mut deps = mock_dependencies(); + let amount1 = Uint128::from(11223344u128); + let addr1 = String::from("addr0001"); + let amount2 = Uint128::from(7890987u128); + let addr2 = String::from("addr0002"); + let info = mock_info("creator", &[]); + let env = mock_env(); + + // Fails with duplicate addresses + let instantiate_msg = InstantiateMsg { + name: "Bash Shell".to_string(), + symbol: "BASH".to_string(), + decimals: 6, + initial_balances: vec![ + Cw20Coin { + address: addr1.clone(), + amount: amount1, + }, + Cw20Coin { + address: addr1.clone(), + amount: amount2, + }, + ], + mint: None, + marketing: None, + }; + let err = + instantiate(deps.as_mut(), env.clone(), info.clone(), instantiate_msg).unwrap_err(); + assert_eq!(err, ContractError::DuplicateInitialBalanceAddresses {}); + + // Works with unique addresses + let instantiate_msg = InstantiateMsg { + name: "Bash Shell".to_string(), + symbol: "BASH".to_string(), + decimals: 6, + initial_balances: vec![ + Cw20Coin { + address: addr1.clone(), + amount: amount1, + }, + Cw20Coin { + address: addr2.clone(), + amount: amount2, + }, + ], + mint: None, + marketing: None, + }; + let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); + assert_eq!(0, res.messages.len()); + assert_eq!( + query_token_info(deps.as_ref()).unwrap(), + TokenInfoResponse { + name: "Bash Shell".to_string(), + symbol: "BASH".to_string(), + decimals: 6, + total_supply: amount1 + amount2, + } + ); + assert_eq!(get_balance(deps.as_ref(), addr1), amount1); + assert_eq!(get_balance(deps.as_ref(), addr2), amount2); + } + + #[test] + fn queries_work() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + let addr1 = String::from("addr0001"); + let amount1 = Uint128::from(12340000u128); + + let expected = do_instantiate(deps.as_mut(), &addr1, amount1); + + // check meta query + let loaded = query_token_info(deps.as_ref()).unwrap(); + assert_eq!(expected, loaded); + + let _info = mock_info("test", &[]); + let env = mock_env(); + // check balance query (full) + let data = query( + deps.as_ref(), + env.clone(), + QueryMsg::Balance { address: addr1 }, + ) + .unwrap(); + let loaded: BalanceResponse = from_binary(&data).unwrap(); + assert_eq!(loaded.balance, amount1); + + // check balance query (empty) + let data = query( + deps.as_ref(), + env, + QueryMsg::Balance { + address: String::from("addr0002"), + }, + ) + .unwrap(); + let loaded: BalanceResponse = from_binary(&data).unwrap(); + assert_eq!(loaded.balance, Uint128::zero()); + } + + #[test] + fn transfer() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + let addr1 = String::from("addr0001"); + let addr2 = String::from("addr0002"); + let amount1 = Uint128::from(12340000u128); + let transfer = Uint128::from(76543u128); + let too_much = Uint128::from(12340321u128); + + do_instantiate(deps.as_mut(), &addr1, amount1); + + // Allows transferring 0 + let info = mock_info(addr1.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Transfer { + recipient: addr2.clone(), + amount: Uint128::zero(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // cannot send more than we have + let info = mock_info(addr1.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Transfer { + recipient: addr2.clone(), + amount: too_much, + }; + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); + + // cannot send from empty account + let info = mock_info(addr2.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Transfer { + recipient: addr1.clone(), + amount: transfer, + }; + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); + + // valid transfer + let info = mock_info(addr1.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Transfer { + recipient: addr2.clone(), + amount: transfer, + }; + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(res.messages.len(), 0); + + let remainder = amount1.checked_sub(transfer).unwrap(); + assert_eq!(get_balance(deps.as_ref(), addr1), remainder); + assert_eq!(get_balance(deps.as_ref(), addr2), transfer); + assert_eq!( + query_token_info(deps.as_ref()).unwrap().total_supply, + amount1 + ); + } + + #[test] + fn burn() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + let addr1 = String::from("addr0001"); + let amount1 = Uint128::from(12340000u128); + let burn = Uint128::from(76543u128); + let too_much = Uint128::from(12340321u128); + + do_instantiate(deps.as_mut(), &addr1, amount1); + + // Allows burning 0 + let info = mock_info(addr1.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Burn { + amount: Uint128::zero(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!( + query_token_info(deps.as_ref()).unwrap().total_supply, + amount1 + ); + + // cannot burn more than we have + let info = mock_info(addr1.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Burn { amount: too_much }; + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); + assert_eq!( + query_token_info(deps.as_ref()).unwrap().total_supply, + amount1 + ); + + // valid burn reduces total supply + let info = mock_info(addr1.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Burn { amount: burn }; + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(res.messages.len(), 0); + + let remainder = amount1.checked_sub(burn).unwrap(); + assert_eq!(get_balance(deps.as_ref(), addr1), remainder); + assert_eq!( + query_token_info(deps.as_ref()).unwrap().total_supply, + remainder + ); + } + + #[test] + fn send() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + let addr1 = String::from("addr0001"); + let contract = String::from("addr0002"); + let amount1 = Uint128::from(12340000u128); + let transfer = Uint128::from(76543u128); + let too_much = Uint128::from(12340321u128); + let send_msg = Binary::from(r#"{"some":123}"#.as_bytes()); + + do_instantiate(deps.as_mut(), &addr1, amount1); + + // Allows sending 0 + let info = mock_info(addr1.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Send { + contract: contract.clone(), + amount: Uint128::zero(), + msg: send_msg.clone(), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // cannot send more than we have + let info = mock_info(addr1.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Send { + contract: contract.clone(), + amount: too_much, + msg: send_msg.clone(), + }; + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); + + // valid transfer + let info = mock_info(addr1.as_ref(), &[]); + let env = mock_env(); + let msg = ExecuteMsg::Send { + contract: contract.clone(), + amount: transfer, + msg: send_msg.clone(), + }; + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(res.messages.len(), 1); + + // ensure proper send message sent + // this is the message we want delivered to the other side + let binary_msg = Cw20ReceiveMsg { + sender: addr1.clone(), + amount: transfer, + msg: send_msg, + } + .into_binary() + .unwrap(); + // and this is how it must be wrapped for the vm to process it + assert_eq!( + res.messages[0], + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: contract.clone(), + msg: binary_msg, + funds: vec![], + })) + ); + + // ensure balance is properly transferred + let remainder = amount1.checked_sub(transfer).unwrap(); + assert_eq!(get_balance(deps.as_ref(), addr1), remainder); + assert_eq!(get_balance(deps.as_ref(), contract), transfer); + assert_eq!( + query_token_info(deps.as_ref()).unwrap().total_supply, + amount1 + ); + } + + mod migration { + use super::*; + + use cosmwasm_std::Empty; + use cw20::{AllAllowancesResponse, AllSpenderAllowancesResponse, SpenderAllowanceInfo}; + use cw_multi_test::{App, Contract, ContractWrapper, Executor}; + use cw_utils::Expiration; + + fn cw20_contract() -> Box> { + let contract = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ) + .with_migrate(crate::contract::migrate); + Box::new(contract) + } + + #[test] + fn test_migrate() { + let mut app = App::default(); + + let cw20_id = app.store_code(cw20_contract()); + let cw20_addr = app + .instantiate_contract( + cw20_id, + Addr::unchecked("sender"), + &InstantiateMsg { + name: "Token".to_string(), + symbol: "TOKEN".to_string(), + decimals: 6, + initial_balances: vec![Cw20Coin { + address: "sender".to_string(), + amount: Uint128::new(100), + }], + mint: None, + marketing: None, + }, + &[], + "TOKEN", + Some("sender".to_string()), + ) + .unwrap(); + + // no allowance to start + let allowance: AllAllowancesResponse = app + .wrap() + .query_wasm_smart( + cw20_addr.to_string(), + &QueryMsg::AllAllowances { + owner: "sender".to_string(), + start_after: None, + limit: None, + }, + ) + .unwrap(); + assert_eq!(allowance, AllAllowancesResponse::default()); + + // Set allowance + let allow1 = Uint128::new(7777); + let expires = Expiration::AtHeight(123_456); + let msg = CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: cw20_addr.to_string(), + msg: to_binary(&ExecuteMsg::IncreaseAllowance { + spender: "spender".into(), + amount: allow1, + expires: Some(expires), + }) + .unwrap(), + funds: vec![], + }); + app.execute(Addr::unchecked("sender"), msg).unwrap(); + + // Now migrate + app.execute( + Addr::unchecked("sender"), + CosmosMsg::Wasm(WasmMsg::Migrate { + contract_addr: cw20_addr.to_string(), + new_code_id: cw20_id, + msg: to_binary(&MigrateMsg {}).unwrap(), + }), + ) + .unwrap(); + + // Smoke check that the contract still works. + let balance: cw20::BalanceResponse = app + .wrap() + .query_wasm_smart( + cw20_addr.clone(), + &QueryMsg::Balance { + address: "sender".to_string(), + }, + ) + .unwrap(); + + assert_eq!(balance.balance, Uint128::new(100)); + + // Confirm that the allowance per spender is there + let allowance: AllSpenderAllowancesResponse = app + .wrap() + .query_wasm_smart( + cw20_addr, + &QueryMsg::AllSpenderAllowances { + spender: "spender".to_string(), + start_after: None, + limit: None, + }, + ) + .unwrap(); + assert_eq!( + allowance.allowances, + &[SpenderAllowanceInfo { + owner: "sender".to_string(), + allowance: allow1, + expires + }] + ); + } + } + + mod marketing { + use super::*; + + #[test] + fn update_unauthorised() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("marketing".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let err = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UpdateMarketing { + project: Some("New project".to_owned()), + description: Some("Better description".to_owned()), + marketing: Some("creator".to_owned()), + }, + ) + .unwrap_err(); + + assert_eq!(err, ContractError::Unauthorized {}); + + // Ensure marketing didn't change + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("marketing")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn update_project() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UpdateMarketing { + project: Some("New project".to_owned()), + description: None, + marketing: None, + }, + ) + .unwrap(); + + assert_eq!(res.messages, vec![]); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("New project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn clear_project() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UpdateMarketing { + project: Some("".to_owned()), + description: None, + marketing: None, + }, + ) + .unwrap(); + + assert_eq!(res.messages, vec![]); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: None, + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn update_description() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UpdateMarketing { + project: None, + description: Some("Better description".to_owned()), + marketing: None, + }, + ) + .unwrap(); + + assert_eq!(res.messages, vec![]); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Better description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn clear_description() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UpdateMarketing { + project: None, + description: Some("".to_owned()), + marketing: None, + }, + ) + .unwrap(); + + assert_eq!(res.messages, vec![]); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: None, + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn update_marketing() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UpdateMarketing { + project: None, + description: None, + marketing: Some("marketing".to_owned()), + }, + ) + .unwrap(); + + assert_eq!(res.messages, vec![]); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("marketing")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn update_marketing_invalid() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let err = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UpdateMarketing { + project: None, + description: None, + marketing: Some("m".to_owned()), + }, + ) + .unwrap_err(); + + assert!( + matches!(err, ContractError::Std(_)), + "Expected Std error, received: {}", + err + ); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn clear_marketing() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UpdateMarketing { + project: None, + description: None, + marketing: Some("".to_owned()), + }, + ) + .unwrap(); + + assert_eq!(res.messages, vec![]); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: None, + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn update_logo_url() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UploadLogo(Logo::Url("new_url".to_owned())), + ) + .unwrap(); + + assert_eq!(res.messages, vec![]); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("new_url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn update_logo_png() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Png(PNG_HEADER.into()))), + ) + .unwrap(); + + assert_eq!(res.messages, vec![]); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Embedded), + } + ); + + assert_eq!( + query_download_logo(deps.as_ref()).unwrap(), + DownloadLogoResponse { + mime_type: "image/png".to_owned(), + data: PNG_HEADER.into(), + } + ); + } + + #[test] + fn update_logo_svg() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let img = "".as_bytes(); + let res = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Svg(img.into()))), + ) + .unwrap(); + + assert_eq!(res.messages, vec![]); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Embedded), + } + ); + + assert_eq!( + query_download_logo(deps.as_ref()).unwrap(), + DownloadLogoResponse { + mime_type: "image/svg+xml".to_owned(), + data: img.into(), + } + ); + } + + #[test] + fn update_logo_png_oversized() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let img = [&PNG_HEADER[..], &[1; 6000][..]].concat(); + let err = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Png(img.into()))), + ) + .unwrap_err(); + + assert_eq!(err, ContractError::LogoTooBig {}); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn update_logo_svg_oversized() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let img = [ + "", + std::str::from_utf8(&[b'x'; 6000]).unwrap(), + "", + ] + .concat() + .into_bytes(); + + let err = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Svg(img.into()))), + ) + .unwrap_err(); + + assert_eq!(err, ContractError::LogoTooBig {}); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn update_logo_png_invalid() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let img = &[1]; + let err = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Png(img.into()))), + ) + .unwrap_err(); + + assert_eq!(err, ContractError::InvalidPngHeader {}); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + + #[test] + fn update_logo_svg_invalid() { + let mut deps = mock_dependencies(); + let instantiate_msg = InstantiateMsg { + name: "Cash Token".to_string(), + symbol: "CASH".to_string(), + decimals: 9, + initial_balances: vec![], + mint: None, + marketing: Some(InstantiateMarketingInfo { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some("creator".to_owned()), + logo: Some(Logo::Url("url".to_owned())), + }), + }; + + let info = mock_info("creator", &[]); + + instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); + + let img = &[1]; + + let err = execute( + deps.as_mut(), + mock_env(), + info, + ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Svg(img.into()))), + ) + .unwrap_err(); + + assert_eq!(err, ContractError::InvalidXmlPreamble {}); + + assert_eq!( + query_marketing_info(deps.as_ref()).unwrap(), + MarketingInfoResponse { + project: Some("Project".to_owned()), + description: Some("Description".to_owned()), + marketing: Some(Addr::unchecked("creator")), + logo: Some(LogoInfo::Url("url".to_owned())), + } + ); + + let err = query_download_logo(deps.as_ref()).unwrap_err(); + assert!( + matches!(err, StdError::NotFound { .. }), + "Expected StdError::NotFound, received {}", + err + ); + } + } +} diff --git a/libraries/cw20-base/src/enumerable.rs b/libraries/cw20-base/src/enumerable.rs new file mode 100644 index 0000000..f465134 --- /dev/null +++ b/libraries/cw20-base/src/enumerable.rs @@ -0,0 +1,319 @@ +use cosmwasm_std::{Deps, Order, StdResult}; +use cw20::{ + AllAccountsResponse, AllAllowancesResponse, AllSpenderAllowancesResponse, AllowanceInfo, + SpenderAllowanceInfo, +}; + +use crate::state::{ALLOWANCES, ALLOWANCES_SPENDER, BALANCES}; +use cw_storage_plus::Bound; + +// settings for pagination +const MAX_LIMIT: u32 = 30; +const DEFAULT_LIMIT: u32 = 10; + +pub fn query_owner_allowances( + deps: Deps, + owner: String, + start_after: Option, + limit: Option, +) -> StdResult { + let owner_addr = deps.api.addr_validate(&owner)?; + let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + let start = start_after.map(|s| Bound::ExclusiveRaw(s.into_bytes())); + + let allowances = ALLOWANCES + .prefix(&owner_addr) + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|item| { + item.map(|(addr, allow)| AllowanceInfo { + spender: addr.into(), + allowance: allow.allowance, + expires: allow.expires, + }) + }) + .collect::>()?; + Ok(AllAllowancesResponse { allowances }) +} + +pub fn query_spender_allowances( + deps: Deps, + spender: String, + start_after: Option, + limit: Option, +) -> StdResult { + let spender_addr = deps.api.addr_validate(&spender)?; + let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + let start = start_after.map(|s| Bound::ExclusiveRaw(s.into_bytes())); + + let allowances = ALLOWANCES_SPENDER + .prefix(&spender_addr) + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|item| { + item.map(|(addr, allow)| SpenderAllowanceInfo { + owner: addr.into(), + allowance: allow.allowance, + expires: allow.expires, + }) + }) + .collect::>()?; + Ok(AllSpenderAllowancesResponse { allowances }) +} + +pub fn query_all_accounts( + deps: Deps, + start_after: Option, + limit: Option, +) -> StdResult { + let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); + + let accounts = BALANCES + .keys(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|item| item.map(Into::into)) + .collect::>()?; + + Ok(AllAccountsResponse { accounts }) +} + +#[cfg(test)] +mod tests { + use super::*; + + use cosmwasm_std::testing::{mock_dependencies_with_balance, mock_env, mock_info}; + use cosmwasm_std::{coins, from_binary, DepsMut, Uint128}; + use cw20::{Cw20Coin, Expiration, TokenInfoResponse}; + + use crate::contract::{execute, instantiate, query, query_token_info}; + use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + // this will set up the instantiation for other tests + fn do_instantiate(mut deps: DepsMut, addr: &str, amount: Uint128) -> TokenInfoResponse { + let instantiate_msg = InstantiateMsg { + name: "Auto Gen".to_string(), + symbol: "AUTO".to_string(), + decimals: 3, + initial_balances: vec![Cw20Coin { + address: addr.into(), + amount, + }], + mint: None, + marketing: None, + }; + let info = mock_info("creator", &[]); + let env = mock_env(); + instantiate(deps.branch(), env, info, instantiate_msg).unwrap(); + query_token_info(deps.as_ref()).unwrap() + } + + #[test] + fn query_all_owner_allowances_works() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + + let owner = String::from("owner"); + // these are in alphabetical order same than insert order + let spender1 = String::from("earlier"); + let spender2 = String::from("later"); + + let info = mock_info(owner.as_ref(), &[]); + let env = mock_env(); + do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000)); + + // no allowance to start + let allowances = query_owner_allowances(deps.as_ref(), owner.clone(), None, None).unwrap(); + assert_eq!(allowances.allowances, vec![]); + + // set allowance with height expiration + let allow1 = Uint128::new(7777); + let expires = Expiration::AtHeight(123_456); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender1.clone(), + amount: allow1, + expires: Some(expires), + }; + execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + + // set allowance with no expiration + let allow2 = Uint128::new(54321); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender2.clone(), + amount: allow2, + expires: None, + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // query list gets 2 + let allowances = query_owner_allowances(deps.as_ref(), owner.clone(), None, None).unwrap(); + assert_eq!(allowances.allowances.len(), 2); + + // first one is spender1 (order of CanonicalAddr uncorrelated with String) + let allowances = + query_owner_allowances(deps.as_ref(), owner.clone(), None, Some(1)).unwrap(); + assert_eq!(allowances.allowances.len(), 1); + let allow = &allowances.allowances[0]; + assert_eq!(&allow.spender, &spender1); + assert_eq!(&allow.expires, &expires); + assert_eq!(&allow.allowance, &allow1); + + // next one is spender2 + let allowances = query_owner_allowances( + deps.as_ref(), + owner, + Some(allow.spender.clone()), + Some(10000), + ) + .unwrap(); + assert_eq!(allowances.allowances.len(), 1); + let allow = &allowances.allowances[0]; + assert_eq!(&allow.spender, &spender2); + assert_eq!(&allow.expires, &Expiration::Never {}); + assert_eq!(&allow.allowance, &allow2); + } + + #[test] + fn query_all_spender_allowances_works() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + + // these are in alphabetical order same than insert order + let owner1 = String::from("owner1"); + let owner2 = String::from("owner2"); + let spender = String::from("spender"); + + let info = mock_info(owner1.as_ref(), &[]); + let env = mock_env(); + do_instantiate(deps.as_mut(), &owner1, Uint128::new(12340000)); + + // no allowance to start + let allowances = + query_spender_allowances(deps.as_ref(), spender.clone(), None, None).unwrap(); + assert_eq!(allowances.allowances, vec![]); + + // set allowance with height expiration + let allow1 = Uint128::new(7777); + let expires = Expiration::AtHeight(123_456); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow1, + expires: Some(expires), + }; + execute(deps.as_mut(), env, info, msg).unwrap(); + + // set allowance with no expiration, from the other owner + let info = mock_info(owner2.as_ref(), &[]); + let env = mock_env(); + do_instantiate(deps.as_mut(), &owner2, Uint128::new(12340000)); + + let allow2 = Uint128::new(54321); + let msg = ExecuteMsg::IncreaseAllowance { + spender: spender.clone(), + amount: allow2, + expires: None, + }; + execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + // query list gets both + let msg = QueryMsg::AllSpenderAllowances { + spender: spender.clone(), + start_after: None, + limit: None, + }; + let allowances: AllSpenderAllowancesResponse = + from_binary(&query(deps.as_ref(), env.clone(), msg).unwrap()).unwrap(); + assert_eq!(allowances.allowances.len(), 2); + + // one is owner1 (order of CanonicalAddr uncorrelated with String) + let msg = QueryMsg::AllSpenderAllowances { + spender: spender.clone(), + start_after: None, + limit: Some(1), + }; + let allowances: AllSpenderAllowancesResponse = + from_binary(&query(deps.as_ref(), env.clone(), msg).unwrap()).unwrap(); + assert_eq!(allowances.allowances.len(), 1); + let allow = &allowances.allowances[0]; + assert_eq!(&allow.owner, &owner1); + assert_eq!(&allow.expires, &expires); + assert_eq!(&allow.allowance, &allow1); + + // other one is owner2 + let msg = QueryMsg::AllSpenderAllowances { + spender, + start_after: Some(owner1), + limit: Some(10000), + }; + let allowances: AllSpenderAllowancesResponse = + from_binary(&query(deps.as_ref(), env, msg).unwrap()).unwrap(); + assert_eq!(allowances.allowances.len(), 1); + let allow = &allowances.allowances[0]; + assert_eq!(&allow.owner, &owner2); + assert_eq!(&allow.expires, &Expiration::Never {}); + assert_eq!(&allow.allowance, &allow2); + } + + #[test] + fn query_all_accounts_works() { + let mut deps = mock_dependencies_with_balance(&coins(2, "token")); + + // insert order and lexicographical order are different + let acct1 = String::from("acct01"); + let acct2 = String::from("zebra"); + let acct3 = String::from("nice"); + let acct4 = String::from("aaaardvark"); + let expected_order = [acct4.clone(), acct1.clone(), acct3.clone(), acct2.clone()]; + + do_instantiate(deps.as_mut(), &acct1, Uint128::new(12340000)); + + // put money everywhere (to create balanaces) + let info = mock_info(acct1.as_ref(), &[]); + let env = mock_env(); + execute( + deps.as_mut(), + env.clone(), + info.clone(), + ExecuteMsg::Transfer { + recipient: acct2, + amount: Uint128::new(222222), + }, + ) + .unwrap(); + execute( + deps.as_mut(), + env.clone(), + info.clone(), + ExecuteMsg::Transfer { + recipient: acct3, + amount: Uint128::new(333333), + }, + ) + .unwrap(); + execute( + deps.as_mut(), + env, + info, + ExecuteMsg::Transfer { + recipient: acct4, + amount: Uint128::new(444444), + }, + ) + .unwrap(); + + // make sure we get the proper results + let accounts = query_all_accounts(deps.as_ref(), None, None).unwrap(); + assert_eq!(accounts.accounts, expected_order); + + // let's do pagination + let accounts = query_all_accounts(deps.as_ref(), None, Some(2)).unwrap(); + assert_eq!(accounts.accounts, expected_order[0..2].to_vec()); + + let accounts = + query_all_accounts(deps.as_ref(), Some(accounts.accounts[1].clone()), Some(1)).unwrap(); + assert_eq!(accounts.accounts, expected_order[2..3].to_vec()); + + let accounts = + query_all_accounts(deps.as_ref(), Some(accounts.accounts[0].clone()), Some(777)) + .unwrap(); + assert_eq!(accounts.accounts, expected_order[3..].to_vec()); + } +} diff --git a/libraries/cw20-base/src/error.rs b/libraries/cw20-base/src/error.rs new file mode 100644 index 0000000..a0b880c --- /dev/null +++ b/libraries/cw20-base/src/error.rs @@ -0,0 +1,43 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Unauthorized")] + Unauthorized {}, + + #[error("Cannot set to own account")] + CannotSetOwnAccount {}, + + // Unused error case. Zero is now treated like every other value. + #[deprecated(note = "Unused. All zero amount checks have been removed")] + #[error("Invalid zero amount")] + InvalidZeroAmount {}, + + #[error("Allowance is expired")] + Expired {}, + + #[error("No allowance for this account")] + NoAllowance {}, + + #[error("Minting cannot exceed the cap")] + CannotExceedCap {}, + + #[error("Logo binary data exceeds 5KB limit")] + LogoTooBig {}, + + #[error("Invalid xml preamble for SVG")] + InvalidXmlPreamble {}, + + #[error("Invalid png header")] + InvalidPngHeader {}, + + #[error("Invalid expiration value")] + InvalidExpiration {}, + + #[error("Duplicate initial balance addresses")] + DuplicateInitialBalanceAddresses {}, +} diff --git a/libraries/cw20-base/src/lib.rs b/libraries/cw20-base/src/lib.rs new file mode 100644 index 0000000..7b601f2 --- /dev/null +++ b/libraries/cw20-base/src/lib.rs @@ -0,0 +1,24 @@ +/*! +This is a basic implementation of a cw20 contract. It implements +the [CW20 spec](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw20/README.md) and is designed to +be deployed as is, or imported into other contracts to easily build +cw20-compatible tokens with custom logic. + +Implements: + +- [x] CW20 Base +- [x] Mintable extension +- [x] Allowances extension + +For more information on this contract, please check out the +[README](https://github.com/CosmWasm/cw-plus/blob/main/contracts/cw20-base/README.md). +*/ + +pub mod allowances; +pub mod contract; +pub mod enumerable; +mod error; +pub mod msg; +pub mod state; + +pub use crate::error::ContractError; diff --git a/libraries/cw20-base/src/msg.rs b/libraries/cw20-base/src/msg.rs new file mode 100644 index 0000000..2088712 --- /dev/null +++ b/libraries/cw20-base/src/msg.rs @@ -0,0 +1,175 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{StdError, StdResult, Uint128}; +use cw20::{Cw20Coin, Logo, MinterResponse}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +pub use cw20::Cw20ExecuteMsg as ExecuteMsg; + +#[cw_serde] +pub struct InstantiateMarketingInfo { + pub project: Option, + pub description: Option, + pub marketing: Option, + pub logo: Option, +} + +#[cw_serde] +#[cfg_attr(test, derive(Default))] +pub struct InstantiateMsg { + pub name: String, + pub symbol: String, + pub decimals: u8, + pub initial_balances: Vec, + pub mint: Option, + pub marketing: Option, +} + +impl InstantiateMsg { + pub fn get_cap(&self) -> Option { + self.mint.as_ref().and_then(|v| v.cap) + } + + pub fn validate(&self) -> StdResult<()> { + // Check name, symbol, decimals + if !self.has_valid_name() { + return Err(StdError::generic_err( + "Name is not in the expected format (3-50 UTF-8 bytes)", + )); + } + if !self.has_valid_symbol() { + return Err(StdError::generic_err( + "Ticker symbol is not in expected format [a-zA-Z\\-]{3,12}", + )); + } + if self.decimals > 18 { + return Err(StdError::generic_err("Decimals must not exceed 18")); + } + Ok(()) + } + + fn has_valid_name(&self) -> bool { + let bytes = self.name.as_bytes(); + if bytes.len() < 3 || bytes.len() > 50 { + return false; + } + true + } + + fn has_valid_symbol(&self) -> bool { + let bytes = self.symbol.as_bytes(); + if bytes.len() < 3 || bytes.len() > 12 { + return false; + } + for byte in bytes.iter() { + if (*byte != 45) && (*byte < 65 || *byte > 90) && (*byte < 97 || *byte > 122) { + return false; + } + } + true + } +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + /// Returns the current balance of the given address, 0 if unset. + #[returns(cw20::BalanceResponse)] + Balance { address: String }, + /// Returns metadata on the contract - name, decimals, supply, etc. + #[returns(cw20::TokenInfoResponse)] + TokenInfo {}, + /// Only with "mintable" extension. + /// Returns who can mint and the hard cap on maximum tokens after minting. + #[returns(cw20::MinterResponse)] + Minter {}, + /// Only with "allowance" extension. + /// Returns how much spender can use from owner account, 0 if unset. + #[returns(cw20::AllowanceResponse)] + Allowance { owner: String, spender: String }, + /// Only with "enumerable" extension (and "allowances") + /// Returns all allowances this owner has approved. Supports pagination. + #[returns(cw20::AllAllowancesResponse)] + AllAllowances { + owner: String, + start_after: Option, + limit: Option, + }, + /// Only with "enumerable" extension (and "allowances") + /// Returns all allowances this spender has been granted. Supports pagination. + #[returns(cw20::AllSpenderAllowancesResponse)] + AllSpenderAllowances { + spender: String, + start_after: Option, + limit: Option, + }, + /// Only with "enumerable" extension + /// Returns all accounts that have balances. Supports pagination. + #[returns(cw20::AllAccountsResponse)] + AllAccounts { + start_after: Option, + limit: Option, + }, + /// Only with "marketing" extension + /// Returns more metadata on the contract to display in the client: + /// - description, logo, project url, etc. + #[returns(cw20::MarketingInfoResponse)] + MarketingInfo {}, + /// Only with "marketing" extension + /// Downloads the embedded logo data (if stored on chain). Errors if no logo data is stored for this + /// contract. + #[returns(cw20::DownloadLogoResponse)] + DownloadLogo {}, +} + +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct MigrateMsg {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validate_instantiatemsg_name() { + // Too short + let mut msg = InstantiateMsg { + name: str::repeat("a", 2), + ..InstantiateMsg::default() + }; + assert!(!msg.has_valid_name()); + + // In the correct length range + msg.name = str::repeat("a", 3); + assert!(msg.has_valid_name()); + + // Too long + msg.name = str::repeat("a", 51); + assert!(!msg.has_valid_name()); + } + + #[test] + fn validate_instantiatemsg_symbol() { + // Too short + let mut msg = InstantiateMsg { + symbol: str::repeat("a", 2), + ..InstantiateMsg::default() + }; + assert!(!msg.has_valid_symbol()); + + // In the correct length range + msg.symbol = str::repeat("a", 3); + assert!(msg.has_valid_symbol()); + + // Too long + msg.symbol = str::repeat("a", 13); + assert!(!msg.has_valid_symbol()); + + // Has illegal char + let illegal_chars = [[64u8], [91u8], [123u8]]; + illegal_chars.iter().for_each(|c| { + let c = std::str::from_utf8(c).unwrap(); + msg.symbol = str::repeat(c, 3); + assert!(!msg.has_valid_symbol()); + }); + } +} diff --git a/libraries/cw20-base/src/state.rs b/libraries/cw20-base/src/state.rs new file mode 100644 index 0000000..dc02c6f --- /dev/null +++ b/libraries/cw20-base/src/state.rs @@ -0,0 +1,36 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Uint128}; +use cw_storage_plus::{Item, Map}; + +use cw20::{AllowanceResponse, Logo, MarketingInfoResponse}; + +#[cw_serde] +pub struct TokenInfo { + pub name: String, + pub symbol: String, + pub decimals: u8, + pub total_supply: Uint128, + pub mint: Option, +} + +#[cw_serde] +pub struct MinterData { + pub minter: Addr, + /// cap is how many more tokens can be issued by the minter + pub cap: Option, +} + +impl TokenInfo { + pub fn get_cap(&self) -> Option { + self.mint.as_ref().and_then(|v| v.cap) + } +} + +pub const TOKEN_INFO: Item = Item::new("token_info"); +pub const MARKETING_INFO: Item = Item::new("marketing_info"); +pub const LOGO: Item = Item::new("logo"); +pub const BALANCES: Map<&Addr, Uint128> = Map::new("balance"); +pub const ALLOWANCES: Map<(&Addr, &Addr), AllowanceResponse> = Map::new("allowance"); +// TODO: After https://github.com/CosmWasm/cw-plus/issues/670 is implemented, replace this with a `MultiIndex` over `ALLOWANCES` +pub const ALLOWANCES_SPENDER: Map<(&Addr, &Addr), AllowanceResponse> = + Map::new("allowance_spender"); From df7b555aef3e406b34c04c8508bdf31f7a1eea7d Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Mon, 29 May 2023 13:45:57 +0545 Subject: [PATCH 04/37] Balanced_HubToken: states and msg added --- Cargo.lock | 60 +++++++++++++++++++ Cargo.toml | 3 +- .../token-contracts/cw-hub-bnusd/Cargo.toml | 1 + .../cw-hub-bnusd/src/contract.rs | 15 ++++- .../token-contracts/cw-hub-bnusd/src/msg.rs | 19 +++++- .../token-contracts/cw-hub-bnusd/src/state.rs | 26 ++++++++ 6 files changed, 117 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88498e1..4e141cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "common" +version = "0.1.0" +dependencies = [ + "bytes", + "cw20", + "hex", + "rlp-derive", + "rustc-hex", + "serde", +] + [[package]] name = "const-oid" version = "0.9.2" @@ -215,6 +227,7 @@ dependencies = [ "cw-multi-test", "cw-storage-plus", "cw2", + "cw20", "schemars", "serde", "thiserror", @@ -278,6 +291,36 @@ dependencies = [ "serde", ] +[[package]] +name = "cw20" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "schemars", + "serde", +] + +[[package]] +name = "cw20-base" +version = "1.0.1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "cw-storage-plus", + "cw-utils", + "cw2", + "cw20", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "der" version = "0.6.1" @@ -572,6 +615,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "ryu" version = "1.0.13" diff --git a/Cargo.toml b/Cargo.toml index 6b9736f..408269d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [workspace] members = [ # "contracts/core-contracts/*", - "contracts/token-contracts/cw-hub-bnusd"] + "contracts/token-contracts/cw-hub-bnusd", + "libraries/rust/common",] [profile.release] diff --git a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml index 4224de1..65ebbf7 100644 --- a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml +++ b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml @@ -37,6 +37,7 @@ cw2 = "1.0.1" schemars = "0.8.12" serde = { version = "1.0.163", default-features = false, features = ["derive"] } thiserror = { version = "1.0.40" } +cw20 = { version = "1.0.1", default-features = false } [dev-dependencies] cw-multi-test = "0.16.2" diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index a463640..dcf771c 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -5,6 +5,9 @@ use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult} use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::X_CALL; + +use common::icallservice::ICallService; /* // version info for migration info @@ -14,12 +17,18 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( - _deps: DepsMut, + deps: DepsMut, _env: Env, _info: MessageInfo, - _msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { - unimplemented!() + X_CALL.save(deps.storage, &msg.xCall)?; + let x_call = X_CALL.load(deps.storage)?; + xCallBTPAddress = ICallService::get_btp_address(&xCall); + let (nid, _) = BTPAddress::parse_btp_address(&xCallBTPAddress)?; + let (hubNet, hubAddress) = BTPAddress::parse_network_address(&_hubAddress)?; + OWNER.save(deps.storage, &deps.api.addr_validate(&info.sender).expect("Issue with Transfer"))?; + Ok(Response::default()) } #[cfg_attr(not(feature = "library"), entry_point)] diff --git a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs b/contracts/token-contracts/cw-hub-bnusd/src/msg.rs index 3b6b9b2..9368b11 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/msg.rs @@ -1,11 +1,24 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::Addr; +use common :: Types; #[cw_serde] -pub struct InstantiateMsg {} +pub struct InstantiateMsg { + pub xCall: Addr, + pub hubAddress: String, +} #[cw_serde] -pub enum ExecuteMsg {} +pub enum ExecuteMsg { + setup {_xCall: Addr, _hubAddress: String}, + handleCallMessage {_from: Addr, _data: String}, + crossTransfer {to : Addr, amount: u128, data: Bytes}, + xCrossTransfer {from: Addr, crossTransferData: Types::CrossTransfer}, + xCrossTransferRevert {from: Addr, crossTransferRevertData: Types::CrossTransferRevert}, +} #[cw_serde] #[derive(QueryResponses)] -pub enum QueryMsg {} +pub enum QueryMsg { + +} diff --git a/contracts/token-contracts/cw-hub-bnusd/src/state.rs b/contracts/token-contracts/cw-hub-bnusd/src/state.rs index 8b13789..603a9ae 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/state.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/state.rs @@ -1 +1,27 @@ +use cosmwasm_std::Addr; +use cw_storage_plus::{Item, Map}; + +pub const CONNECTED_CHAINS: &'static str = "connected_chains"; +pub const SPOKE_CONTRACTS: &'static str = "spoke_contract"; +pub const CROSS_CHAIN_SUPPLY: &'static str = "cross_chain_supply"; + +pub const CROSSCHAINSUPPLY: Map<&String,u128> = Map::new(CROSS_CHAIN_SUPPLY); +pub const CONNECTEDCHAINS: Item> = Item::new(CONNECTED_CHAINS); + + +// pub const NAME: Item = Item::new("name"); +// pub const SYMBOL: Item = Item::new("symbol"); +// pub const DECIMAL: Item = Item::new("decimal"); +pub const OWNER: Item = Item::new("owner"); +// pub const TOTALSUPPLY: Item = Item::new("total_supply"); + +// pub const BALANCES: Map<&Addr,u128> = Map::new("balances"); + +// const ZERO_ADDRESSES: &'static str = "0000000000000000000000000000000000000000"; + +pub const X_CALL : Item = Item::new("xCall"); +pub const X_CALL_BTP_ADDRESS : Item = Item::new("xCallBTPAddress"); +pub const NID : Item = Item::new("nid"); +pub const HUB_ADDRESS : Item = Item::new("hubAddress"); +pub const HUB_NET : Item = Item::new("hubNet"); From df961847090076b42e9841f4bd2638efd1362baf Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Fri, 2 Jun 2023 12:54:58 +0545 Subject: [PATCH 05/37] cw20 base contract imported --- Cargo.lock | 18 +- .../cw20-base/.cargo/config | 0 {libraries => contracts}/cw20-base/Cargo.toml | 0 {libraries => contracts}/cw20-base/README.md | 0 .../cw20-base/src/allowances.rs | 0 .../cw20-base/src/bin/schema.rs | 0 .../cw20-base/src/contract.rs | 0 .../cw20-base/src/enumerable.rs | 0 .../cw20-base/src/error.rs | 0 {libraries => contracts}/cw20-base/src/lib.rs | 0 {libraries => contracts}/cw20-base/src/msg.rs | 0 .../cw20-base/src/state.rs | 0 .../token-contracts/cw-hub-bnusd/Cargo.toml | 4 + .../cw-hub-bnusd/src/bin/schema.rs | 14 +- .../cw-hub-bnusd/src/contract.rs | 227 +++++++++++++++++- .../token-contracts/cw-hub-bnusd/src/error.rs | 14 ++ .../token-contracts/cw-hub-bnusd/src/msg.rs | 36 ++- libraries/rust/common/Cargo.toml | 1 + libraries/rust/common/src/btpAddress.rs | 69 ++++++ libraries/rust/common/src/lib.rs | 5 +- libraries/rust/common/src/parseAddress.rs | 58 +++++ libraries/rust/common/src/rlp/mod.rs | 1 + libraries/rust/common/src/rlpdecode.rs | 106 ++++++++ libraries/rust/common/src/types.rs | 2 +- 24 files changed, 525 insertions(+), 30 deletions(-) rename {libraries => contracts}/cw20-base/.cargo/config (100%) rename {libraries => contracts}/cw20-base/Cargo.toml (100%) rename {libraries => contracts}/cw20-base/README.md (100%) rename {libraries => contracts}/cw20-base/src/allowances.rs (100%) rename {libraries => contracts}/cw20-base/src/bin/schema.rs (100%) rename {libraries => contracts}/cw20-base/src/contract.rs (100%) rename {libraries => contracts}/cw20-base/src/enumerable.rs (100%) rename {libraries => contracts}/cw20-base/src/error.rs (100%) rename {libraries => contracts}/cw20-base/src/lib.rs (100%) rename {libraries => contracts}/cw20-base/src/msg.rs (100%) rename {libraries => contracts}/cw20-base/src/state.rs (100%) create mode 100644 libraries/rust/common/src/btpAddress.rs create mode 100644 libraries/rust/common/src/parseAddress.rs create mode 100644 libraries/rust/common/src/rlpdecode.rs diff --git a/Cargo.lock b/Cargo.lock index 4e141cb..94f7a35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,8 +78,10 @@ name = "common" version = "0.1.0" dependencies = [ "bytes", + "cosmwasm-std", "cw20", "hex", + "rlp", "rlp-derive", "rustc-hex", "serde", @@ -221,6 +223,8 @@ dependencies = [ name = "cw-hub-bnusd" version = "0.1.0" dependencies = [ + "bytes", + "common", "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", @@ -228,6 +232,7 @@ dependencies = [ "cw-storage-plus", "cw2", "cw20", + "cw20-base", "schemars", "serde", "thiserror", @@ -307,10 +312,11 @@ dependencies = [ [[package]] name = "cw20-base" version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcd279230b08ed8afd8be5828221622bd5b9ce25d0b01d58bad626c6ce0169c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-multi-test", "cw-storage-plus", "cw-utils", "cw2", @@ -615,6 +621,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "rlp-derive" version = "0.1.0" diff --git a/libraries/cw20-base/.cargo/config b/contracts/cw20-base/.cargo/config similarity index 100% rename from libraries/cw20-base/.cargo/config rename to contracts/cw20-base/.cargo/config diff --git a/libraries/cw20-base/Cargo.toml b/contracts/cw20-base/Cargo.toml similarity index 100% rename from libraries/cw20-base/Cargo.toml rename to contracts/cw20-base/Cargo.toml diff --git a/libraries/cw20-base/README.md b/contracts/cw20-base/README.md similarity index 100% rename from libraries/cw20-base/README.md rename to contracts/cw20-base/README.md diff --git a/libraries/cw20-base/src/allowances.rs b/contracts/cw20-base/src/allowances.rs similarity index 100% rename from libraries/cw20-base/src/allowances.rs rename to contracts/cw20-base/src/allowances.rs diff --git a/libraries/cw20-base/src/bin/schema.rs b/contracts/cw20-base/src/bin/schema.rs similarity index 100% rename from libraries/cw20-base/src/bin/schema.rs rename to contracts/cw20-base/src/bin/schema.rs diff --git a/libraries/cw20-base/src/contract.rs b/contracts/cw20-base/src/contract.rs similarity index 100% rename from libraries/cw20-base/src/contract.rs rename to contracts/cw20-base/src/contract.rs diff --git a/libraries/cw20-base/src/enumerable.rs b/contracts/cw20-base/src/enumerable.rs similarity index 100% rename from libraries/cw20-base/src/enumerable.rs rename to contracts/cw20-base/src/enumerable.rs diff --git a/libraries/cw20-base/src/error.rs b/contracts/cw20-base/src/error.rs similarity index 100% rename from libraries/cw20-base/src/error.rs rename to contracts/cw20-base/src/error.rs diff --git a/libraries/cw20-base/src/lib.rs b/contracts/cw20-base/src/lib.rs similarity index 100% rename from libraries/cw20-base/src/lib.rs rename to contracts/cw20-base/src/lib.rs diff --git a/libraries/cw20-base/src/msg.rs b/contracts/cw20-base/src/msg.rs similarity index 100% rename from libraries/cw20-base/src/msg.rs rename to contracts/cw20-base/src/msg.rs diff --git a/libraries/cw20-base/src/state.rs b/contracts/cw20-base/src/state.rs similarity index 100% rename from libraries/cw20-base/src/state.rs rename to contracts/cw20-base/src/state.rs diff --git a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml index 65ebbf7..ab82ca1 100644 --- a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml +++ b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml @@ -38,6 +38,10 @@ schemars = "0.8.12" serde = { version = "1.0.163", default-features = false, features = ["derive"] } thiserror = { version = "1.0.40" } cw20 = { version = "1.0.1", default-features = false } +cw20-base = { version = "1.0.1", features = ["library"] } +bytes = "1.0" +common = { path = "../../../libraries/rust/common" } + [dev-dependencies] cw-multi-test = "0.16.2" diff --git a/contracts/token-contracts/cw-hub-bnusd/src/bin/schema.rs b/contracts/token-contracts/cw-hub-bnusd/src/bin/schema.rs index 91f938a..b7a9d63 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/bin/schema.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/bin/schema.rs @@ -1,11 +1,11 @@ -use cosmwasm_schema::write_api; +// use cosmwasm_schema::write_api; -use cw_hub_bnusd::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +// use cw_hub_bnusd::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { - write_api! { - instantiate: InstantiateMsg, - execute: ExecuteMsg, - query: QueryMsg, - } + // write_api! { + // instantiate: InstantiateMsg, + // execute: ExecuteMsg, + // query: QueryMsg, + // } } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index dcf771c..5a3b86a 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,13 +1,21 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, Uint128}; // use cw2::set_contract_version; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::state::X_CALL; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, Types}; +use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; +use bytes::Bytes; +use common::btpAddress::BTPAddress; use common::icallservice::ICallService; +use common::parseAddress::ParseAddress; + +use common::rlpdecode::RLPItem; + +use cw20_base::contract::{execute_burn, execute_mint}; +use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; /* // version info for migration info @@ -22,23 +30,64 @@ pub fn instantiate( _info: MessageInfo, msg: InstantiateMsg, ) -> Result { - X_CALL.save(deps.storage, &msg.xCall)?; + // create initial accounts + // store token info using cw20-base format + let data = TokenInfo { + name: "HubToken".to_string(), + symbol: "HUBT".to_string(), + decimals: 18, + total_supply: Uint128::zero(), + // set self as minter, so we can properly execute mint and burn + mint: Some(MinterData { + minter: _env.contract.address, + cap: None, + }), + }; + + TOKEN_INFO.save(deps.storage, &data)?; + + X_CALL.save(deps.storage, &msg.x_call)?; let x_call = X_CALL.load(deps.storage)?; - xCallBTPAddress = ICallService::get_btp_address(&xCall); - let (nid, _) = BTPAddress::parse_btp_address(&xCallBTPAddress)?; - let (hubNet, hubAddress) = BTPAddress::parse_network_address(&_hubAddress)?; - OWNER.save(deps.storage, &deps.api.addr_validate(&info.sender).expect("Issue with Transfer"))?; + // xCallBTPAddress = ICallService::get_btp_address(&x_call, _env)?; + let (nid, _) = BTPAddress::parse_btp_address(&"aaa")?; + let (hubNet, hubAddress) = BTPAddress::parse_network_address(&msg.hub_address)?; + + X_CALL_BTP_ADDRESS.save(deps.storage, &"sss".to_string())?; + NID.save(deps.storage, &nid.to_string())?; + HUB_ADDRESS.save(deps.storage, &hubAddress.to_string())?; + HUB_NET.save(deps.storage, &hubNet.to_string())?; + OWNER.save(deps.storage, &_info.sender)?; Ok(Response::default()) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( - _deps: DepsMut, + deps: DepsMut, _env: Env, - _info: MessageInfo, - _msg: ExecuteMsg, + info: MessageInfo, + msg: ExecuteMsg, ) -> Result { - unimplemented!() + use ExecuteMsg::*; + match msg { + Setup { + _xCall, + _hubAddress, + } => execute::setup(deps, _env, info, _xCall, _hubAddress), + HandleCallMessage { _from, _data } => { + execute::handle_call_message(deps, _env, info, _from, _data) + } + CrossTransfer { to, amount, data } => { + execute::cross_transfer(deps, _env, info, to, amount, data.into()) + } + XCrossTransfer { + from, + crossTransferData, + } => execute::x_cross_transfer(deps, _env, info, from, crossTransferData), + XCrossTransferRevert { + from, + crossTransferRevertData, + } => execute::x_cross_transfer_revert(deps, _env, info, from, crossTransferRevertData), + } } #[cfg_attr(not(feature = "library"), entry_point)] @@ -46,5 +95,159 @@ pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { unimplemented!() } +mod execute { + use super::*; + + pub fn setup( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + x_call: Addr, + hub_address: String, + ) -> Result { + X_CALL.save(deps.storage, &x_call)?; + // xCallBTPAddress = ICallService::get_btp_address(&x_call, _env)?; + let (nid, _) = BTPAddress::parse_btp_address(&"xCallBTPAddress")?; + let (hubNet, hubAddress) = BTPAddress::parse_network_address(&hub_address)?; + + X_CALL_BTP_ADDRESS.save(deps.storage, &"xCallBTPAddress".to_string())?; + NID.save(deps.storage, &nid.to_string())?; + HUB_ADDRESS.save(deps.storage, &hubAddress.to_string())?; + HUB_NET.save(deps.storage, &hubNet.to_string())?; + OWNER.save(deps.storage, &_info.sender)?; + Ok(Response::default()) + } + + pub fn handle_call_message( + deps: DepsMut, + _env: Env, + info: MessageInfo, + from: Addr, + data: Vec, + ) -> Result { + // let mut data = RLPItem::new(data.len(),data.as_ptr()).to_list(); + // let method: String = String::from(data[0].to_bytes()?); + + // match method.as_str() { + // "xCrossTransfer" => { + // let cross_transfer_data: Types::CrossTransfer = + // RLPDecodeStruct::decode_cross_transfer(&data)?; + // x_cross_transfer(from, cross_transfer_data)?; + // } + // "xCrossTransferRevert" => { + // let cross_transfer_revert_data: Types::CrossTransferRevert = + // RLPDecodeStruct::decode_cross_transfer_revert(&data)?; + // x_cross_transfer_revert(from, cross_transfer_revert_data)?; + // } + // _ => { + // return Err(ContractError::InvalidMethod); + // } + // } + + Ok(Response::default()) + } + + pub fn cross_transfer( + deps: DepsMut, + env: Env, + info: MessageInfo, + to: Addr, + amount: u128, + data: Bytes, + ) -> Result { + execute_burn(deps, env, info.clone(), amount.into()); + let mut nid = NID.load(deps.storage)?; + let mut hub_net: String = HUB_NET.load(deps.storage)?; + let mut hub_address: String = HUB_ADDRESS.load(deps.storage)?; + + let from = BTPAddress::btp_address(&nid, &info.sender.to_string()); + + let call_data = Types::CrossTransfer { + from: from.clone(), + to: to.to_string().clone(), + value: amount.clone(), + data: data.to_vec(), + }; + + let rollback_data = Types::CrossTransferRevert { + from, + value: amount, + }; + + let hub_btp_address = BTPAddress::btp_address(&hub_net, &hub_address); + // let call_message = CallMessage { + // address: hub_btp_address, + // method: call_data.encode_cross_transfer_message()?, + // revert_data: Some(rollback_data.encode_cross_transfer_revert_message()?), + // value: info.funds, + // }; + + // let res: Response = deps.querier.query(&call_message.into())?; + + Ok(Response::default()) + } + + pub fn x_cross_transfer( + deps: DepsMut, + env: Env, + info: MessageInfo, + from: Addr, + cross_transfer_data: Types::CrossTransfer, + ) -> Result { + let mut nid = NID.load(deps.storage)?; + let mut hub_net: String = HUB_NET.load(deps.storage)?; + let mut hub_address: String = HUB_ADDRESS.load(deps.storage)?; + + let btp_address = BTPAddress::btp_address(&hub_net, &hub_address); + + if from != btp_address { + return Err(ContractError::Unauthorized {}); + } + + let (net, account) = BTPAddress::parse_network_address(&cross_transfer_data.to)?; + if net != nid { + return Err(ContractError::WrongNetwork); + } + + let to = ParseAddress::parse_address(&account, "Invalid to Address")?; + + let res = execute_mint(deps, env, info, to, cross_transfer_data.value.into()) + .expect("Fail to mint"); + + // let res = _mint(deps, to, cross_transfer_data.value)?; + + // TODO Emit log + + Ok(res) + } + + pub fn x_cross_transfer_revert( + deps: DepsMut, + env: Env, + info: MessageInfo, + from: Addr, + cross_transfer_revert_data: Types::CrossTransferRevert, + ) -> Result { + let mut nid = NID.load(deps.storage)?; + let mut x_call_btp_address = X_CALL_BTP_ADDRESS.load(deps.storage)?; + + if from != x_call_btp_address { + return Err(ContractError::OnlyCallService); + } + + let (net, account) = BTPAddress::parse_network_address(&cross_transfer_revert_data.from)?; + if net != nid { + return Err(ContractError::InvalidBTPAddress); + } + + let to = ParseAddress::parse_address(&account, "Invalid to Address")?; + + let res = execute_mint(deps, env, info, to, cross_transfer_revert_data.value.into()) + .expect("Fail to mint"); + + Ok(res) + } +} + #[cfg(test)] mod tests {} diff --git a/contracts/token-contracts/cw-hub-bnusd/src/error.rs b/contracts/token-contracts/cw-hub-bnusd/src/error.rs index 4a69d8f..4cf6d90 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/error.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/error.rs @@ -8,6 +8,20 @@ pub enum ContractError { #[error("Unauthorized")] Unauthorized {}, + #[error("Wrong Address")] + WrongAddress, + #[error("Invalid BTP Address")] + InvalidBTPAddress, + #[error("Wrong Network")] + WrongNetwork, + #[error("Invalid to Address")] + InvalidToAddress, + #[error("OnlyCallService")] + OnlyCallService, + #[error("OnlyHub")] + OnlyHub, + #[error("Invalid Method")] + InvalidMethod, // Add any other custom errors you like here. // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs b/contracts/token-contracts/cw-hub-bnusd/src/msg.rs index 9368b11..def41bd 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/msg.rs @@ -1,20 +1,40 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::Addr; -use common :: Types; + +pub mod Types { + use super::*; + + #[cw_serde] + pub struct CrossTransfer { + pub from: String, + pub to: String, + pub value: u128, + pub data: Vec, + } + + #[cw_serde] + pub struct CrossTransferRevert { + pub from: String, + pub value: u128, + } +} #[cw_serde] pub struct InstantiateMsg { - pub xCall: Addr, - pub hubAddress: String, + pub x_call: Addr, + pub hub_address: String, + pub name: String, + pub symbol: String, + pub decimals: u8, } #[cw_serde] pub enum ExecuteMsg { - setup {_xCall: Addr, _hubAddress: String}, - handleCallMessage {_from: Addr, _data: String}, - crossTransfer {to : Addr, amount: u128, data: Bytes}, - xCrossTransfer {from: Addr, crossTransferData: Types::CrossTransfer}, - xCrossTransferRevert {from: Addr, crossTransferRevertData: Types::CrossTransferRevert}, + Setup {_xCall: Addr, _hubAddress: String}, + HandleCallMessage {_from: Addr, _data: Vec}, + CrossTransfer {to : Addr, amount: u128, data: Vec}, + XCrossTransfer {from: Addr, crossTransferData: Types::CrossTransfer}, + XCrossTransferRevert {from: Addr, crossTransferRevertData: Types::CrossTransferRevert}, } #[cw_serde] diff --git a/libraries/rust/common/Cargo.toml b/libraries/rust/common/Cargo.toml index fe9feb3..6b706af 100644 --- a/libraries/rust/common/Cargo.toml +++ b/libraries/rust/common/Cargo.toml @@ -13,3 +13,4 @@ rustc-hex = { version = "2.1.0", default-features = false } serde = { version = "1.0.156", default-features = false,features = ["derive"] } hex ={ version = "0.4.3", default-features = false } cosmwasm-std = { version = "1.2.5", default-features = false } +rlp = { version = "0.5.2", default-features = false } diff --git a/libraries/rust/common/src/btpAddress.rs b/libraries/rust/common/src/btpAddress.rs new file mode 100644 index 0000000..2a01f63 --- /dev/null +++ b/libraries/rust/common/src/btpAddress.rs @@ -0,0 +1,69 @@ +use cosmwasm_std::{StdError, StdResult}; + +pub struct BTPAddress; + +impl BTPAddress { + const PREFIX: &'static [u8] = b"btp://"; + const REVERT: &'static str = "invalidBTPAddress"; + const DELIMITER: &'static [u8] = b"/"; + + pub fn parse_btp_address(_str: &str) -> StdResult<(&str, &str)> { + let offset = BTPAddress::_validate(_str)?; + let network_address = &_str[6..offset]; + let account_address = &_str[offset + 1..]; + Ok((network_address, account_address)) + } + + pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { + let offset = BTPAddress::_validate_network(_str)?; + let network_address = &_str[0..offset]; + let account_address = &_str[offset + 1..]; + Ok((network_address, account_address)) + } + + pub fn network_address(_str: &str) -> StdResult<&str> { + let offset = BTPAddress::_validate(_str)?; + let network_address = &_str[6..offset]; + Ok(network_address) + } + + fn _validate(_str: &str) -> StdResult { + let bytes = _str.as_bytes(); + for (i, &byte) in bytes.iter().enumerate() { + if i < 6 { + if byte != BTPAddress::PREFIX[i] { + return Err(StdError::generic_err(BTPAddress::REVERT)); + } + } else if byte == BTPAddress::DELIMITER[0] { + if i > 6 && i < (bytes.len() - 1) { + return Ok(i); + } else { + return Err(StdError::generic_err(BTPAddress::REVERT)); + } + } + } + Err(StdError::generic_err(BTPAddress::REVERT)) + } + + fn _validate_network(_str: &str) -> StdResult { + let bytes = _str.as_bytes(); + for (i, &byte) in bytes.iter().enumerate() { + if byte == BTPAddress::DELIMITER[0] { + if i < (bytes.len() - 1) { + return Ok(i); + } else { + return Err(StdError::generic_err(BTPAddress::REVERT)); + } + } + } + Err(StdError::generic_err(BTPAddress::REVERT)) + } + + fn _slice(_str: &str, from: usize, to: usize) -> &str { + &_str[from..to] + } + + pub fn btp_address(network: &str, account: &str) -> String { + format!("{:?}{}{:?}{}", BTPAddress::PREFIX, network, BTPAddress::DELIMITER, account) + } +} diff --git a/libraries/rust/common/src/lib.rs b/libraries/rust/common/src/lib.rs index c014f22..87616e6 100644 --- a/libraries/rust/common/src/lib.rs +++ b/libraries/rust/common/src/lib.rs @@ -1,4 +1,7 @@ pub mod rlp; pub mod types; pub mod icallservice; -pub mod strings; \ No newline at end of file +pub mod strings; +pub mod btpAddress; +pub mod parseAddress; +pub mod rlpdecode; \ No newline at end of file diff --git a/libraries/rust/common/src/parseAddress.rs b/libraries/rust/common/src/parseAddress.rs new file mode 100644 index 0000000..b39a20f --- /dev/null +++ b/libraries/rust/common/src/parseAddress.rs @@ -0,0 +1,58 @@ +pub mod ParseAddress{ +use cosmwasm_std::{StdError, StdResult}; + +pub fn parse_address(account: &str, revert_msg: &str) -> StdResult { + let account_bytes = account.as_bytes(); + + if account_bytes.len() != 42 || account_bytes[0] != b'0' || account_bytes[1] != b'x' { + return Err(StdError::generic_err(revert_msg)); + } + + let mut account_address_bytes: Vec = vec![0; 20]; + + for i in 0..40 { + let b = account_bytes[i + 2]; + + let is_valid_ascii = match b { + 48..=57 | 65..=70 | 97..=102 => true, + _ => false, + }; + + if !is_valid_ascii { + return Err(StdError::generic_err(revert_msg)); + } + + let ascii_offset = if b < 65 { + 48 // 0-9 + } else if b > 102 { + 87 // a-f + } else { + 55 // A-F + }; + + let nibble = if i % 2 == 0 { + b - ascii_offset + } else { + (account_address_bytes[(i - 1) / 2] << 4) + (b - ascii_offset) + }; + + if i % 2 == 1 { + account_address_bytes[(i - 1) / 2] = nibble; + } + } + + let packed = hex::encode(account_address_bytes); + let mut account_address: &str = ""; + + if account_address == "0000000000000000000000000000000000000000" { + for i in 2..account_bytes.len() { + if account_bytes[i] != b'0' { + return Err(StdError::generic_err(revert_msg)); + } + } + } + + Ok(account_address.to_string()) +} + +} \ No newline at end of file diff --git a/libraries/rust/common/src/rlp/mod.rs b/libraries/rust/common/src/rlp/mod.rs index d2d1b67..cb971d0 100644 --- a/libraries/rust/common/src/rlp/mod.rs +++ b/libraries/rust/common/src/rlp/mod.rs @@ -1,3 +1,4 @@ + #![allow(unused)] // Copyright 2020 Parity Technologies // diff --git a/libraries/rust/common/src/rlpdecode.rs b/libraries/rust/common/src/rlpdecode.rs new file mode 100644 index 0000000..bbdee83 --- /dev/null +++ b/libraries/rust/common/src/rlpdecode.rs @@ -0,0 +1,106 @@ +extern crate rlp; + +use crate::rlp::{DecoderError, Rlp, RlpStream}; + +mod constants { + pub const STRING_SHORT_START: u8 = 0x80; + pub const STRING_LONG_START: u8 = 0xb8; + pub const LIST_SHORT_START: u8 = 0xc0; + pub const LIST_LONG_START: u8 = 0xf8; + pub const WORD_SIZE: u8 = 32; +} + +#[derive(Debug)] +pub struct RLPItem { + len: usize, + mem_ptr: *const u8, +} + +impl RLPItem { + pub fn new(len: usize, mem_ptr: *const u8) -> RLPItem { + RLPItem { len, mem_ptr } + } + + pub fn to_list(&self) -> Result, DecoderError> { + if !self.is_list() { + return Err(DecoderError::RlpExpectedToBeList); + } + + let items = self.num_items(); + let mut result = Vec::with_capacity(items); + + let mut mem_ptr = self.mem_ptr as usize + self.payload_offset(); + let mut data_len: usize; + for _ in 0..items { + data_len = self.item_length(mem_ptr as *const u8)?; + result.push(RLPItem::new(data_len, mem_ptr as *const u8)); + mem_ptr = mem_ptr + data_len; + } + + Ok(result) + } + + fn is_list(&self) -> bool { + if self.len == 0 { + return false; + } + + let byte0 = unsafe { *self.mem_ptr }; + if byte0 < constants::LIST_SHORT_START { + return false; + } + + true + } + + fn item_length(&self, ptr: *const u8) -> Result { + let byte0 = unsafe { *ptr }; + match byte0 { + 0x80..=0xb7 => Ok(1), + 0xb8..=0xbf => { + let len_bytes = byte0 - constants::LIST_LONG_START + 1; + let length = self.decode_length(unsafe { ptr.add(1) }, len_bytes.into())?; + Ok((1 + len_bytes + (length as u8)).into()) + } + _ => Err(DecoderError::RlpInvalidIndirection), + } + } + + fn payload_offset(&self) -> usize { + if self.len == 0 { + return 0; + } + + let byte0 = unsafe { *self.mem_ptr }; + match byte0 { + 0x80..=0xb7 => 0, + 0xb8..=0xbf => (byte0 - constants::LIST_LONG_START + 1).into(), + _ => 0, + } + } + + fn decode_length(&self, ptr: *const u8, len: usize) -> Result { + let mut length = 0; + for i in 0..len { + let byte = unsafe { *ptr.add(i) }; + length = length * 256 + byte as usize; + } + + Ok(length) + } + + fn num_items(&self) -> usize { + if self.len == 0 { + return 0; + } + + let byte0 = unsafe { *self.mem_ptr }; + match byte0 { + 0x80..=0xb7 => (byte0 - constants::LIST_SHORT_START).into(), + 0xb8..=0xbf => self + .decode_length(unsafe { self.mem_ptr.add(1) }, (byte0 - constants::LIST_LONG_START + 1).into()) + .unwrap(), + _ => 0, + } + } +} diff --git a/libraries/rust/common/src/types.rs b/libraries/rust/common/src/types.rs index ca32a8a..69a9812 100644 --- a/libraries/rust/common/src/types.rs +++ b/libraries/rust/common/src/types.rs @@ -1,4 +1,4 @@ -pub mod types { +pub mod Types { pub struct CrossTransfer { pub from: String, pub to: String, From f229d60edc68e132fa6360dd2a6825780e713160 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Mon, 12 Jun 2023 05:20:52 +0545 Subject: [PATCH 06/37] hubtoken almost completed --- Cargo.lock | 1 + .../token-contracts/cw-hub-bnusd/Cargo.toml | 1 + .../cw-hub-bnusd/src/constants.rs | 3 + .../cw-hub-bnusd/src/contract.rs | 274 +++++++++++++----- .../token-contracts/cw-hub-bnusd/src/error.rs | 7 + .../token-contracts/cw-hub-bnusd/src/lib.rs | 2 + .../token-contracts/cw-hub-bnusd/src/msg.rs | 57 ++-- .../token-contracts/cw-hub-bnusd/src/state.rs | 2 +- .../token-contracts/cw-hub-bnusd/src/types.rs | 51 ++++ libraries/rust/common/src/btpAddress.rs | 25 ++ libraries/rust/common/src/icallservice.rs | 23 -- libraries/rust/common/src/lib.rs | 5 - libraries/rust/common/src/parseAddress.rs | 58 ---- libraries/rust/common/src/rlpdecode.rs | 106 ------- libraries/rust/common/src/strings.rs | 45 --- libraries/rust/common/src/types.rs | 13 - 16 files changed, 318 insertions(+), 355 deletions(-) create mode 100644 contracts/token-contracts/cw-hub-bnusd/src/constants.rs create mode 100644 contracts/token-contracts/cw-hub-bnusd/src/types.rs delete mode 100644 libraries/rust/common/src/icallservice.rs delete mode 100644 libraries/rust/common/src/parseAddress.rs delete mode 100644 libraries/rust/common/src/rlpdecode.rs delete mode 100644 libraries/rust/common/src/strings.rs delete mode 100644 libraries/rust/common/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 94f7a35..38d90d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,6 +233,7 @@ dependencies = [ "cw2", "cw20", "cw20-base", + "hex", "schemars", "serde", "thiserror", diff --git a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml index ab82ca1..ea6c3fb 100644 --- a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml +++ b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml @@ -41,6 +41,7 @@ cw20 = { version = "1.0.1", default-features = false } cw20-base = { version = "1.0.1", features = ["library"] } bytes = "1.0" common = { path = "../../../libraries/rust/common" } +hex = "0.4.3" [dev-dependencies] diff --git a/contracts/token-contracts/cw-hub-bnusd/src/constants.rs b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs new file mode 100644 index 0000000..73a65f9 --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs @@ -0,0 +1,3 @@ +//Constants for Reply messages + +pub const REPLY_MSG_SUCCESS: u64 = 1; \ No newline at end of file diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index 5a3b86a..d9a2071 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,22 +1,23 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, Uint128}; +use cosmwasm_std::{ + Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, Uint128, +}; // use cw2::set_contract_version; - +use crate::constants::REPLY_MSG_SUCCESS; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, Types}; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, XCallMsg}; use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; use bytes::Bytes; use common::btpAddress::BTPAddress; -use common::icallservice::ICallService; -use common::parseAddress::ParseAddress; - -use common::rlpdecode::RLPItem; - use cw20_base::contract::{execute_burn, execute_mint}; use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; +use common::rlp::{DecoderError, Rlp}; + +use crate::types::Types::{CrossTransfer, CrossTransferRevert}; + /* // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw-hub-bnusd"; @@ -44,18 +45,24 @@ pub fn instantiate( }), }; - TOKEN_INFO.save(deps.storage, &data)?; - - X_CALL.save(deps.storage, &msg.x_call)?; - let x_call = X_CALL.load(deps.storage)?; + let save_token = TOKEN_INFO.save(deps.storage, &data); + if save_token.is_err() { + return Err(ContractError::Std(save_token.err().unwrap())); + } + deps.api.addr_validate(&msg.x_call).expect("ContractError::InvalidToAddress"); + let xcall=X_CALL.save(deps.storage, &msg.x_call); + if xcall.is_err() { + return Err(ContractError::Std(xcall.err().unwrap())); + } + let _x_call = &msg.x_call; // xCallBTPAddress = ICallService::get_btp_address(&x_call, _env)?; let (nid, _) = BTPAddress::parse_btp_address(&"aaa")?; - let (hubNet, hubAddress) = BTPAddress::parse_network_address(&msg.hub_address)?; + let (hub_net, hub_address) = BTPAddress::parse_network_address(&msg.hub_address)?; X_CALL_BTP_ADDRESS.save(deps.storage, &"sss".to_string())?; NID.save(deps.storage, &nid.to_string())?; - HUB_ADDRESS.save(deps.storage, &hubAddress.to_string())?; - HUB_NET.save(deps.storage, &hubNet.to_string())?; + HUB_ADDRESS.save(deps.storage, &hub_address.to_string())?; + HUB_NET.save(deps.storage, &hub_net.to_string())?; OWNER.save(deps.storage, &_info.sender)?; Ok(Response::default()) } @@ -70,9 +77,9 @@ pub fn execute( use ExecuteMsg::*; match msg { Setup { - _xCall, - _hubAddress, - } => execute::setup(deps, _env, info, _xCall, _hubAddress), + _x_call, + _hub_address, + } => execute::setup(deps, _env, info, _x_call, _hub_address), HandleCallMessage { _from, _data } => { execute::handle_call_message(deps, _env, info, _from, _data) } @@ -81,12 +88,12 @@ pub fn execute( } XCrossTransfer { from, - crossTransferData, - } => execute::x_cross_transfer(deps, _env, info, from, crossTransferData), + cross_transfer_data, + } => execute::x_cross_transfer(deps, _env, info, from, cross_transfer_data), XCrossTransferRevert { from, - crossTransferRevertData, - } => execute::x_cross_transfer_revert(deps, _env, info, from, crossTransferRevertData), + cross_transfer_revert_data, + } => execute::x_cross_transfer_revert(deps, _env, info, from, cross_transfer_revert_data), } } @@ -95,25 +102,49 @@ pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { unimplemented!() } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { + match msg.id { + REPLY_MSG_SUCCESS => reply::reply_msg_success(deps, env, msg), + _ => Err(ContractError::InvalidReply), + } +} + +mod reply { + use super::*; + + pub fn reply_msg_success( + _deps: DepsMut, + _env: Env, + _msg: Reply, + ) -> Result { + Ok(Response::default()) + } +} + mod execute { + use cosmwasm_std::{to_binary, CosmosMsg, SubMsg}; + use super::*; pub fn setup( deps: DepsMut, _env: Env, _info: MessageInfo, - x_call: Addr, + x_call: String, hub_address: String, ) -> Result { + deps.api.addr_validate(&x_call).expect("ContractError::InvalidToAddress"); X_CALL.save(deps.storage, &x_call)?; + //Network address call remaining // xCallBTPAddress = ICallService::get_btp_address(&x_call, _env)?; let (nid, _) = BTPAddress::parse_btp_address(&"xCallBTPAddress")?; - let (hubNet, hubAddress) = BTPAddress::parse_network_address(&hub_address)?; + let (hub_net, hub_address) = BTPAddress::parse_network_address(&hub_address)?; X_CALL_BTP_ADDRESS.save(deps.storage, &"xCallBTPAddress".to_string())?; NID.save(deps.storage, &nid.to_string())?; - HUB_ADDRESS.save(deps.storage, &hubAddress.to_string())?; - HUB_NET.save(deps.storage, &hubNet.to_string())?; + HUB_ADDRESS.save(deps.storage, &hub_address.to_string())?; + HUB_NET.save(deps.storage, &hub_net.to_string())?; OWNER.save(deps.storage, &_info.sender)?; Ok(Response::default()) } @@ -122,27 +153,40 @@ mod execute { deps: DepsMut, _env: Env, info: MessageInfo, - from: Addr, + from: String, data: Vec, ) -> Result { - // let mut data = RLPItem::new(data.len(),data.as_ptr()).to_list(); - // let method: String = String::from(data[0].to_bytes()?); - - // match method.as_str() { - // "xCrossTransfer" => { - // let cross_transfer_data: Types::CrossTransfer = - // RLPDecodeStruct::decode_cross_transfer(&data)?; - // x_cross_transfer(from, cross_transfer_data)?; - // } - // "xCrossTransferRevert" => { - // let cross_transfer_revert_data: Types::CrossTransferRevert = - // RLPDecodeStruct::decode_cross_transfer_revert(&data)?; - // x_cross_transfer_revert(from, cross_transfer_revert_data)?; - // } - // _ => { - // return Err(ContractError::InvalidMethod); - // } - // } + deps.api.addr_validate(&from).expect("ContractError::InvalidToAddress"); + let rlp: Rlp = Rlp::new(&data); + let data: Result, DecoderError> = rlp.as_list(); + match data { + Ok(decoded_data) => { + let method = &decoded_data[0]; + + match method.as_str() { + "xCrossTransfer" => { + let cross_transfer_data: CrossTransfer = + rlpdecode_struct::decode_cross_transfer(&decoded_data); + x_cross_transfer(deps, _env, info, from, cross_transfer_data)?; + } + "xCrossTransferRevert" => { + let cross_transfer_revert_data: CrossTransferRevert = + rlpdecode_struct::decode_cross_transfer_revert(&decoded_data); + x_cross_transfer_revert( + deps, + _env, + info, + from, + cross_transfer_revert_data, + )?; + } + _ => { + return Err(ContractError::InvalidMethod); + } + } + } + Err(_error) => return Err(ContractError::InvalidData), + } Ok(Response::default()) } @@ -151,52 +195,70 @@ mod execute { deps: DepsMut, env: Env, info: MessageInfo, - to: Addr, + to: String, amount: u128, data: Bytes, ) -> Result { - execute_burn(deps, env, info.clone(), amount.into()); - let mut nid = NID.load(deps.storage)?; - let mut hub_net: String = HUB_NET.load(deps.storage)?; - let mut hub_address: String = HUB_ADDRESS.load(deps.storage)?; + use super::*; + deps.api.addr_validate(&to).expect("ContractError::InvalidToAddress"); + let funds = info.funds.clone(); + let nid = NID.load(deps.storage)?; + let hub_net: String = HUB_NET.load(deps.storage)?; + let hub_address: String = HUB_ADDRESS.load(deps.storage)?; let from = BTPAddress::btp_address(&nid, &info.sender.to_string()); - let call_data = Types::CrossTransfer { + let _call_data = CrossTransfer { from: from.clone(), to: to.to_string().clone(), value: amount.clone(), data: data.to_vec(), }; - let rollback_data = Types::CrossTransferRevert { + let _rollback_data = CrossTransferRevert { from, value: amount, }; - let hub_btp_address = BTPAddress::btp_address(&hub_net, &hub_address); - // let call_message = CallMessage { - // address: hub_btp_address, - // method: call_data.encode_cross_transfer_message()?, - // revert_data: Some(rollback_data.encode_cross_transfer_revert_message()?), - // value: info.funds, - // }; + let _hub_btp_address = BTPAddress::btp_address(&hub_net, &hub_address); - // let res: Response = deps.querier.query(&call_message.into())?; + let call_message = XCallMsg::SendCallMessage { + to: _hub_btp_address, + data: _call_data.encode_cross_transfer_message(), + rollback: Some(_rollback_data.encode_cross_transfer_revert_message()), + }; - Ok(Response::default()) + let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { + contract_addr: X_CALL.load(deps.storage).unwrap(), + msg: to_binary(&call_message)?, + funds, + }); + let sub_message = SubMsg::reply_always(wasm_execute_message, REPLY_MSG_SUCCESS); + let _result = execute_burn(deps, env, info, amount.into()); + match _result { + Ok(resp) => { + print!("this is {:?}", resp) + } + Err(_error) => { + return Err(ContractError::MintError); + } + } + Ok(Response::new() + .add_submessage(sub_message) + .add_attribute("method", "cross_transfer")) } pub fn x_cross_transfer( deps: DepsMut, env: Env, info: MessageInfo, - from: Addr, - cross_transfer_data: Types::CrossTransfer, + from: String, + cross_transfer_data: CrossTransfer, ) -> Result { - let mut nid = NID.load(deps.storage)?; - let mut hub_net: String = HUB_NET.load(deps.storage)?; - let mut hub_address: String = HUB_ADDRESS.load(deps.storage)?; + deps.api.addr_validate(&from).expect("ContractError::InvalidToAddress"); + let nid = NID.load(deps.storage)?; + let hub_net: String = HUB_NET.load(deps.storage)?; + let hub_address: String = HUB_ADDRESS.load(deps.storage)?; let btp_address = BTPAddress::btp_address(&hub_net, &hub_address); @@ -209,15 +271,11 @@ mod execute { return Err(ContractError::WrongNetwork); } - let to = ParseAddress::parse_address(&account, "Invalid to Address")?; + let _to = deps.api.addr_validate(&account).expect("ContractError::InvalidToAddress"); - let res = execute_mint(deps, env, info, to, cross_transfer_data.value.into()) + let res = execute_mint(deps, env, info, account.to_string(), cross_transfer_data.value.into()) .expect("Fail to mint"); - // let res = _mint(deps, to, cross_transfer_data.value)?; - - // TODO Emit log - Ok(res) } @@ -225,11 +283,12 @@ mod execute { deps: DepsMut, env: Env, info: MessageInfo, - from: Addr, - cross_transfer_revert_data: Types::CrossTransferRevert, + from: String, + cross_transfer_revert_data: CrossTransferRevert, ) -> Result { - let mut nid = NID.load(deps.storage)?; - let mut x_call_btp_address = X_CALL_BTP_ADDRESS.load(deps.storage)?; + deps.api.addr_validate(&from).expect("ContractError::InvalidToAddress"); + let nid = NID.load(deps.storage)?; + let x_call_btp_address = X_CALL_BTP_ADDRESS.load(deps.storage)?; if from != x_call_btp_address { return Err(ContractError::OnlyCallService); @@ -240,14 +299,71 @@ mod execute { return Err(ContractError::InvalidBTPAddress); } - let to = ParseAddress::parse_address(&account, "Invalid to Address")?; + let _to = deps.api.addr_validate(&account).expect("ContractError::InvalidToAddress"); + - let res = execute_mint(deps, env, info, to, cross_transfer_revert_data.value.into()) + let res = execute_mint(deps, env, info, account.to_string(), cross_transfer_revert_data.value.into()) .expect("Fail to mint"); Ok(res) } } +mod rlpdecode_struct { + use super::*; + pub fn decode_cross_transfer(ls: &Vec) -> CrossTransfer { + CrossTransfer { + from: ls[1].clone(), + to: ls[2].clone(), + value: ls[3].parse::().unwrap_or_default(), + data: ls[4].clone().into(), + } + } + + pub fn decode_cross_transfer_revert(ls: &Vec) -> CrossTransferRevert { + CrossTransferRevert { + from: ls[1].clone().into(), + value: ls[2].parse::().unwrap_or_default(), + } + } +} #[cfg(test)] -mod tests {} +mod tests { + use common::rlp::{DecoderError, Rlp, RlpStream}; + + #[test] + fn test() { + let bytes: Vec = [ + 248, 133, 142, 120, 67, 114, 111, 115, 115, 84, 114, 97, 110, 115, 102, 101, 114, 184, + 57, 98, 116, 112, 58, 47, 47, 48, 120, 51, 56, 46, 98, 115, 99, 47, 48, 120, 48, 51, + 52, 65, 97, 68, 69, 56, 54, 66, 70, 52, 48, 50, 70, 48, 50, 51, 65, 97, 49, 55, 69, 53, + 55, 50, 53, 102, 65, 66, 67, 52, 97, 98, 57, 69, 57, 55, 57, 56, 184, 56, 98, 116, 112, + 58, 47, 47, 48, 120, 49, 46, 105, 99, 111, 110, 47, 48, 120, 48, 51, 65, 97, 68, 69, + 56, 54, 66, 70, 52, 48, 50, 70, 48, 50, 51, 65, 97, 49, 55, 69, 53, 55, 50, 53, 102, + 65, 66, 67, 52, 97, 98, 57, 69, 57, 55, 57, 56, 100, 132, 100, 97, 116, 97, + ] + .into(); + let rlp: Rlp = Rlp::new(&bytes); + let ddata: Result, DecoderError> = rlp.as_list(); + + let mut _decoded_data: Vec = Vec::new(); + + print!("this is {:?} {:?} {:?}", bytes, ddata, rlp) + } + #[test] + + fn encodetest() { + let method = "xCrossTransfer"; + let val: u32 = 100; + + let mut calldata = RlpStream::new_list(4); + calldata.append(&method.to_string()); + calldata.append(&"btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); + calldata.append(&"btp://0x1.icon/0x03AaDE86BF402F023Aa17E5725fABC4ab9E9798"); + calldata.append(&val); + calldata.append(&"data"); + + let encoded = calldata.as_raw().to_vec(); + print!("this is {:?}", encoded) + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/src/error.rs b/contracts/token-contracts/cw-hub-bnusd/src/error.rs index 4cf6d90..a670d6f 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/error.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/error.rs @@ -22,6 +22,13 @@ pub enum ContractError { OnlyHub, #[error("Invalid Method")] InvalidMethod, + #[error("Invalid Reply")] + InvalidReply, + #[error("Invalid Reply Data")] + MintError, + #[error("Invalid Reply Data")] + InvalidData, + // Add any other custom errors you like here. // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs index 233dbf5..4992f5a 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs @@ -3,5 +3,7 @@ mod error; pub mod helpers; pub mod msg; pub mod state; +pub mod constants; +pub mod types; pub use crate::error::ContractError; diff --git a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs b/contracts/token-contracts/cw-hub-bnusd/src/msg.rs index def41bd..279a6a2 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/msg.rs @@ -1,27 +1,11 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::Addr; -pub mod Types { - use super::*; +use crate::types::Types; - #[cw_serde] - pub struct CrossTransfer { - pub from: String, - pub to: String, - pub value: u128, - pub data: Vec, - } - - #[cw_serde] - pub struct CrossTransferRevert { - pub from: String, - pub value: u128, - } -} #[cw_serde] pub struct InstantiateMsg { - pub x_call: Addr, + pub x_call: String, pub hub_address: String, pub name: String, pub symbol: String, @@ -30,15 +14,38 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { - Setup {_xCall: Addr, _hubAddress: String}, - HandleCallMessage {_from: Addr, _data: Vec}, - CrossTransfer {to : Addr, amount: u128, data: Vec}, - XCrossTransfer {from: Addr, crossTransferData: Types::CrossTransfer}, - XCrossTransferRevert {from: Addr, crossTransferRevertData: Types::CrossTransferRevert}, + Setup { + _x_call: String, + _hub_address: String, + }, + HandleCallMessage { + _from: String, + _data: Vec, + }, + CrossTransfer { + to: String, + amount: u128, + data: Vec, + }, + XCrossTransfer { + from: String, + cross_transfer_data: Types::CrossTransfer, + }, + XCrossTransferRevert { + from: String, + cross_transfer_revert_data: Types::CrossTransferRevert, + }, } #[cw_serde] #[derive(QueryResponses)] -pub enum QueryMsg { - +pub enum QueryMsg {} + +#[cw_serde] +pub enum XCallMsg { + SendCallMessage { + to: String, + data: Vec, + rollback: Option>, + }, } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/state.rs b/contracts/token-contracts/cw-hub-bnusd/src/state.rs index 603a9ae..7fa2b75 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/state.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/state.rs @@ -19,7 +19,7 @@ pub const OWNER: Item = Item::new("owner"); // const ZERO_ADDRESSES: &'static str = "0000000000000000000000000000000000000000"; -pub const X_CALL : Item = Item::new("xCall"); +pub const X_CALL : Item = Item::new("xCall"); pub const X_CALL_BTP_ADDRESS : Item = Item::new("xCallBTPAddress"); pub const NID : Item = Item::new("nid"); pub const HUB_ADDRESS : Item = Item::new("hubAddress"); diff --git a/contracts/token-contracts/cw-hub-bnusd/src/types.rs b/contracts/token-contracts/cw-hub-bnusd/src/types.rs new file mode 100644 index 0000000..6a0ff7d --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/src/types.rs @@ -0,0 +1,51 @@ +use common::rlp::{RlpStream}; +use cosmwasm_schema::{cw_serde}; + +pub mod Types { + use super::*; + + #[cw_serde] + pub struct CrossTransfer { + pub from: String, + pub to: String, + pub value: u128, + pub data: Vec, + } + + #[cw_serde] + pub struct CrossTransferRevert { + pub from: String, + pub value: u128, + } +} + +impl Types::CrossTransfer { + pub fn encode_cross_transfer_message(self) -> Vec { + let method = "xCrossTransfer"; + + let mut calldata = RlpStream::new_list(4); + calldata.append(&method.to_string()); + calldata.append(&self.from); + calldata.append(&self.to); + calldata.append(&self.value); + calldata.append(&self.data); + + let encoded = calldata.as_raw().to_vec(); + encoded + } +} + +impl Types::CrossTransferRevert { + pub fn encode_cross_transfer_revert_message(self) -> Vec { + let method = "xCrossTransferRevert"; + + let mut calldata = RlpStream::new_list(3); + calldata.append(&method.to_string()); + calldata.append(&self.from); + calldata.append(&self.value); + + let encoded = calldata.as_raw().to_vec(); + encoded + } +} + \ No newline at end of file diff --git a/libraries/rust/common/src/btpAddress.rs b/libraries/rust/common/src/btpAddress.rs index 2a01f63..a182c63 100644 --- a/libraries/rust/common/src/btpAddress.rs +++ b/libraries/rust/common/src/btpAddress.rs @@ -67,3 +67,28 @@ impl BTPAddress { format!("{:?}{}{:?}{}", BTPAddress::PREFIX, network, BTPAddress::DELIMITER, account) } } + +mod tests{ + #[test] + fn test_parse_btp_address() { + let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; + let (network, account) = super::BTPAddress::parse_btp_address(btp_address).unwrap(); + assert_eq!(network, "0x38.bsc"); + assert_eq!(account, "0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); + } + + #[test] + fn test_parse_network_address() { + let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; + let (network, account) = super::BTPAddress::parse_network_address(btp_address).unwrap(); + assert_eq!(network, "btp:"); + assert_eq!(account, "/0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); + } + + #[test] + fn test_network_address() { + let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; + let network = super::BTPAddress::network_address(btp_address).unwrap(); + assert_eq!(network, "0x38.bsc"); + } +} \ No newline at end of file diff --git a/libraries/rust/common/src/icallservice.rs b/libraries/rust/common/src/icallservice.rs deleted file mode 100644 index a61c2e1..0000000 --- a/libraries/rust/common/src/icallservice.rs +++ /dev/null @@ -1,23 +0,0 @@ -use cosmwasm_std::{ - Env, MessageInfo, Response, StdError, StdResult, Storage, -}; - -#[derive(Clone)] -pub struct ICallService {} - -impl ICallService { - pub fn get_btp_address(&self, _env: Env) -> StdResult { - Ok("BTP_ADDRESS".to_string()) - } - - pub fn send_call_message( - &self, - _env: Env, - _to: String, - _data: Vec, - _rollback: Vec, - ) -> StdResult { - - Ok(Response::default()) - } -} diff --git a/libraries/rust/common/src/lib.rs b/libraries/rust/common/src/lib.rs index 87616e6..fc8a6fd 100644 --- a/libraries/rust/common/src/lib.rs +++ b/libraries/rust/common/src/lib.rs @@ -1,7 +1,2 @@ pub mod rlp; -pub mod types; -pub mod icallservice; -pub mod strings; pub mod btpAddress; -pub mod parseAddress; -pub mod rlpdecode; \ No newline at end of file diff --git a/libraries/rust/common/src/parseAddress.rs b/libraries/rust/common/src/parseAddress.rs deleted file mode 100644 index b39a20f..0000000 --- a/libraries/rust/common/src/parseAddress.rs +++ /dev/null @@ -1,58 +0,0 @@ -pub mod ParseAddress{ -use cosmwasm_std::{StdError, StdResult}; - -pub fn parse_address(account: &str, revert_msg: &str) -> StdResult { - let account_bytes = account.as_bytes(); - - if account_bytes.len() != 42 || account_bytes[0] != b'0' || account_bytes[1] != b'x' { - return Err(StdError::generic_err(revert_msg)); - } - - let mut account_address_bytes: Vec = vec![0; 20]; - - for i in 0..40 { - let b = account_bytes[i + 2]; - - let is_valid_ascii = match b { - 48..=57 | 65..=70 | 97..=102 => true, - _ => false, - }; - - if !is_valid_ascii { - return Err(StdError::generic_err(revert_msg)); - } - - let ascii_offset = if b < 65 { - 48 // 0-9 - } else if b > 102 { - 87 // a-f - } else { - 55 // A-F - }; - - let nibble = if i % 2 == 0 { - b - ascii_offset - } else { - (account_address_bytes[(i - 1) / 2] << 4) + (b - ascii_offset) - }; - - if i % 2 == 1 { - account_address_bytes[(i - 1) / 2] = nibble; - } - } - - let packed = hex::encode(account_address_bytes); - let mut account_address: &str = ""; - - if account_address == "0000000000000000000000000000000000000000" { - for i in 2..account_bytes.len() { - if account_bytes[i] != b'0' { - return Err(StdError::generic_err(revert_msg)); - } - } - } - - Ok(account_address.to_string()) -} - -} \ No newline at end of file diff --git a/libraries/rust/common/src/rlpdecode.rs b/libraries/rust/common/src/rlpdecode.rs deleted file mode 100644 index bbdee83..0000000 --- a/libraries/rust/common/src/rlpdecode.rs +++ /dev/null @@ -1,106 +0,0 @@ -extern crate rlp; - -use crate::rlp::{DecoderError, Rlp, RlpStream}; - -mod constants { - pub const STRING_SHORT_START: u8 = 0x80; - pub const STRING_LONG_START: u8 = 0xb8; - pub const LIST_SHORT_START: u8 = 0xc0; - pub const LIST_LONG_START: u8 = 0xf8; - pub const WORD_SIZE: u8 = 32; -} - -#[derive(Debug)] -pub struct RLPItem { - len: usize, - mem_ptr: *const u8, -} - -impl RLPItem { - pub fn new(len: usize, mem_ptr: *const u8) -> RLPItem { - RLPItem { len, mem_ptr } - } - - pub fn to_list(&self) -> Result, DecoderError> { - if !self.is_list() { - return Err(DecoderError::RlpExpectedToBeList); - } - - let items = self.num_items(); - let mut result = Vec::with_capacity(items); - - let mut mem_ptr = self.mem_ptr as usize + self.payload_offset(); - let mut data_len: usize; - for _ in 0..items { - data_len = self.item_length(mem_ptr as *const u8)?; - result.push(RLPItem::new(data_len, mem_ptr as *const u8)); - mem_ptr = mem_ptr + data_len; - } - - Ok(result) - } - - fn is_list(&self) -> bool { - if self.len == 0 { - return false; - } - - let byte0 = unsafe { *self.mem_ptr }; - if byte0 < constants::LIST_SHORT_START { - return false; - } - - true - } - - fn item_length(&self, ptr: *const u8) -> Result { - let byte0 = unsafe { *ptr }; - match byte0 { - 0x80..=0xb7 => Ok(1), - 0xb8..=0xbf => { - let len_bytes = byte0 - constants::LIST_LONG_START + 1; - let length = self.decode_length(unsafe { ptr.add(1) }, len_bytes.into())?; - Ok((1 + len_bytes + (length as u8)).into()) - } - _ => Err(DecoderError::RlpInvalidIndirection), - } - } - - fn payload_offset(&self) -> usize { - if self.len == 0 { - return 0; - } - - let byte0 = unsafe { *self.mem_ptr }; - match byte0 { - 0x80..=0xb7 => 0, - 0xb8..=0xbf => (byte0 - constants::LIST_LONG_START + 1).into(), - _ => 0, - } - } - - fn decode_length(&self, ptr: *const u8, len: usize) -> Result { - let mut length = 0; - for i in 0..len { - let byte = unsafe { *ptr.add(i) }; - length = length * 256 + byte as usize; - } - - Ok(length) - } - - fn num_items(&self) -> usize { - if self.len == 0 { - return 0; - } - - let byte0 = unsafe { *self.mem_ptr }; - match byte0 { - 0x80..=0xb7 => (byte0 - constants::LIST_SHORT_START).into(), - 0xb8..=0xbf => self - .decode_length(unsafe { self.mem_ptr.add(1) }, (byte0 - constants::LIST_LONG_START + 1).into()) - .unwrap(), - _ => 0, - } - } -} diff --git a/libraries/rust/common/src/strings.rs b/libraries/rust/common/src/strings.rs deleted file mode 100644 index 58263b7..0000000 --- a/libraries/rust/common/src/strings.rs +++ /dev/null @@ -1,45 +0,0 @@ -pub mod strings { - pub fn bytes_to_hex(buffer: &[u8]) -> String { - if buffer.is_empty() { - return "0x".to_string(); - } - - let converted: Vec = buffer - .iter() - .flat_map(|byte| vec![byte / 16, byte % 16]) - .collect(); - - let base: Vec = b"0123456789abcdef".to_vec(); - - let hex_string: String = converted - .iter() - .map(|byte| base[*byte as usize] as char) - .collect(); - - format!("0x{}", hex_string) - } - - pub fn concat(base: &str, value: &str) -> String { - format!("{}{}", base, value) - } - - pub fn index_of(base: &str, value: &str) -> Option { - base.find(value) - } - - pub fn length(base: &str) -> usize { - base.len() - } - - pub fn split(base: &str, value: &str) -> Vec { - base.split(value).map(|s| s.to_string()).collect() - } - - pub fn compare_to(base: &str, value: &str) -> bool { - base == value - } - - pub fn lower(base: &str) -> String { - base.to_lowercase() - } -} diff --git a/libraries/rust/common/src/types.rs b/libraries/rust/common/src/types.rs deleted file mode 100644 index 69a9812..0000000 --- a/libraries/rust/common/src/types.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub mod Types { - pub struct CrossTransfer { - pub from: String, - pub to: String, - pub value: u128, - pub data: Vec, - } - - pub struct CrossTransferRevert { - pub from: String, - pub value: u128, - } -} From 3cf7bf28f510dcf8185a84c056949175e51b6e4f Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Tue, 13 Jun 2023 13:24:53 +0545 Subject: [PATCH 07/37] style: Format code and assign constants to variable methods --- contracts/token-contracts/cw-hub-bnusd/src/constants.rs | 4 +++- contracts/token-contracts/cw-hub-bnusd/src/contract.rs | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/token-contracts/cw-hub-bnusd/src/constants.rs b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs index 73a65f9..aad31f1 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/constants.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs @@ -1,3 +1,5 @@ //Constants for Reply messages -pub const REPLY_MSG_SUCCESS: u64 = 1; \ No newline at end of file +pub const REPLY_MSG_SUCCESS: u64 = 1; +pub const X_CROSS_TRANSFER: &str = "XCrossTransfer"; +pub const X_CROSS_TRANSFER_REVERT: &str = "XCrossTransferRevert"; diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index d9a2071..78d884b 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -4,7 +4,7 @@ use cosmwasm_std::{ Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, Uint128, }; // use cw2::set_contract_version; -use crate::constants::REPLY_MSG_SUCCESS; +use crate::constants::{REPLY_MSG_SUCCESS, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT}; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, XCallMsg}; use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; @@ -164,12 +164,12 @@ mod execute { let method = &decoded_data[0]; match method.as_str() { - "xCrossTransfer" => { + X_CROSS_TRANSFER => { let cross_transfer_data: CrossTransfer = rlpdecode_struct::decode_cross_transfer(&decoded_data); x_cross_transfer(deps, _env, info, from, cross_transfer_data)?; } - "xCrossTransferRevert" => { + X_CROSS_TRANSFER_REVERT => { let cross_transfer_revert_data: CrossTransferRevert = rlpdecode_struct::decode_cross_transfer_revert(&decoded_data); x_cross_transfer_revert( From ac44f457cfddb8a7d6de318dcf6945ec14c91569 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Mon, 19 Jun 2023 22:15:17 +0545 Subject: [PATCH 08/37] style: parseNetworkAddress shifted to cw-common --- contracts/cw-common/Cargo.toml | 16 ++++ contracts/cw-common/src/lib.rs | 1 + contracts/cw-common/src/networkAddress.rs | 94 +++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 contracts/cw-common/Cargo.toml create mode 100644 contracts/cw-common/src/lib.rs create mode 100644 contracts/cw-common/src/networkAddress.rs diff --git a/contracts/cw-common/Cargo.toml b/contracts/cw-common/Cargo.toml new file mode 100644 index 0000000..dbb526e --- /dev/null +++ b/contracts/cw-common/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "cw-common" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bytes = { version = "1.4.0", default-features = false } +rlp-derive = { version = "0.1.0", default-features = false } +cw20 = { version = "1.0.1", default-features = false } +rustc-hex = { version = "2.1.0", default-features = false } +serde = { version = "1.0.156", default-features = false,features = ["derive"] } +hex ={ version = "0.4.3", default-features = false } +cosmwasm-std = { version = "1.2.5", default-features = false } +rlp = { version = "0.5.2", default-features = false } diff --git a/contracts/cw-common/src/lib.rs b/contracts/cw-common/src/lib.rs new file mode 100644 index 0000000..5750a01 --- /dev/null +++ b/contracts/cw-common/src/lib.rs @@ -0,0 +1 @@ +pub mod networkAddress; diff --git a/contracts/cw-common/src/networkAddress.rs b/contracts/cw-common/src/networkAddress.rs new file mode 100644 index 0000000..c2fd31d --- /dev/null +++ b/contracts/cw-common/src/networkAddress.rs @@ -0,0 +1,94 @@ +use cosmwasm_std::{StdError, StdResult}; + +pub struct NetworkAddress; + +impl NetworkAddress { + const PREFIX: &'static [u8] = b"btp://"; + const REVERT: &'static str = "invalidNetworkAddress"; + const DELIMITER: &'static [u8] = b"/"; + + pub fn parse_btp_address(_str: &str) -> StdResult<(&str, &str)> { + let offset = NetworkAddress::_validate(_str)?; + let network_address = &_str[6.._str.len()]; + let account_address = &_str[offset + 1..]; + Ok((network_address, account_address)) + } + + pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { + let offset = NetworkAddress::_validate_network(_str)?; + let network_address = &_str[0..offset]; + let account_address = &_str[offset + 1..]; + Ok((network_address, account_address)) + } + + pub fn network_address(_str: &str) -> StdResult<&str> { + let offset = NetworkAddress::_validate(_str)?; + let network_address = &_str[6..offset]; + Ok(network_address) + } + + fn _validate(_str: &str) -> StdResult { + let bytes = _str.as_bytes(); + for (i, &byte) in bytes.iter().enumerate() { + if i < 6 { + if byte != NetworkAddress::PREFIX[i] { + return Err(StdError::generic_err(NetworkAddress::REVERT)); + } + } else if byte == NetworkAddress::DELIMITER[0] { + if i > 6 && i < (bytes.len() - 1) { + return Ok(i); + } else { + return Err(StdError::generic_err(NetworkAddress::REVERT)); + } + } + } + Err(StdError::generic_err(NetworkAddress::REVERT)) + } + + fn _validate_network(_str: &str) -> StdResult { + let bytes = _str.as_bytes(); + for (i, &byte) in bytes.iter().enumerate() { + if byte == NetworkAddress::DELIMITER[0] { + if i < (bytes.len() - 1) { + return Ok(i); + } else { + return Err(StdError::generic_err(NetworkAddress::REVERT)); + } + } + } + Err(StdError::generic_err(NetworkAddress::REVERT)) + } + + fn _slice(_str: &str, from: usize, to: usize) -> &str { + &_str[from..to] + } + + pub fn btp_address(network: &str, account: &str) -> String { + format!("{:?}{}{:?}{}", NetworkAddress::PREFIX, network, NetworkAddress::DELIMITER, account) + } +} + +mod tests{ + #[test] + fn test_parse_btp_address() { + let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; + let (network, account) = super::NetworkAddress::parse_btp_address(btp_address).unwrap(); + assert_eq!(network, "0x38.bsc"); + assert_eq!(account, "0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); + } + + #[test] + fn test_parse_network_address() { + let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; + let (network, account) = super::NetworkAddress::parse_network_address(btp_address).unwrap(); + assert_eq!(network, "btp:"); + assert_eq!(account, "/0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); + } + + #[test] + fn test_network_address() { + let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; + let network = super::NetworkAddress::network_address(btp_address).unwrap(); + assert_eq!(network, "0x38.bsc"); + } +} \ No newline at end of file From 9d7307ac7c0ccabfb7cca7f8aeb3145224073589 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Mon, 19 Jun 2023 22:15:43 +0545 Subject: [PATCH 09/37] style: parseNetworkAddress shifted to cw-common --- Cargo.lock | 15 + contracts/cw20-base/.cargo/config | 6 - contracts/cw20-base/Cargo.toml | 33 - contracts/cw20-base/README.md | 48 - contracts/cw20-base/src/allowances.rs | 879 ------- contracts/cw20-base/src/bin/schema.rs | 11 - contracts/cw20-base/src/contract.rs | 2236 ----------------- contracts/cw20-base/src/enumerable.rs | 319 --- contracts/cw20-base/src/error.rs | 43 - contracts/cw20-base/src/lib.rs | 24 - contracts/cw20-base/src/msg.rs | 175 -- contracts/cw20-base/src/state.rs | 36 - .../token-contracts/cw-hub-bnusd/Cargo.toml | 1 + .../cw-hub-bnusd/src/contract.rs | 57 +- .../token-contracts/cw-hub-bnusd/src/msg.rs | 14 + .../token-contracts/cw-hub-bnusd/src/state.rs | 2 + libraries/rust/common/src/btpAddress.rs | 94 - libraries/rust/common/src/lib.rs | 3 +- 18 files changed, 74 insertions(+), 3922 deletions(-) delete mode 100644 contracts/cw20-base/.cargo/config delete mode 100644 contracts/cw20-base/Cargo.toml delete mode 100644 contracts/cw20-base/README.md delete mode 100644 contracts/cw20-base/src/allowances.rs delete mode 100644 contracts/cw20-base/src/bin/schema.rs delete mode 100644 contracts/cw20-base/src/contract.rs delete mode 100644 contracts/cw20-base/src/enumerable.rs delete mode 100644 contracts/cw20-base/src/error.rs delete mode 100644 contracts/cw20-base/src/lib.rs delete mode 100644 contracts/cw20-base/src/msg.rs delete mode 100644 contracts/cw20-base/src/state.rs delete mode 100644 libraries/rust/common/src/btpAddress.rs diff --git a/Cargo.lock b/Cargo.lock index 38d90d4..3481e17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -219,6 +219,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cw-common" +version = "0.1.0" +dependencies = [ + "bytes", + "cosmwasm-std", + "cw20", + "hex", + "rlp", + "rlp-derive", + "rustc-hex", + "serde", +] + [[package]] name = "cw-hub-bnusd" version = "0.1.0" @@ -228,6 +242,7 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", + "cw-common", "cw-multi-test", "cw-storage-plus", "cw2", diff --git a/contracts/cw20-base/.cargo/config b/contracts/cw20-base/.cargo/config deleted file mode 100644 index f517478..0000000 --- a/contracts/cw20-base/.cargo/config +++ /dev/null @@ -1,6 +0,0 @@ -[alias] -wasm = "build --release --lib --target wasm32-unknown-unknown" -wasm-debug = "build --lib --target wasm32-unknown-unknown" -unit-test = "test --lib" -integration-test = "test --test integration" -schema = "run --bin schema" diff --git a/contracts/cw20-base/Cargo.toml b/contracts/cw20-base/Cargo.toml deleted file mode 100644 index 8cf5e08..0000000 --- a/contracts/cw20-base/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "cw20-base" -version = "1.0.1" -authors = ["Ethan Frey "] -edition = "2021" -description = "Basic implementation of a CosmWasm-20 compliant token" -license = "Apache-2.0" -repository = "https://github.com/CosmWasm/cw-plus" -homepage = "https://cosmwasm.com" -documentation = "https://docs.cosmwasm.com" - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -backtraces = ["cosmwasm-std/backtraces"] -# use library feature to disable all instantiate/execute/query exports -library = [] - -[dependencies] -cosmwasm-schema = { version = "1.1.0" } -cw-utils = "1.0.1" -cw2 = { version = "1.0.1" } -cw20 = { version = "1.0.1" } -cw-storage-plus = "1.0.1" -cosmwasm-std = { version = "1.1.0" } -schemars = "0.8.1" -semver = "1" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } -thiserror = { version = "1.0.23" } - -[dev-dependencies] -cw-multi-test = "0.16.1" diff --git a/contracts/cw20-base/README.md b/contracts/cw20-base/README.md deleted file mode 100644 index 01db9e0..0000000 --- a/contracts/cw20-base/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# CW20 Basic - -This is a basic implementation of a cw20 contract. It implements -the [CW20 spec](../../packages/cw20/README.md) and is designed to -be deployed as is, or imported into other contracts to easily build -cw20-compatible tokens with custom logic. - -Implements: - -- [x] CW20 Base -- [x] Mintable extension -- [x] Allowances extension - -## Running this contract - -You will need Rust 1.44.1+ with `wasm32-unknown-unknown` target installed. - -You can run unit tests on this via: - -`cargo test` - -Once you are happy with the content, you can compile it to wasm via: - -``` -RUSTFLAGS='-C link-arg=-s' cargo wasm -cp ../../target/wasm32-unknown-unknown/release/cw20_base.wasm . -ls -l cw20_base.wasm -sha256sum cw20_base.wasm -``` - -Or for a production-ready (optimized) build, run a build command in the -the repository root: https://github.com/CosmWasm/cw-plus#compiling. - -## Importing this contract - -You can also import much of the logic of this contract to build another -ERC20-contract, such as a bonding curve, overiding or extending what you -need. - -Basically, you just need to write your handle function and import -`cw20_base::contract::handle_transfer`, etc and dispatch to them. -This allows you to use custom `ExecuteMsg` and `QueryMsg` with your additional -calls, but then use the underlying implementation for the standard cw20 -messages you want to support. The same with `QueryMsg`. You *could* reuse `instantiate` -as it, but it is likely you will want to change it. And it is rather simple. - -Look at [`cw20-staking`](https://github.com/CosmWasm/cw-tokens/tree/main/contracts/cw20-staking) for an example of how to "inherit" -all this token functionality and combine it with custom logic. diff --git a/contracts/cw20-base/src/allowances.rs b/contracts/cw20-base/src/allowances.rs deleted file mode 100644 index 38b36da..0000000 --- a/contracts/cw20-base/src/allowances.rs +++ /dev/null @@ -1,879 +0,0 @@ -use cosmwasm_std::{ - attr, Addr, Binary, BlockInfo, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, - Storage, Uint128, -}; -use cw20::{AllowanceResponse, Cw20ReceiveMsg, Expiration}; - -use crate::error::ContractError; -use crate::state::{ALLOWANCES, ALLOWANCES_SPENDER, BALANCES, TOKEN_INFO}; - -pub fn execute_increase_allowance( - deps: DepsMut, - env: Env, - info: MessageInfo, - spender: String, - amount: Uint128, - expires: Option, -) -> Result { - let spender_addr = deps.api.addr_validate(&spender)?; - if spender_addr == info.sender { - return Err(ContractError::CannotSetOwnAccount {}); - } - - let update_fn = |allow: Option| -> Result<_, _> { - let mut val = allow.unwrap_or_default(); - if let Some(exp) = expires { - if exp.is_expired(&env.block) { - return Err(ContractError::InvalidExpiration {}); - } - val.expires = exp; - } - val.allowance += amount; - Ok(val) - }; - ALLOWANCES.update(deps.storage, (&info.sender, &spender_addr), update_fn)?; - ALLOWANCES_SPENDER.update(deps.storage, (&spender_addr, &info.sender), update_fn)?; - - let res = Response::new().add_attributes(vec![ - attr("action", "increase_allowance"), - attr("owner", info.sender), - attr("spender", spender), - attr("amount", amount), - ]); - Ok(res) -} - -pub fn execute_decrease_allowance( - deps: DepsMut, - env: Env, - info: MessageInfo, - spender: String, - amount: Uint128, - expires: Option, -) -> Result { - let spender_addr = deps.api.addr_validate(&spender)?; - if spender_addr == info.sender { - return Err(ContractError::CannotSetOwnAccount {}); - } - - let key = (&info.sender, &spender_addr); - - fn reverse<'a>(t: (&'a Addr, &'a Addr)) -> (&'a Addr, &'a Addr) { - (t.1, t.0) - } - - // load value and delete if it hits 0, or update otherwise - let mut allowance = ALLOWANCES.load(deps.storage, key)?; - if amount < allowance.allowance { - // update the new amount - allowance.allowance = allowance - .allowance - .checked_sub(amount) - .map_err(StdError::overflow)?; - if let Some(exp) = expires { - if exp.is_expired(&env.block) { - return Err(ContractError::InvalidExpiration {}); - } - allowance.expires = exp; - } - ALLOWANCES.save(deps.storage, key, &allowance)?; - ALLOWANCES_SPENDER.save(deps.storage, reverse(key), &allowance)?; - } else { - ALLOWANCES.remove(deps.storage, key); - ALLOWANCES_SPENDER.remove(deps.storage, reverse(key)); - } - - let res = Response::new().add_attributes(vec![ - attr("action", "decrease_allowance"), - attr("owner", info.sender), - attr("spender", spender), - attr("amount", amount), - ]); - Ok(res) -} - -// this can be used to update a lower allowance - call bucket.update with proper keys -pub fn deduct_allowance( - storage: &mut dyn Storage, - owner: &Addr, - spender: &Addr, - block: &BlockInfo, - amount: Uint128, -) -> Result { - let update_fn = |current: Option| -> _ { - match current { - Some(mut a) => { - if a.expires.is_expired(block) { - Err(ContractError::Expired {}) - } else { - // deduct the allowance if enough - a.allowance = a - .allowance - .checked_sub(amount) - .map_err(StdError::overflow)?; - Ok(a) - } - } - None => Err(ContractError::NoAllowance {}), - } - }; - ALLOWANCES.update(storage, (owner, spender), update_fn)?; - ALLOWANCES_SPENDER.update(storage, (spender, owner), update_fn) -} - -pub fn execute_transfer_from( - deps: DepsMut, - env: Env, - info: MessageInfo, - owner: String, - recipient: String, - amount: Uint128, -) -> Result { - let rcpt_addr = deps.api.addr_validate(&recipient)?; - let owner_addr = deps.api.addr_validate(&owner)?; - - // deduct allowance before doing anything else have enough allowance - deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?; - - BALANCES.update( - deps.storage, - &owner_addr, - |balance: Option| -> StdResult<_> { - Ok(balance.unwrap_or_default().checked_sub(amount)?) - }, - )?; - BALANCES.update( - deps.storage, - &rcpt_addr, - |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, - )?; - - let res = Response::new().add_attributes(vec![ - attr("action", "transfer_from"), - attr("from", owner), - attr("to", recipient), - attr("by", info.sender), - attr("amount", amount), - ]); - Ok(res) -} - -pub fn execute_burn_from( - deps: DepsMut, - - env: Env, - info: MessageInfo, - owner: String, - amount: Uint128, -) -> Result { - let owner_addr = deps.api.addr_validate(&owner)?; - - // deduct allowance before doing anything else have enough allowance - deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?; - - // lower balance - BALANCES.update( - deps.storage, - &owner_addr, - |balance: Option| -> StdResult<_> { - Ok(balance.unwrap_or_default().checked_sub(amount)?) - }, - )?; - // reduce total_supply - TOKEN_INFO.update(deps.storage, |mut meta| -> StdResult<_> { - meta.total_supply = meta.total_supply.checked_sub(amount)?; - Ok(meta) - })?; - - let res = Response::new().add_attributes(vec![ - attr("action", "burn_from"), - attr("from", owner), - attr("by", info.sender), - attr("amount", amount), - ]); - Ok(res) -} - -pub fn execute_send_from( - deps: DepsMut, - env: Env, - info: MessageInfo, - owner: String, - contract: String, - amount: Uint128, - msg: Binary, -) -> Result { - let rcpt_addr = deps.api.addr_validate(&contract)?; - let owner_addr = deps.api.addr_validate(&owner)?; - - // deduct allowance before doing anything else have enough allowance - deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?; - - // move the tokens to the contract - BALANCES.update( - deps.storage, - &owner_addr, - |balance: Option| -> StdResult<_> { - Ok(balance.unwrap_or_default().checked_sub(amount)?) - }, - )?; - BALANCES.update( - deps.storage, - &rcpt_addr, - |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, - )?; - - let attrs = vec![ - attr("action", "send_from"), - attr("from", &owner), - attr("to", &contract), - attr("by", &info.sender), - attr("amount", amount), - ]; - - // create a send message - let msg = Cw20ReceiveMsg { - sender: info.sender.into(), - amount, - msg, - } - .into_cosmos_msg(contract)?; - - let res = Response::new().add_message(msg).add_attributes(attrs); - Ok(res) -} - -pub fn query_allowance(deps: Deps, owner: String, spender: String) -> StdResult { - let owner_addr = deps.api.addr_validate(&owner)?; - let spender_addr = deps.api.addr_validate(&spender)?; - let allowance = ALLOWANCES - .may_load(deps.storage, (&owner_addr, &spender_addr))? - .unwrap_or_default(); - Ok(allowance) -} - -#[cfg(test)] -mod tests { - use super::*; - - use cosmwasm_std::testing::{mock_dependencies_with_balance, mock_env, mock_info}; - use cosmwasm_std::{coins, CosmosMsg, SubMsg, Timestamp, WasmMsg}; - use cw20::{Cw20Coin, TokenInfoResponse}; - - use crate::contract::{execute, instantiate, query_balance, query_token_info}; - use crate::msg::{ExecuteMsg, InstantiateMsg}; - - fn get_balance>(deps: Deps, address: T) -> Uint128 { - query_balance(deps, address.into()).unwrap().balance - } - - // this will set up the instantiation for other tests - fn do_instantiate>( - mut deps: DepsMut, - addr: T, - amount: Uint128, - ) -> TokenInfoResponse { - let instantiate_msg = InstantiateMsg { - name: "Auto Gen".to_string(), - symbol: "AUTO".to_string(), - decimals: 3, - initial_balances: vec![Cw20Coin { - address: addr.into(), - amount, - }], - mint: None, - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - instantiate(deps.branch(), env, info, instantiate_msg).unwrap(); - query_token_info(deps.as_ref()).unwrap() - } - - #[test] - fn increase_decrease_allowances() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - - let owner = String::from("addr0001"); - let spender = String::from("addr0002"); - let info = mock_info(owner.as_ref(), &[]); - let env = mock_env(); - do_instantiate(deps.as_mut(), owner.clone(), Uint128::new(12340000)); - - // no allowance to start - let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); - assert_eq!(allowance, AllowanceResponse::default()); - - // set allowance with height expiration - let allow1 = Uint128::new(7777); - let expires = Expiration::AtHeight(123_456); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow1, - expires: Some(expires), - }; - execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - - // ensure it looks good - let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); - assert_eq!( - allowance, - AllowanceResponse { - allowance: allow1, - expires - } - ); - - // decrease it a bit with no expire set - stays the same - let lower = Uint128::new(4444); - let allow2 = allow1.checked_sub(lower).unwrap(); - let msg = ExecuteMsg::DecreaseAllowance { - spender: spender.clone(), - amount: lower, - expires: None, - }; - execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); - assert_eq!( - allowance, - AllowanceResponse { - allowance: allow2, - expires - } - ); - - // increase it some more and override the expires - let raise = Uint128::new(87654); - let allow3 = allow2 + raise; - let new_expire = Expiration::AtTime(Timestamp::from_seconds(8888888888)); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: raise, - expires: Some(new_expire), - }; - execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); - assert_eq!( - allowance, - AllowanceResponse { - allowance: allow3, - expires: new_expire - } - ); - - // decrease it below 0 - let msg = ExecuteMsg::DecreaseAllowance { - spender: spender.clone(), - amount: Uint128::new(99988647623876347), - expires: None, - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - let allowance = query_allowance(deps.as_ref(), owner, spender).unwrap(); - assert_eq!(allowance, AllowanceResponse::default()); - } - - #[test] - fn allowances_independent() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - - let owner = String::from("addr0001"); - let spender = String::from("addr0002"); - let spender2 = String::from("addr0003"); - let info = mock_info(owner.as_ref(), &[]); - let env = mock_env(); - do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000)); - - // no allowance to start - assert_eq!( - query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(), - AllowanceResponse::default() - ); - assert_eq!( - query_allowance(deps.as_ref(), owner.clone(), spender2.clone()).unwrap(), - AllowanceResponse::default() - ); - assert_eq!( - query_allowance(deps.as_ref(), spender.clone(), spender2.clone()).unwrap(), - AllowanceResponse::default() - ); - - // set allowance with height expiration - let allow1 = Uint128::new(7777); - let expires = Expiration::AtHeight(123_456); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow1, - expires: Some(expires), - }; - execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - - // set other allowance with no expiration - let allow2 = Uint128::new(87654); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender2.clone(), - amount: allow2, - expires: None, - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - - // check they are proper - let expect_one = AllowanceResponse { - allowance: allow1, - expires, - }; - let expect_two = AllowanceResponse { - allowance: allow2, - expires: Expiration::Never {}, - }; - assert_eq!( - query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(), - expect_one - ); - assert_eq!( - query_allowance(deps.as_ref(), owner.clone(), spender2.clone()).unwrap(), - expect_two - ); - assert_eq!( - query_allowance(deps.as_ref(), spender.clone(), spender2.clone()).unwrap(), - AllowanceResponse::default() - ); - - // also allow spender -> spender2 with no interference - let info = mock_info(spender.as_ref(), &[]); - let env = mock_env(); - let allow3 = Uint128::new(1821); - let expires3 = Expiration::AtTime(Timestamp::from_seconds(3767626296)); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender2.clone(), - amount: allow3, - expires: Some(expires3), - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - let expect_three = AllowanceResponse { - allowance: allow3, - expires: expires3, - }; - assert_eq!( - query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(), - expect_one - ); - assert_eq!( - query_allowance(deps.as_ref(), owner, spender2.clone()).unwrap(), - expect_two - ); - assert_eq!( - query_allowance(deps.as_ref(), spender, spender2).unwrap(), - expect_three - ); - } - - #[test] - fn no_self_allowance() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - - let owner = String::from("addr0001"); - let info = mock_info(owner.as_ref(), &[]); - let env = mock_env(); - do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000)); - - // self-allowance - let msg = ExecuteMsg::IncreaseAllowance { - spender: owner.clone(), - amount: Uint128::new(7777), - expires: None, - }; - let err = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err(); - assert_eq!(err, ContractError::CannotSetOwnAccount {}); - - // decrease self-allowance - let msg = ExecuteMsg::DecreaseAllowance { - spender: owner, - amount: Uint128::new(7777), - expires: None, - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::CannotSetOwnAccount {}); - } - - #[test] - fn transfer_from_respects_limits() { - let mut deps = mock_dependencies_with_balance(&[]); - let owner = String::from("addr0001"); - let spender = String::from("addr0002"); - let rcpt = String::from("addr0003"); - - let start = Uint128::new(999999); - do_instantiate(deps.as_mut(), &owner, start); - - // provide an allowance - let allow1 = Uint128::new(77777); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow1, - expires: None, - }; - let info = mock_info(owner.as_ref(), &[]); - let env = mock_env(); - execute(deps.as_mut(), env, info, msg).unwrap(); - - // valid transfer of part of the allowance - let transfer = Uint128::new(44444); - let msg = ExecuteMsg::TransferFrom { - owner: owner.clone(), - recipient: rcpt.clone(), - amount: transfer, - }; - let info = mock_info(spender.as_ref(), &[]); - let env = mock_env(); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.attributes[0], attr("action", "transfer_from")); - - // make sure money arrived - assert_eq!( - get_balance(deps.as_ref(), owner.clone()), - start.checked_sub(transfer).unwrap() - ); - assert_eq!(get_balance(deps.as_ref(), rcpt.clone()), transfer); - - // ensure it looks good - let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); - let expect = AllowanceResponse { - allowance: allow1.checked_sub(transfer).unwrap(), - expires: Expiration::Never {}, - }; - assert_eq!(expect, allowance); - - // cannot send more than the allowance - let msg = ExecuteMsg::TransferFrom { - owner: owner.clone(), - recipient: rcpt.clone(), - amount: Uint128::new(33443), - }; - let info = mock_info(spender.as_ref(), &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - - // let us increase limit, but set the expiration to expire in the next block - let info = mock_info(owner.as_ref(), &[]); - let mut env = mock_env(); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: Uint128::new(1000), - expires: Some(Expiration::AtHeight(env.block.height + 1)), - }; - execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - - env.block.height += 1; - - // we should now get the expiration error - let msg = ExecuteMsg::TransferFrom { - owner, - recipient: rcpt, - amount: Uint128::new(33443), - }; - let info = mock_info(spender.as_ref(), &[]); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::Expired {}); - } - - #[test] - fn burn_from_respects_limits() { - let mut deps = mock_dependencies_with_balance(&[]); - let owner = String::from("addr0001"); - let spender = String::from("addr0002"); - - let start = Uint128::new(999999); - do_instantiate(deps.as_mut(), &owner, start); - - // provide an allowance - let allow1 = Uint128::new(77777); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow1, - expires: None, - }; - let info = mock_info(owner.as_ref(), &[]); - let env = mock_env(); - execute(deps.as_mut(), env, info, msg).unwrap(); - - // valid burn of part of the allowance - let transfer = Uint128::new(44444); - let msg = ExecuteMsg::BurnFrom { - owner: owner.clone(), - amount: transfer, - }; - let info = mock_info(spender.as_ref(), &[]); - let env = mock_env(); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.attributes[0], attr("action", "burn_from")); - - // make sure money burnt - assert_eq!( - get_balance(deps.as_ref(), owner.clone()), - start.checked_sub(transfer).unwrap() - ); - - // ensure it looks good - let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); - let expect = AllowanceResponse { - allowance: allow1.checked_sub(transfer).unwrap(), - expires: Expiration::Never {}, - }; - assert_eq!(expect, allowance); - - // cannot burn more than the allowance - let msg = ExecuteMsg::BurnFrom { - owner: owner.clone(), - amount: Uint128::new(33443), - }; - let info = mock_info(spender.as_ref(), &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - - // let us increase limit, but set the expiration to expire in the next block - let info = mock_info(owner.as_ref(), &[]); - let mut env = mock_env(); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: Uint128::new(1000), - expires: Some(Expiration::AtHeight(env.block.height + 1)), - }; - execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - - // increase block height, so the limit is expired now - env.block.height += 1; - - // we should now get the expiration error - let msg = ExecuteMsg::BurnFrom { - owner, - amount: Uint128::new(33443), - }; - let info = mock_info(spender.as_ref(), &[]); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::Expired {}); - } - - #[test] - fn send_from_respects_limits() { - let mut deps = mock_dependencies_with_balance(&[]); - let owner = String::from("addr0001"); - let spender = String::from("addr0002"); - let contract = String::from("cool-dex"); - let send_msg = Binary::from(r#"{"some":123}"#.as_bytes()); - - let start = Uint128::new(999999); - do_instantiate(deps.as_mut(), &owner, start); - - // provide an allowance - let allow1 = Uint128::new(77777); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow1, - expires: None, - }; - let info = mock_info(owner.as_ref(), &[]); - let env = mock_env(); - execute(deps.as_mut(), env, info, msg).unwrap(); - - // valid send of part of the allowance - let transfer = Uint128::new(44444); - let msg = ExecuteMsg::SendFrom { - owner: owner.clone(), - amount: transfer, - contract: contract.clone(), - msg: send_msg.clone(), - }; - let info = mock_info(spender.as_ref(), &[]); - let env = mock_env(); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.attributes[0], attr("action", "send_from")); - assert_eq!(1, res.messages.len()); - - // we record this as sent by the one who requested, not the one who was paying - let binary_msg = Cw20ReceiveMsg { - sender: spender.clone(), - amount: transfer, - msg: send_msg.clone(), - } - .into_binary() - .unwrap(); - assert_eq!( - res.messages[0], - SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: contract.clone(), - msg: binary_msg, - funds: vec![], - })) - ); - - // make sure money sent - assert_eq!( - get_balance(deps.as_ref(), owner.clone()), - start.checked_sub(transfer).unwrap() - ); - assert_eq!(get_balance(deps.as_ref(), contract.clone()), transfer); - - // ensure it looks good - let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); - let expect = AllowanceResponse { - allowance: allow1.checked_sub(transfer).unwrap(), - expires: Expiration::Never {}, - }; - assert_eq!(expect, allowance); - - // cannot send more than the allowance - let msg = ExecuteMsg::SendFrom { - owner: owner.clone(), - amount: Uint128::new(33443), - contract: contract.clone(), - msg: send_msg.clone(), - }; - let info = mock_info(spender.as_ref(), &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - - // let us increase limit, but set the expiration to the next block - let info = mock_info(owner.as_ref(), &[]); - let mut env = mock_env(); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: Uint128::new(1000), - expires: Some(Expiration::AtHeight(env.block.height + 1)), - }; - execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - - // increase block height, so the limit is expired now - env.block.height += 1; - - // we should now get the expiration error - let msg = ExecuteMsg::SendFrom { - owner, - amount: Uint128::new(33443), - contract, - msg: send_msg, - }; - let info = mock_info(spender.as_ref(), &[]); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::Expired {}); - } - - #[test] - fn no_past_expiration() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - - let owner = String::from("addr0001"); - let spender = String::from("addr0002"); - let info = mock_info(owner.as_ref(), &[]); - let env = mock_env(); - do_instantiate(deps.as_mut(), owner.clone(), Uint128::new(12340000)); - - // set allowance with height expiration at current block height - let expires = Expiration::AtHeight(env.block.height); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: Uint128::new(7777), - expires: Some(expires), - }; - - // ensure it is rejected - assert_eq!( - Err(ContractError::InvalidExpiration {}), - execute(deps.as_mut(), env.clone(), info.clone(), msg) - ); - - // set allowance with time expiration in the past - let expires = Expiration::AtTime(env.block.time.minus_seconds(1)); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: Uint128::new(7777), - expires: Some(expires), - }; - - // ensure it is rejected - assert_eq!( - Err(ContractError::InvalidExpiration {}), - execute(deps.as_mut(), env.clone(), info.clone(), msg) - ); - - // set allowance with height expiration at next block height - let expires = Expiration::AtHeight(env.block.height + 1); - let allow = Uint128::new(7777); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow, - expires: Some(expires), - }; - - execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - - // ensure it looks good - let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); - assert_eq!( - allowance, - AllowanceResponse { - allowance: allow, - expires - } - ); - - // set allowance with time expiration in the future - let expires = Expiration::AtTime(env.block.time.plus_seconds(10)); - let allow = Uint128::new(7777); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow, - expires: Some(expires), - }; - - execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - - // ensure it looks good - let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(); - assert_eq!( - allowance, - AllowanceResponse { - allowance: allow + allow, // we increased twice - expires - } - ); - - // decrease with height expiration at current block height - let expires = Expiration::AtHeight(env.block.height); - let allow = Uint128::new(7777); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow, - expires: Some(expires), - }; - - // ensure it is rejected - assert_eq!( - Err(ContractError::InvalidExpiration {}), - execute(deps.as_mut(), env.clone(), info.clone(), msg) - ); - - // decrease with height expiration at next block height - let expires = Expiration::AtHeight(env.block.height + 1); - let allow = Uint128::new(7777); - let msg = ExecuteMsg::DecreaseAllowance { - spender: spender.clone(), - amount: allow, - expires: Some(expires), - }; - - execute(deps.as_mut(), env, info, msg).unwrap(); - - // ensure it looks good - let allowance = query_allowance(deps.as_ref(), owner, spender).unwrap(); - assert_eq!( - allowance, - AllowanceResponse { - allowance: allow, - expires - } - ); - } -} diff --git a/contracts/cw20-base/src/bin/schema.rs b/contracts/cw20-base/src/bin/schema.rs deleted file mode 100644 index 40d16fd..0000000 --- a/contracts/cw20-base/src/bin/schema.rs +++ /dev/null @@ -1,11 +0,0 @@ -use cosmwasm_schema::write_api; - -use cw20_base::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; - -fn main() { - write_api! { - instantiate: InstantiateMsg, - execute: ExecuteMsg, - query: QueryMsg, - } -} diff --git a/contracts/cw20-base/src/contract.rs b/contracts/cw20-base/src/contract.rs deleted file mode 100644 index 7e3547b..0000000 --- a/contracts/cw20-base/src/contract.rs +++ /dev/null @@ -1,2236 +0,0 @@ -#[cfg(not(feature = "library"))] -use cosmwasm_std::entry_point; -use cosmwasm_std::Order::Ascending; -use cosmwasm_std::{ - to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Uint128, -}; - -use cw2::set_contract_version; -use cw20::{ - BalanceResponse, Cw20Coin, Cw20ReceiveMsg, DownloadLogoResponse, EmbeddedLogo, Logo, LogoInfo, - MarketingInfoResponse, MinterResponse, TokenInfoResponse, -}; -use cw_utils::ensure_from_older_version; - -use crate::allowances::{ - execute_burn_from, execute_decrease_allowance, execute_increase_allowance, execute_send_from, - execute_transfer_from, query_allowance, -}; -use crate::enumerable::{query_all_accounts, query_owner_allowances, query_spender_allowances}; -use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; -use crate::state::{ - MinterData, TokenInfo, ALLOWANCES, ALLOWANCES_SPENDER, BALANCES, LOGO, MARKETING_INFO, - TOKEN_INFO, -}; - -// version info for migration info -const CONTRACT_NAME: &str = "crates.io:cw20-base"; -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); - -const LOGO_SIZE_CAP: usize = 5 * 1024; - -/// Checks if data starts with XML preamble -fn verify_xml_preamble(data: &[u8]) -> Result<(), ContractError> { - // The easiest way to perform this check would be just match on regex, however regex - // compilation is heavy and probably not worth it. - - let preamble = data - .split_inclusive(|c| *c == b'>') - .next() - .ok_or(ContractError::InvalidXmlPreamble {})?; - - const PREFIX: &[u8] = b""; - - if !(preamble.starts_with(PREFIX) && preamble.ends_with(POSTFIX)) { - Err(ContractError::InvalidXmlPreamble {}) - } else { - Ok(()) - } - - // Additionally attributes format could be validated as they are well defined, as well as - // comments presence inside of preable, but it is probably not worth it. -} - -/// Validates XML logo -fn verify_xml_logo(logo: &[u8]) -> Result<(), ContractError> { - verify_xml_preamble(logo)?; - - if logo.len() > LOGO_SIZE_CAP { - Err(ContractError::LogoTooBig {}) - } else { - Ok(()) - } -} - -/// Validates png logo -fn verify_png_logo(logo: &[u8]) -> Result<(), ContractError> { - // PNG header format: - // 0x89 - magic byte, out of ASCII table to fail on 7-bit systems - // "PNG" ascii representation - // [0x0d, 0x0a] - dos style line ending - // 0x1a - dos control character, stop displaying rest of the file - // 0x0a - unix style line ending - const HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; - if logo.len() > LOGO_SIZE_CAP { - Err(ContractError::LogoTooBig {}) - } else if !logo.starts_with(&HEADER) { - Err(ContractError::InvalidPngHeader {}) - } else { - Ok(()) - } -} - -/// Checks if passed logo is correct, and if not, returns an error -fn verify_logo(logo: &Logo) -> Result<(), ContractError> { - match logo { - Logo::Embedded(EmbeddedLogo::Svg(logo)) => verify_xml_logo(logo), - Logo::Embedded(EmbeddedLogo::Png(logo)) => verify_png_logo(logo), - Logo::Url(_) => Ok(()), // Any reasonable url validation would be regex based, probably not worth it - } -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn instantiate( - mut deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, -) -> Result { - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - // check valid token info - msg.validate()?; - // create initial accounts - let total_supply = create_accounts(&mut deps, &msg.initial_balances)?; - - if let Some(limit) = msg.get_cap() { - if total_supply > limit { - return Err(StdError::generic_err("Initial supply greater than cap").into()); - } - } - - let mint = match msg.mint { - Some(m) => Some(MinterData { - minter: deps.api.addr_validate(&m.minter)?, - cap: m.cap, - }), - None => None, - }; - - // store token info - let data = TokenInfo { - name: msg.name, - symbol: msg.symbol, - decimals: msg.decimals, - total_supply, - mint, - }; - TOKEN_INFO.save(deps.storage, &data)?; - - if let Some(marketing) = msg.marketing { - let logo = if let Some(logo) = marketing.logo { - verify_logo(&logo)?; - LOGO.save(deps.storage, &logo)?; - - match logo { - Logo::Url(url) => Some(LogoInfo::Url(url)), - Logo::Embedded(_) => Some(LogoInfo::Embedded), - } - } else { - None - }; - - let data = MarketingInfoResponse { - project: marketing.project, - description: marketing.description, - marketing: marketing - .marketing - .map(|addr| deps.api.addr_validate(&addr)) - .transpose()?, - logo, - }; - MARKETING_INFO.save(deps.storage, &data)?; - } - - Ok(Response::default()) -} - -pub fn create_accounts( - deps: &mut DepsMut, - accounts: &[Cw20Coin], -) -> Result { - validate_accounts(accounts)?; - - let mut total_supply = Uint128::zero(); - for row in accounts { - let address = deps.api.addr_validate(&row.address)?; - BALANCES.save(deps.storage, &address, &row.amount)?; - total_supply += row.amount; - } - - Ok(total_supply) -} - -pub fn validate_accounts(accounts: &[Cw20Coin]) -> Result<(), ContractError> { - let mut addresses = accounts.iter().map(|c| &c.address).collect::>(); - addresses.sort(); - addresses.dedup(); - - if addresses.len() != accounts.len() { - Err(ContractError::DuplicateInitialBalanceAddresses {}) - } else { - Ok(()) - } -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::Transfer { recipient, amount } => { - execute_transfer(deps, env, info, recipient, amount) - } - ExecuteMsg::Burn { amount } => execute_burn(deps, env, info, amount), - ExecuteMsg::Send { - contract, - amount, - msg, - } => execute_send(deps, env, info, contract, amount, msg), - ExecuteMsg::Mint { recipient, amount } => execute_mint(deps, env, info, recipient, amount), - ExecuteMsg::IncreaseAllowance { - spender, - amount, - expires, - } => execute_increase_allowance(deps, env, info, spender, amount, expires), - ExecuteMsg::DecreaseAllowance { - spender, - amount, - expires, - } => execute_decrease_allowance(deps, env, info, spender, amount, expires), - ExecuteMsg::TransferFrom { - owner, - recipient, - amount, - } => execute_transfer_from(deps, env, info, owner, recipient, amount), - ExecuteMsg::BurnFrom { owner, amount } => execute_burn_from(deps, env, info, owner, amount), - ExecuteMsg::SendFrom { - owner, - contract, - amount, - msg, - } => execute_send_from(deps, env, info, owner, contract, amount, msg), - ExecuteMsg::UpdateMarketing { - project, - description, - marketing, - } => execute_update_marketing(deps, env, info, project, description, marketing), - ExecuteMsg::UploadLogo(logo) => execute_upload_logo(deps, env, info, logo), - ExecuteMsg::UpdateMinter { new_minter } => { - execute_update_minter(deps, env, info, new_minter) - } - } -} - -pub fn execute_transfer( - deps: DepsMut, - _env: Env, - info: MessageInfo, - recipient: String, - amount: Uint128, -) -> Result { - let rcpt_addr = deps.api.addr_validate(&recipient)?; - - BALANCES.update( - deps.storage, - &info.sender, - |balance: Option| -> StdResult<_> { - Ok(balance.unwrap_or_default().checked_sub(amount)?) - }, - )?; - BALANCES.update( - deps.storage, - &rcpt_addr, - |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, - )?; - - let res = Response::new() - .add_attribute("action", "transfer") - .add_attribute("from", info.sender) - .add_attribute("to", recipient) - .add_attribute("amount", amount); - Ok(res) -} - -pub fn execute_burn( - deps: DepsMut, - _env: Env, - info: MessageInfo, - amount: Uint128, -) -> Result { - // lower balance - BALANCES.update( - deps.storage, - &info.sender, - |balance: Option| -> StdResult<_> { - Ok(balance.unwrap_or_default().checked_sub(amount)?) - }, - )?; - // reduce total_supply - TOKEN_INFO.update(deps.storage, |mut info| -> StdResult<_> { - info.total_supply = info.total_supply.checked_sub(amount)?; - Ok(info) - })?; - - let res = Response::new() - .add_attribute("action", "burn") - .add_attribute("from", info.sender) - .add_attribute("amount", amount); - Ok(res) -} - -pub fn execute_mint( - deps: DepsMut, - _env: Env, - info: MessageInfo, - recipient: String, - amount: Uint128, -) -> Result { - let mut config = TOKEN_INFO - .may_load(deps.storage)? - .ok_or(ContractError::Unauthorized {})?; - - if config - .mint - .as_ref() - .ok_or(ContractError::Unauthorized {})? - .minter - != info.sender - { - return Err(ContractError::Unauthorized {}); - } - - // update supply and enforce cap - config.total_supply += amount; - if let Some(limit) = config.get_cap() { - if config.total_supply > limit { - return Err(ContractError::CannotExceedCap {}); - } - } - TOKEN_INFO.save(deps.storage, &config)?; - - // add amount to recipient balance - let rcpt_addr = deps.api.addr_validate(&recipient)?; - BALANCES.update( - deps.storage, - &rcpt_addr, - |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, - )?; - - let res = Response::new() - .add_attribute("action", "mint") - .add_attribute("to", recipient) - .add_attribute("amount", amount); - Ok(res) -} - -pub fn execute_send( - deps: DepsMut, - _env: Env, - info: MessageInfo, - contract: String, - amount: Uint128, - msg: Binary, -) -> Result { - let rcpt_addr = deps.api.addr_validate(&contract)?; - - // move the tokens to the contract - BALANCES.update( - deps.storage, - &info.sender, - |balance: Option| -> StdResult<_> { - Ok(balance.unwrap_or_default().checked_sub(amount)?) - }, - )?; - BALANCES.update( - deps.storage, - &rcpt_addr, - |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, - )?; - - let res = Response::new() - .add_attribute("action", "send") - .add_attribute("from", &info.sender) - .add_attribute("to", &contract) - .add_attribute("amount", amount) - .add_message( - Cw20ReceiveMsg { - sender: info.sender.into(), - amount, - msg, - } - .into_cosmos_msg(contract)?, - ); - Ok(res) -} - -pub fn execute_update_minter( - deps: DepsMut, - _env: Env, - info: MessageInfo, - new_minter: Option, -) -> Result { - let mut config = TOKEN_INFO - .may_load(deps.storage)? - .ok_or(ContractError::Unauthorized {})?; - - let mint = config.mint.as_ref().ok_or(ContractError::Unauthorized {})?; - if mint.minter != info.sender { - return Err(ContractError::Unauthorized {}); - } - - let minter_data = new_minter - .map(|new_minter| deps.api.addr_validate(&new_minter)) - .transpose()? - .map(|minter| MinterData { - minter, - cap: mint.cap, - }); - - config.mint = minter_data; - - TOKEN_INFO.save(deps.storage, &config)?; - - Ok(Response::default() - .add_attribute("action", "update_minter") - .add_attribute( - "new_minter", - config - .mint - .map(|m| m.minter.into_string()) - .unwrap_or_else(|| "None".to_string()), - )) -} - -pub fn execute_update_marketing( - deps: DepsMut, - _env: Env, - info: MessageInfo, - project: Option, - description: Option, - marketing: Option, -) -> Result { - let mut marketing_info = MARKETING_INFO - .may_load(deps.storage)? - .ok_or(ContractError::Unauthorized {})?; - - if marketing_info - .marketing - .as_ref() - .ok_or(ContractError::Unauthorized {})? - != &info.sender - { - return Err(ContractError::Unauthorized {}); - } - - match project { - Some(empty) if empty.trim().is_empty() => marketing_info.project = None, - Some(project) => marketing_info.project = Some(project), - None => (), - } - - match description { - Some(empty) if empty.trim().is_empty() => marketing_info.description = None, - Some(description) => marketing_info.description = Some(description), - None => (), - } - - match marketing { - Some(empty) if empty.trim().is_empty() => marketing_info.marketing = None, - Some(marketing) => marketing_info.marketing = Some(deps.api.addr_validate(&marketing)?), - None => (), - } - - if marketing_info.project.is_none() - && marketing_info.description.is_none() - && marketing_info.marketing.is_none() - && marketing_info.logo.is_none() - { - MARKETING_INFO.remove(deps.storage); - } else { - MARKETING_INFO.save(deps.storage, &marketing_info)?; - } - - let res = Response::new().add_attribute("action", "update_marketing"); - Ok(res) -} - -pub fn execute_upload_logo( - deps: DepsMut, - _env: Env, - info: MessageInfo, - logo: Logo, -) -> Result { - let mut marketing_info = MARKETING_INFO - .may_load(deps.storage)? - .ok_or(ContractError::Unauthorized {})?; - - verify_logo(&logo)?; - - if marketing_info - .marketing - .as_ref() - .ok_or(ContractError::Unauthorized {})? - != &info.sender - { - return Err(ContractError::Unauthorized {}); - } - - LOGO.save(deps.storage, &logo)?; - - let logo_info = match logo { - Logo::Url(url) => LogoInfo::Url(url), - Logo::Embedded(_) => LogoInfo::Embedded, - }; - - marketing_info.logo = Some(logo_info); - MARKETING_INFO.save(deps.storage, &marketing_info)?; - - let res = Response::new().add_attribute("action", "upload_logo"); - Ok(res) -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - match msg { - QueryMsg::Balance { address } => to_binary(&query_balance(deps, address)?), - QueryMsg::TokenInfo {} => to_binary(&query_token_info(deps)?), - QueryMsg::Minter {} => to_binary(&query_minter(deps)?), - QueryMsg::Allowance { owner, spender } => { - to_binary(&query_allowance(deps, owner, spender)?) - } - QueryMsg::AllAllowances { - owner, - start_after, - limit, - } => to_binary(&query_owner_allowances(deps, owner, start_after, limit)?), - QueryMsg::AllSpenderAllowances { - spender, - start_after, - limit, - } => to_binary(&query_spender_allowances( - deps, - spender, - start_after, - limit, - )?), - QueryMsg::AllAccounts { start_after, limit } => { - to_binary(&query_all_accounts(deps, start_after, limit)?) - } - QueryMsg::MarketingInfo {} => to_binary(&query_marketing_info(deps)?), - QueryMsg::DownloadLogo {} => to_binary(&query_download_logo(deps)?), - } -} - -pub fn query_balance(deps: Deps, address: String) -> StdResult { - let address = deps.api.addr_validate(&address)?; - let balance = BALANCES - .may_load(deps.storage, &address)? - .unwrap_or_default(); - Ok(BalanceResponse { balance }) -} - -pub fn query_token_info(deps: Deps) -> StdResult { - let info = TOKEN_INFO.load(deps.storage)?; - let res = TokenInfoResponse { - name: info.name, - symbol: info.symbol, - decimals: info.decimals, - total_supply: info.total_supply, - }; - Ok(res) -} - -pub fn query_minter(deps: Deps) -> StdResult> { - let meta = TOKEN_INFO.load(deps.storage)?; - let minter = match meta.mint { - Some(m) => Some(MinterResponse { - minter: m.minter.into(), - cap: m.cap, - }), - None => None, - }; - Ok(minter) -} - -pub fn query_marketing_info(deps: Deps) -> StdResult { - Ok(MARKETING_INFO.may_load(deps.storage)?.unwrap_or_default()) -} - -pub fn query_download_logo(deps: Deps) -> StdResult { - let logo = LOGO.load(deps.storage)?; - match logo { - Logo::Embedded(EmbeddedLogo::Svg(logo)) => Ok(DownloadLogoResponse { - mime_type: "image/svg+xml".to_owned(), - data: logo, - }), - Logo::Embedded(EmbeddedLogo::Png(logo)) => Ok(DownloadLogoResponse { - mime_type: "image/png".to_owned(), - data: logo, - }), - Logo::Url(_) => Err(StdError::not_found("logo")), - } -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { - let original_version = - ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - if original_version < "0.14.0".parse::().unwrap() { - // Build reverse map of allowances per spender - let data = ALLOWANCES - .range(deps.storage, None, None, Ascending) - .collect::>>()?; - for ((owner, spender), allowance) in data { - ALLOWANCES_SPENDER.save(deps.storage, (&spender, &owner), &allowance)?; - } - } - Ok(Response::default()) -} - -#[cfg(test)] -mod tests { - use cosmwasm_std::testing::{ - mock_dependencies, mock_dependencies_with_balance, mock_env, mock_info, - }; - use cosmwasm_std::{coins, from_binary, Addr, CosmosMsg, StdError, SubMsg, WasmMsg}; - - use super::*; - use crate::msg::InstantiateMarketingInfo; - - fn get_balance>(deps: Deps, address: T) -> Uint128 { - query_balance(deps, address.into()).unwrap().balance - } - - // this will set up the instantiation for other tests - fn do_instantiate_with_minter( - deps: DepsMut, - addr: &str, - amount: Uint128, - minter: &str, - cap: Option, - ) -> TokenInfoResponse { - _do_instantiate( - deps, - addr, - amount, - Some(MinterResponse { - minter: minter.to_string(), - cap, - }), - ) - } - - // this will set up the instantiation for other tests - fn do_instantiate(deps: DepsMut, addr: &str, amount: Uint128) -> TokenInfoResponse { - _do_instantiate(deps, addr, amount, None) - } - - // this will set up the instantiation for other tests - fn _do_instantiate( - mut deps: DepsMut, - addr: &str, - amount: Uint128, - mint: Option, - ) -> TokenInfoResponse { - let instantiate_msg = InstantiateMsg { - name: "Auto Gen".to_string(), - symbol: "AUTO".to_string(), - decimals: 3, - initial_balances: vec![Cw20Coin { - address: addr.to_string(), - amount, - }], - mint: mint.clone(), - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.branch(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - let meta = query_token_info(deps.as_ref()).unwrap(); - assert_eq!( - meta, - TokenInfoResponse { - name: "Auto Gen".to_string(), - symbol: "AUTO".to_string(), - decimals: 3, - total_supply: amount, - } - ); - assert_eq!(get_balance(deps.as_ref(), addr), amount); - assert_eq!(query_minter(deps.as_ref()).unwrap(), mint,); - meta - } - - const PNG_HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; - - mod instantiate { - use super::*; - - #[test] - fn basic() { - let mut deps = mock_dependencies(); - let amount = Uint128::from(11223344u128); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![Cw20Coin { - address: String::from("addr0000"), - amount, - }], - mint: None, - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - assert_eq!( - query_token_info(deps.as_ref()).unwrap(), - TokenInfoResponse { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - total_supply: amount, - } - ); - assert_eq!( - get_balance(deps.as_ref(), "addr0000"), - Uint128::new(11223344) - ); - } - - #[test] - fn mintable() { - let mut deps = mock_dependencies(); - let amount = Uint128::new(11223344); - let minter = String::from("asmodat"); - let limit = Uint128::new(511223344); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![Cw20Coin { - address: "addr0000".into(), - amount, - }], - mint: Some(MinterResponse { - minter: minter.clone(), - cap: Some(limit), - }), - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - assert_eq!( - query_token_info(deps.as_ref()).unwrap(), - TokenInfoResponse { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - total_supply: amount, - } - ); - assert_eq!( - get_balance(deps.as_ref(), "addr0000"), - Uint128::new(11223344) - ); - assert_eq!( - query_minter(deps.as_ref()).unwrap(), - Some(MinterResponse { - minter, - cap: Some(limit), - }), - ); - } - - #[test] - fn mintable_over_cap() { - let mut deps = mock_dependencies(); - let amount = Uint128::new(11223344); - let minter = String::from("asmodat"); - let limit = Uint128::new(11223300); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![Cw20Coin { - address: String::from("addr0000"), - amount, - }], - mint: Some(MinterResponse { - minter, - cap: Some(limit), - }), - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - let err = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap_err(); - assert_eq!( - err, - StdError::generic_err("Initial supply greater than cap").into() - ); - } - - mod marketing { - use super::*; - - #[test] - fn basic() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("marketing".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("marketing")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn invalid_marketing() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("m".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - let env = mock_env(); - instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap_err(); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - } - } - - #[test] - fn can_mint_by_minter() { - let mut deps = mock_dependencies(); - - let genesis = String::from("genesis"); - let amount = Uint128::new(11223344); - let minter = String::from("asmodat"); - let limit = Uint128::new(511223344); - do_instantiate_with_minter(deps.as_mut(), &genesis, amount, &minter, Some(limit)); - - // minter can mint coins to some winner - let winner = String::from("lucky"); - let prize = Uint128::new(222_222_222); - let msg = ExecuteMsg::Mint { - recipient: winner.clone(), - amount: prize, - }; - - let info = mock_info(minter.as_ref(), &[]); - let env = mock_env(); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - assert_eq!(get_balance(deps.as_ref(), genesis), amount); - assert_eq!(get_balance(deps.as_ref(), winner.clone()), prize); - - // Allows minting 0 - let msg = ExecuteMsg::Mint { - recipient: winner.clone(), - amount: Uint128::zero(), - }; - let info = mock_info(minter.as_ref(), &[]); - let env = mock_env(); - execute(deps.as_mut(), env, info, msg).unwrap(); - - // but if it exceeds cap (even over multiple rounds), it fails - // cap is enforced - let msg = ExecuteMsg::Mint { - recipient: winner, - amount: Uint128::new(333_222_222), - }; - let info = mock_info(minter.as_ref(), &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::CannotExceedCap {}); - } - - #[test] - fn others_cannot_mint() { - let mut deps = mock_dependencies(); - do_instantiate_with_minter( - deps.as_mut(), - &String::from("genesis"), - Uint128::new(1234), - &String::from("minter"), - None, - ); - - let msg = ExecuteMsg::Mint { - recipient: String::from("lucky"), - amount: Uint128::new(222), - }; - let info = mock_info("anyone else", &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); - } - - #[test] - fn minter_can_update_minter_but_not_cap() { - let mut deps = mock_dependencies(); - let minter = String::from("minter"); - let cap = Some(Uint128::from(3000000u128)); - do_instantiate_with_minter( - deps.as_mut(), - &String::from("genesis"), - Uint128::new(1234), - &minter, - cap, - ); - - let new_minter = "new_minter"; - let msg = ExecuteMsg::UpdateMinter { - new_minter: Some(new_minter.to_string()), - }; - - let info = mock_info(&minter, &[]); - let env = mock_env(); - let res = execute(deps.as_mut(), env.clone(), info, msg); - assert!(res.is_ok()); - let query_minter_msg = QueryMsg::Minter {}; - let res = query(deps.as_ref(), env, query_minter_msg); - let mint: MinterResponse = from_binary(&res.unwrap()).unwrap(); - - // Minter cannot update cap. - assert!(mint.cap == cap); - assert!(mint.minter == new_minter) - } - - #[test] - fn others_cannot_update_minter() { - let mut deps = mock_dependencies(); - let minter = String::from("minter"); - do_instantiate_with_minter( - deps.as_mut(), - &String::from("genesis"), - Uint128::new(1234), - &minter, - None, - ); - - let msg = ExecuteMsg::UpdateMinter { - new_minter: Some("new_minter".to_string()), - }; - - let info = mock_info("not the minter", &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); - } - - #[test] - fn unset_minter() { - let mut deps = mock_dependencies(); - let minter = String::from("minter"); - let cap = None; - do_instantiate_with_minter( - deps.as_mut(), - &String::from("genesis"), - Uint128::new(1234), - &minter, - cap, - ); - - let msg = ExecuteMsg::UpdateMinter { new_minter: None }; - - let info = mock_info(&minter, &[]); - let env = mock_env(); - let res = execute(deps.as_mut(), env.clone(), info, msg); - assert!(res.is_ok()); - let query_minter_msg = QueryMsg::Minter {}; - let res = query(deps.as_ref(), env, query_minter_msg); - let mint: Option = from_binary(&res.unwrap()).unwrap(); - - // Check that mint information was removed. - assert_eq!(mint, None); - - // Check that old minter can no longer mint. - let msg = ExecuteMsg::Mint { - recipient: String::from("lucky"), - amount: Uint128::new(222), - }; - let info = mock_info("minter", &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); - } - - #[test] - fn no_one_mints_if_minter_unset() { - let mut deps = mock_dependencies(); - do_instantiate(deps.as_mut(), &String::from("genesis"), Uint128::new(1234)); - - let msg = ExecuteMsg::Mint { - recipient: String::from("lucky"), - amount: Uint128::new(222), - }; - let info = mock_info("genesis", &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); - } - - #[test] - fn instantiate_multiple_accounts() { - let mut deps = mock_dependencies(); - let amount1 = Uint128::from(11223344u128); - let addr1 = String::from("addr0001"); - let amount2 = Uint128::from(7890987u128); - let addr2 = String::from("addr0002"); - let info = mock_info("creator", &[]); - let env = mock_env(); - - // Fails with duplicate addresses - let instantiate_msg = InstantiateMsg { - name: "Bash Shell".to_string(), - symbol: "BASH".to_string(), - decimals: 6, - initial_balances: vec![ - Cw20Coin { - address: addr1.clone(), - amount: amount1, - }, - Cw20Coin { - address: addr1.clone(), - amount: amount2, - }, - ], - mint: None, - marketing: None, - }; - let err = - instantiate(deps.as_mut(), env.clone(), info.clone(), instantiate_msg).unwrap_err(); - assert_eq!(err, ContractError::DuplicateInitialBalanceAddresses {}); - - // Works with unique addresses - let instantiate_msg = InstantiateMsg { - name: "Bash Shell".to_string(), - symbol: "BASH".to_string(), - decimals: 6, - initial_balances: vec![ - Cw20Coin { - address: addr1.clone(), - amount: amount1, - }, - Cw20Coin { - address: addr2.clone(), - amount: amount2, - }, - ], - mint: None, - marketing: None, - }; - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - assert_eq!( - query_token_info(deps.as_ref()).unwrap(), - TokenInfoResponse { - name: "Bash Shell".to_string(), - symbol: "BASH".to_string(), - decimals: 6, - total_supply: amount1 + amount2, - } - ); - assert_eq!(get_balance(deps.as_ref(), addr1), amount1); - assert_eq!(get_balance(deps.as_ref(), addr2), amount2); - } - - #[test] - fn queries_work() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - let addr1 = String::from("addr0001"); - let amount1 = Uint128::from(12340000u128); - - let expected = do_instantiate(deps.as_mut(), &addr1, amount1); - - // check meta query - let loaded = query_token_info(deps.as_ref()).unwrap(); - assert_eq!(expected, loaded); - - let _info = mock_info("test", &[]); - let env = mock_env(); - // check balance query (full) - let data = query( - deps.as_ref(), - env.clone(), - QueryMsg::Balance { address: addr1 }, - ) - .unwrap(); - let loaded: BalanceResponse = from_binary(&data).unwrap(); - assert_eq!(loaded.balance, amount1); - - // check balance query (empty) - let data = query( - deps.as_ref(), - env, - QueryMsg::Balance { - address: String::from("addr0002"), - }, - ) - .unwrap(); - let loaded: BalanceResponse = from_binary(&data).unwrap(); - assert_eq!(loaded.balance, Uint128::zero()); - } - - #[test] - fn transfer() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - let addr1 = String::from("addr0001"); - let addr2 = String::from("addr0002"); - let amount1 = Uint128::from(12340000u128); - let transfer = Uint128::from(76543u128); - let too_much = Uint128::from(12340321u128); - - do_instantiate(deps.as_mut(), &addr1, amount1); - - // Allows transferring 0 - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Transfer { - recipient: addr2.clone(), - amount: Uint128::zero(), - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - - // cannot send more than we have - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Transfer { - recipient: addr2.clone(), - amount: too_much, - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - - // cannot send from empty account - let info = mock_info(addr2.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Transfer { - recipient: addr1.clone(), - amount: transfer, - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - - // valid transfer - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Transfer { - recipient: addr2.clone(), - amount: transfer, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.messages.len(), 0); - - let remainder = amount1.checked_sub(transfer).unwrap(); - assert_eq!(get_balance(deps.as_ref(), addr1), remainder); - assert_eq!(get_balance(deps.as_ref(), addr2), transfer); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - amount1 - ); - } - - #[test] - fn burn() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - let addr1 = String::from("addr0001"); - let amount1 = Uint128::from(12340000u128); - let burn = Uint128::from(76543u128); - let too_much = Uint128::from(12340321u128); - - do_instantiate(deps.as_mut(), &addr1, amount1); - - // Allows burning 0 - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Burn { - amount: Uint128::zero(), - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - amount1 - ); - - // cannot burn more than we have - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Burn { amount: too_much }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - amount1 - ); - - // valid burn reduces total supply - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Burn { amount: burn }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.messages.len(), 0); - - let remainder = amount1.checked_sub(burn).unwrap(); - assert_eq!(get_balance(deps.as_ref(), addr1), remainder); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - remainder - ); - } - - #[test] - fn send() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - let addr1 = String::from("addr0001"); - let contract = String::from("addr0002"); - let amount1 = Uint128::from(12340000u128); - let transfer = Uint128::from(76543u128); - let too_much = Uint128::from(12340321u128); - let send_msg = Binary::from(r#"{"some":123}"#.as_bytes()); - - do_instantiate(deps.as_mut(), &addr1, amount1); - - // Allows sending 0 - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Send { - contract: contract.clone(), - amount: Uint128::zero(), - msg: send_msg.clone(), - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - - // cannot send more than we have - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Send { - contract: contract.clone(), - amount: too_much, - msg: send_msg.clone(), - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - - // valid transfer - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Send { - contract: contract.clone(), - amount: transfer, - msg: send_msg.clone(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.messages.len(), 1); - - // ensure proper send message sent - // this is the message we want delivered to the other side - let binary_msg = Cw20ReceiveMsg { - sender: addr1.clone(), - amount: transfer, - msg: send_msg, - } - .into_binary() - .unwrap(); - // and this is how it must be wrapped for the vm to process it - assert_eq!( - res.messages[0], - SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: contract.clone(), - msg: binary_msg, - funds: vec![], - })) - ); - - // ensure balance is properly transferred - let remainder = amount1.checked_sub(transfer).unwrap(); - assert_eq!(get_balance(deps.as_ref(), addr1), remainder); - assert_eq!(get_balance(deps.as_ref(), contract), transfer); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - amount1 - ); - } - - mod migration { - use super::*; - - use cosmwasm_std::Empty; - use cw20::{AllAllowancesResponse, AllSpenderAllowancesResponse, SpenderAllowanceInfo}; - use cw_multi_test::{App, Contract, ContractWrapper, Executor}; - use cw_utils::Expiration; - - fn cw20_contract() -> Box> { - let contract = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ) - .with_migrate(crate::contract::migrate); - Box::new(contract) - } - - #[test] - fn test_migrate() { - let mut app = App::default(); - - let cw20_id = app.store_code(cw20_contract()); - let cw20_addr = app - .instantiate_contract( - cw20_id, - Addr::unchecked("sender"), - &InstantiateMsg { - name: "Token".to_string(), - symbol: "TOKEN".to_string(), - decimals: 6, - initial_balances: vec![Cw20Coin { - address: "sender".to_string(), - amount: Uint128::new(100), - }], - mint: None, - marketing: None, - }, - &[], - "TOKEN", - Some("sender".to_string()), - ) - .unwrap(); - - // no allowance to start - let allowance: AllAllowancesResponse = app - .wrap() - .query_wasm_smart( - cw20_addr.to_string(), - &QueryMsg::AllAllowances { - owner: "sender".to_string(), - start_after: None, - limit: None, - }, - ) - .unwrap(); - assert_eq!(allowance, AllAllowancesResponse::default()); - - // Set allowance - let allow1 = Uint128::new(7777); - let expires = Expiration::AtHeight(123_456); - let msg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: cw20_addr.to_string(), - msg: to_binary(&ExecuteMsg::IncreaseAllowance { - spender: "spender".into(), - amount: allow1, - expires: Some(expires), - }) - .unwrap(), - funds: vec![], - }); - app.execute(Addr::unchecked("sender"), msg).unwrap(); - - // Now migrate - app.execute( - Addr::unchecked("sender"), - CosmosMsg::Wasm(WasmMsg::Migrate { - contract_addr: cw20_addr.to_string(), - new_code_id: cw20_id, - msg: to_binary(&MigrateMsg {}).unwrap(), - }), - ) - .unwrap(); - - // Smoke check that the contract still works. - let balance: cw20::BalanceResponse = app - .wrap() - .query_wasm_smart( - cw20_addr.clone(), - &QueryMsg::Balance { - address: "sender".to_string(), - }, - ) - .unwrap(); - - assert_eq!(balance.balance, Uint128::new(100)); - - // Confirm that the allowance per spender is there - let allowance: AllSpenderAllowancesResponse = app - .wrap() - .query_wasm_smart( - cw20_addr, - &QueryMsg::AllSpenderAllowances { - spender: "spender".to_string(), - start_after: None, - limit: None, - }, - ) - .unwrap(); - assert_eq!( - allowance.allowances, - &[SpenderAllowanceInfo { - owner: "sender".to_string(), - allowance: allow1, - expires - }] - ); - } - } - - mod marketing { - use super::*; - - #[test] - fn update_unauthorised() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("marketing".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let err = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UpdateMarketing { - project: Some("New project".to_owned()), - description: Some("Better description".to_owned()), - marketing: Some("creator".to_owned()), - }, - ) - .unwrap_err(); - - assert_eq!(err, ContractError::Unauthorized {}); - - // Ensure marketing didn't change - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("marketing")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn update_project() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let res = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UpdateMarketing { - project: Some("New project".to_owned()), - description: None, - marketing: None, - }, - ) - .unwrap(); - - assert_eq!(res.messages, vec![]); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("New project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn clear_project() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let res = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UpdateMarketing { - project: Some("".to_owned()), - description: None, - marketing: None, - }, - ) - .unwrap(); - - assert_eq!(res.messages, vec![]); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: None, - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn update_description() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let res = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UpdateMarketing { - project: None, - description: Some("Better description".to_owned()), - marketing: None, - }, - ) - .unwrap(); - - assert_eq!(res.messages, vec![]); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Better description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn clear_description() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let res = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UpdateMarketing { - project: None, - description: Some("".to_owned()), - marketing: None, - }, - ) - .unwrap(); - - assert_eq!(res.messages, vec![]); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: None, - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn update_marketing() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let res = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UpdateMarketing { - project: None, - description: None, - marketing: Some("marketing".to_owned()), - }, - ) - .unwrap(); - - assert_eq!(res.messages, vec![]); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("marketing")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn update_marketing_invalid() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let err = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UpdateMarketing { - project: None, - description: None, - marketing: Some("m".to_owned()), - }, - ) - .unwrap_err(); - - assert!( - matches!(err, ContractError::Std(_)), - "Expected Std error, received: {}", - err - ); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn clear_marketing() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let res = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UpdateMarketing { - project: None, - description: None, - marketing: Some("".to_owned()), - }, - ) - .unwrap(); - - assert_eq!(res.messages, vec![]); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: None, - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn update_logo_url() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let res = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UploadLogo(Logo::Url("new_url".to_owned())), - ) - .unwrap(); - - assert_eq!(res.messages, vec![]); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("new_url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn update_logo_png() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let res = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Png(PNG_HEADER.into()))), - ) - .unwrap(); - - assert_eq!(res.messages, vec![]); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Embedded), - } - ); - - assert_eq!( - query_download_logo(deps.as_ref()).unwrap(), - DownloadLogoResponse { - mime_type: "image/png".to_owned(), - data: PNG_HEADER.into(), - } - ); - } - - #[test] - fn update_logo_svg() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let img = "".as_bytes(); - let res = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Svg(img.into()))), - ) - .unwrap(); - - assert_eq!(res.messages, vec![]); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Embedded), - } - ); - - assert_eq!( - query_download_logo(deps.as_ref()).unwrap(), - DownloadLogoResponse { - mime_type: "image/svg+xml".to_owned(), - data: img.into(), - } - ); - } - - #[test] - fn update_logo_png_oversized() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let img = [&PNG_HEADER[..], &[1; 6000][..]].concat(); - let err = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Png(img.into()))), - ) - .unwrap_err(); - - assert_eq!(err, ContractError::LogoTooBig {}); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn update_logo_svg_oversized() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let img = [ - "", - std::str::from_utf8(&[b'x'; 6000]).unwrap(), - "", - ] - .concat() - .into_bytes(); - - let err = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Svg(img.into()))), - ) - .unwrap_err(); - - assert_eq!(err, ContractError::LogoTooBig {}); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn update_logo_png_invalid() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let img = &[1]; - let err = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Png(img.into()))), - ) - .unwrap_err(); - - assert_eq!(err, ContractError::InvalidPngHeader {}); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn update_logo_svg_invalid() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("creator".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - - instantiate(deps.as_mut(), mock_env(), info.clone(), instantiate_msg).unwrap(); - - let img = &[1]; - - let err = execute( - deps.as_mut(), - mock_env(), - info, - ExecuteMsg::UploadLogo(Logo::Embedded(EmbeddedLogo::Svg(img.into()))), - ) - .unwrap_err(); - - assert_eq!(err, ContractError::InvalidXmlPreamble {}); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("creator")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - } -} diff --git a/contracts/cw20-base/src/enumerable.rs b/contracts/cw20-base/src/enumerable.rs deleted file mode 100644 index f465134..0000000 --- a/contracts/cw20-base/src/enumerable.rs +++ /dev/null @@ -1,319 +0,0 @@ -use cosmwasm_std::{Deps, Order, StdResult}; -use cw20::{ - AllAccountsResponse, AllAllowancesResponse, AllSpenderAllowancesResponse, AllowanceInfo, - SpenderAllowanceInfo, -}; - -use crate::state::{ALLOWANCES, ALLOWANCES_SPENDER, BALANCES}; -use cw_storage_plus::Bound; - -// settings for pagination -const MAX_LIMIT: u32 = 30; -const DEFAULT_LIMIT: u32 = 10; - -pub fn query_owner_allowances( - deps: Deps, - owner: String, - start_after: Option, - limit: Option, -) -> StdResult { - let owner_addr = deps.api.addr_validate(&owner)?; - let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; - let start = start_after.map(|s| Bound::ExclusiveRaw(s.into_bytes())); - - let allowances = ALLOWANCES - .prefix(&owner_addr) - .range(deps.storage, start, None, Order::Ascending) - .take(limit) - .map(|item| { - item.map(|(addr, allow)| AllowanceInfo { - spender: addr.into(), - allowance: allow.allowance, - expires: allow.expires, - }) - }) - .collect::>()?; - Ok(AllAllowancesResponse { allowances }) -} - -pub fn query_spender_allowances( - deps: Deps, - spender: String, - start_after: Option, - limit: Option, -) -> StdResult { - let spender_addr = deps.api.addr_validate(&spender)?; - let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; - let start = start_after.map(|s| Bound::ExclusiveRaw(s.into_bytes())); - - let allowances = ALLOWANCES_SPENDER - .prefix(&spender_addr) - .range(deps.storage, start, None, Order::Ascending) - .take(limit) - .map(|item| { - item.map(|(addr, allow)| SpenderAllowanceInfo { - owner: addr.into(), - allowance: allow.allowance, - expires: allow.expires, - }) - }) - .collect::>()?; - Ok(AllSpenderAllowancesResponse { allowances }) -} - -pub fn query_all_accounts( - deps: Deps, - start_after: Option, - limit: Option, -) -> StdResult { - let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; - let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); - - let accounts = BALANCES - .keys(deps.storage, start, None, Order::Ascending) - .take(limit) - .map(|item| item.map(Into::into)) - .collect::>()?; - - Ok(AllAccountsResponse { accounts }) -} - -#[cfg(test)] -mod tests { - use super::*; - - use cosmwasm_std::testing::{mock_dependencies_with_balance, mock_env, mock_info}; - use cosmwasm_std::{coins, from_binary, DepsMut, Uint128}; - use cw20::{Cw20Coin, Expiration, TokenInfoResponse}; - - use crate::contract::{execute, instantiate, query, query_token_info}; - use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; - - // this will set up the instantiation for other tests - fn do_instantiate(mut deps: DepsMut, addr: &str, amount: Uint128) -> TokenInfoResponse { - let instantiate_msg = InstantiateMsg { - name: "Auto Gen".to_string(), - symbol: "AUTO".to_string(), - decimals: 3, - initial_balances: vec![Cw20Coin { - address: addr.into(), - amount, - }], - mint: None, - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - instantiate(deps.branch(), env, info, instantiate_msg).unwrap(); - query_token_info(deps.as_ref()).unwrap() - } - - #[test] - fn query_all_owner_allowances_works() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - - let owner = String::from("owner"); - // these are in alphabetical order same than insert order - let spender1 = String::from("earlier"); - let spender2 = String::from("later"); - - let info = mock_info(owner.as_ref(), &[]); - let env = mock_env(); - do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000)); - - // no allowance to start - let allowances = query_owner_allowances(deps.as_ref(), owner.clone(), None, None).unwrap(); - assert_eq!(allowances.allowances, vec![]); - - // set allowance with height expiration - let allow1 = Uint128::new(7777); - let expires = Expiration::AtHeight(123_456); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender1.clone(), - amount: allow1, - expires: Some(expires), - }; - execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - - // set allowance with no expiration - let allow2 = Uint128::new(54321); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender2.clone(), - amount: allow2, - expires: None, - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - - // query list gets 2 - let allowances = query_owner_allowances(deps.as_ref(), owner.clone(), None, None).unwrap(); - assert_eq!(allowances.allowances.len(), 2); - - // first one is spender1 (order of CanonicalAddr uncorrelated with String) - let allowances = - query_owner_allowances(deps.as_ref(), owner.clone(), None, Some(1)).unwrap(); - assert_eq!(allowances.allowances.len(), 1); - let allow = &allowances.allowances[0]; - assert_eq!(&allow.spender, &spender1); - assert_eq!(&allow.expires, &expires); - assert_eq!(&allow.allowance, &allow1); - - // next one is spender2 - let allowances = query_owner_allowances( - deps.as_ref(), - owner, - Some(allow.spender.clone()), - Some(10000), - ) - .unwrap(); - assert_eq!(allowances.allowances.len(), 1); - let allow = &allowances.allowances[0]; - assert_eq!(&allow.spender, &spender2); - assert_eq!(&allow.expires, &Expiration::Never {}); - assert_eq!(&allow.allowance, &allow2); - } - - #[test] - fn query_all_spender_allowances_works() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - - // these are in alphabetical order same than insert order - let owner1 = String::from("owner1"); - let owner2 = String::from("owner2"); - let spender = String::from("spender"); - - let info = mock_info(owner1.as_ref(), &[]); - let env = mock_env(); - do_instantiate(deps.as_mut(), &owner1, Uint128::new(12340000)); - - // no allowance to start - let allowances = - query_spender_allowances(deps.as_ref(), spender.clone(), None, None).unwrap(); - assert_eq!(allowances.allowances, vec![]); - - // set allowance with height expiration - let allow1 = Uint128::new(7777); - let expires = Expiration::AtHeight(123_456); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow1, - expires: Some(expires), - }; - execute(deps.as_mut(), env, info, msg).unwrap(); - - // set allowance with no expiration, from the other owner - let info = mock_info(owner2.as_ref(), &[]); - let env = mock_env(); - do_instantiate(deps.as_mut(), &owner2, Uint128::new(12340000)); - - let allow2 = Uint128::new(54321); - let msg = ExecuteMsg::IncreaseAllowance { - spender: spender.clone(), - amount: allow2, - expires: None, - }; - execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - - // query list gets both - let msg = QueryMsg::AllSpenderAllowances { - spender: spender.clone(), - start_after: None, - limit: None, - }; - let allowances: AllSpenderAllowancesResponse = - from_binary(&query(deps.as_ref(), env.clone(), msg).unwrap()).unwrap(); - assert_eq!(allowances.allowances.len(), 2); - - // one is owner1 (order of CanonicalAddr uncorrelated with String) - let msg = QueryMsg::AllSpenderAllowances { - spender: spender.clone(), - start_after: None, - limit: Some(1), - }; - let allowances: AllSpenderAllowancesResponse = - from_binary(&query(deps.as_ref(), env.clone(), msg).unwrap()).unwrap(); - assert_eq!(allowances.allowances.len(), 1); - let allow = &allowances.allowances[0]; - assert_eq!(&allow.owner, &owner1); - assert_eq!(&allow.expires, &expires); - assert_eq!(&allow.allowance, &allow1); - - // other one is owner2 - let msg = QueryMsg::AllSpenderAllowances { - spender, - start_after: Some(owner1), - limit: Some(10000), - }; - let allowances: AllSpenderAllowancesResponse = - from_binary(&query(deps.as_ref(), env, msg).unwrap()).unwrap(); - assert_eq!(allowances.allowances.len(), 1); - let allow = &allowances.allowances[0]; - assert_eq!(&allow.owner, &owner2); - assert_eq!(&allow.expires, &Expiration::Never {}); - assert_eq!(&allow.allowance, &allow2); - } - - #[test] - fn query_all_accounts_works() { - let mut deps = mock_dependencies_with_balance(&coins(2, "token")); - - // insert order and lexicographical order are different - let acct1 = String::from("acct01"); - let acct2 = String::from("zebra"); - let acct3 = String::from("nice"); - let acct4 = String::from("aaaardvark"); - let expected_order = [acct4.clone(), acct1.clone(), acct3.clone(), acct2.clone()]; - - do_instantiate(deps.as_mut(), &acct1, Uint128::new(12340000)); - - // put money everywhere (to create balanaces) - let info = mock_info(acct1.as_ref(), &[]); - let env = mock_env(); - execute( - deps.as_mut(), - env.clone(), - info.clone(), - ExecuteMsg::Transfer { - recipient: acct2, - amount: Uint128::new(222222), - }, - ) - .unwrap(); - execute( - deps.as_mut(), - env.clone(), - info.clone(), - ExecuteMsg::Transfer { - recipient: acct3, - amount: Uint128::new(333333), - }, - ) - .unwrap(); - execute( - deps.as_mut(), - env, - info, - ExecuteMsg::Transfer { - recipient: acct4, - amount: Uint128::new(444444), - }, - ) - .unwrap(); - - // make sure we get the proper results - let accounts = query_all_accounts(deps.as_ref(), None, None).unwrap(); - assert_eq!(accounts.accounts, expected_order); - - // let's do pagination - let accounts = query_all_accounts(deps.as_ref(), None, Some(2)).unwrap(); - assert_eq!(accounts.accounts, expected_order[0..2].to_vec()); - - let accounts = - query_all_accounts(deps.as_ref(), Some(accounts.accounts[1].clone()), Some(1)).unwrap(); - assert_eq!(accounts.accounts, expected_order[2..3].to_vec()); - - let accounts = - query_all_accounts(deps.as_ref(), Some(accounts.accounts[0].clone()), Some(777)) - .unwrap(); - assert_eq!(accounts.accounts, expected_order[3..].to_vec()); - } -} diff --git a/contracts/cw20-base/src/error.rs b/contracts/cw20-base/src/error.rs deleted file mode 100644 index a0b880c..0000000 --- a/contracts/cw20-base/src/error.rs +++ /dev/null @@ -1,43 +0,0 @@ -use cosmwasm_std::StdError; -use thiserror::Error; - -#[derive(Error, Debug, PartialEq)] -pub enum ContractError { - #[error("{0}")] - Std(#[from] StdError), - - #[error("Unauthorized")] - Unauthorized {}, - - #[error("Cannot set to own account")] - CannotSetOwnAccount {}, - - // Unused error case. Zero is now treated like every other value. - #[deprecated(note = "Unused. All zero amount checks have been removed")] - #[error("Invalid zero amount")] - InvalidZeroAmount {}, - - #[error("Allowance is expired")] - Expired {}, - - #[error("No allowance for this account")] - NoAllowance {}, - - #[error("Minting cannot exceed the cap")] - CannotExceedCap {}, - - #[error("Logo binary data exceeds 5KB limit")] - LogoTooBig {}, - - #[error("Invalid xml preamble for SVG")] - InvalidXmlPreamble {}, - - #[error("Invalid png header")] - InvalidPngHeader {}, - - #[error("Invalid expiration value")] - InvalidExpiration {}, - - #[error("Duplicate initial balance addresses")] - DuplicateInitialBalanceAddresses {}, -} diff --git a/contracts/cw20-base/src/lib.rs b/contracts/cw20-base/src/lib.rs deleted file mode 100644 index 7b601f2..0000000 --- a/contracts/cw20-base/src/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -/*! -This is a basic implementation of a cw20 contract. It implements -the [CW20 spec](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw20/README.md) and is designed to -be deployed as is, or imported into other contracts to easily build -cw20-compatible tokens with custom logic. - -Implements: - -- [x] CW20 Base -- [x] Mintable extension -- [x] Allowances extension - -For more information on this contract, please check out the -[README](https://github.com/CosmWasm/cw-plus/blob/main/contracts/cw20-base/README.md). -*/ - -pub mod allowances; -pub mod contract; -pub mod enumerable; -mod error; -pub mod msg; -pub mod state; - -pub use crate::error::ContractError; diff --git a/contracts/cw20-base/src/msg.rs b/contracts/cw20-base/src/msg.rs deleted file mode 100644 index 2088712..0000000 --- a/contracts/cw20-base/src/msg.rs +++ /dev/null @@ -1,175 +0,0 @@ -use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{StdError, StdResult, Uint128}; -use cw20::{Cw20Coin, Logo, MinterResponse}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -pub use cw20::Cw20ExecuteMsg as ExecuteMsg; - -#[cw_serde] -pub struct InstantiateMarketingInfo { - pub project: Option, - pub description: Option, - pub marketing: Option, - pub logo: Option, -} - -#[cw_serde] -#[cfg_attr(test, derive(Default))] -pub struct InstantiateMsg { - pub name: String, - pub symbol: String, - pub decimals: u8, - pub initial_balances: Vec, - pub mint: Option, - pub marketing: Option, -} - -impl InstantiateMsg { - pub fn get_cap(&self) -> Option { - self.mint.as_ref().and_then(|v| v.cap) - } - - pub fn validate(&self) -> StdResult<()> { - // Check name, symbol, decimals - if !self.has_valid_name() { - return Err(StdError::generic_err( - "Name is not in the expected format (3-50 UTF-8 bytes)", - )); - } - if !self.has_valid_symbol() { - return Err(StdError::generic_err( - "Ticker symbol is not in expected format [a-zA-Z\\-]{3,12}", - )); - } - if self.decimals > 18 { - return Err(StdError::generic_err("Decimals must not exceed 18")); - } - Ok(()) - } - - fn has_valid_name(&self) -> bool { - let bytes = self.name.as_bytes(); - if bytes.len() < 3 || bytes.len() > 50 { - return false; - } - true - } - - fn has_valid_symbol(&self) -> bool { - let bytes = self.symbol.as_bytes(); - if bytes.len() < 3 || bytes.len() > 12 { - return false; - } - for byte in bytes.iter() { - if (*byte != 45) && (*byte < 65 || *byte > 90) && (*byte < 97 || *byte > 122) { - return false; - } - } - true - } -} - -#[cw_serde] -#[derive(QueryResponses)] -pub enum QueryMsg { - /// Returns the current balance of the given address, 0 if unset. - #[returns(cw20::BalanceResponse)] - Balance { address: String }, - /// Returns metadata on the contract - name, decimals, supply, etc. - #[returns(cw20::TokenInfoResponse)] - TokenInfo {}, - /// Only with "mintable" extension. - /// Returns who can mint and the hard cap on maximum tokens after minting. - #[returns(cw20::MinterResponse)] - Minter {}, - /// Only with "allowance" extension. - /// Returns how much spender can use from owner account, 0 if unset. - #[returns(cw20::AllowanceResponse)] - Allowance { owner: String, spender: String }, - /// Only with "enumerable" extension (and "allowances") - /// Returns all allowances this owner has approved. Supports pagination. - #[returns(cw20::AllAllowancesResponse)] - AllAllowances { - owner: String, - start_after: Option, - limit: Option, - }, - /// Only with "enumerable" extension (and "allowances") - /// Returns all allowances this spender has been granted. Supports pagination. - #[returns(cw20::AllSpenderAllowancesResponse)] - AllSpenderAllowances { - spender: String, - start_after: Option, - limit: Option, - }, - /// Only with "enumerable" extension - /// Returns all accounts that have balances. Supports pagination. - #[returns(cw20::AllAccountsResponse)] - AllAccounts { - start_after: Option, - limit: Option, - }, - /// Only with "marketing" extension - /// Returns more metadata on the contract to display in the client: - /// - description, logo, project url, etc. - #[returns(cw20::MarketingInfoResponse)] - MarketingInfo {}, - /// Only with "marketing" extension - /// Downloads the embedded logo data (if stored on chain). Errors if no logo data is stored for this - /// contract. - #[returns(cw20::DownloadLogoResponse)] - DownloadLogo {}, -} - -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct MigrateMsg {} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn validate_instantiatemsg_name() { - // Too short - let mut msg = InstantiateMsg { - name: str::repeat("a", 2), - ..InstantiateMsg::default() - }; - assert!(!msg.has_valid_name()); - - // In the correct length range - msg.name = str::repeat("a", 3); - assert!(msg.has_valid_name()); - - // Too long - msg.name = str::repeat("a", 51); - assert!(!msg.has_valid_name()); - } - - #[test] - fn validate_instantiatemsg_symbol() { - // Too short - let mut msg = InstantiateMsg { - symbol: str::repeat("a", 2), - ..InstantiateMsg::default() - }; - assert!(!msg.has_valid_symbol()); - - // In the correct length range - msg.symbol = str::repeat("a", 3); - assert!(msg.has_valid_symbol()); - - // Too long - msg.symbol = str::repeat("a", 13); - assert!(!msg.has_valid_symbol()); - - // Has illegal char - let illegal_chars = [[64u8], [91u8], [123u8]]; - illegal_chars.iter().for_each(|c| { - let c = std::str::from_utf8(c).unwrap(); - msg.symbol = str::repeat(c, 3); - assert!(!msg.has_valid_symbol()); - }); - } -} diff --git a/contracts/cw20-base/src/state.rs b/contracts/cw20-base/src/state.rs deleted file mode 100644 index dc02c6f..0000000 --- a/contracts/cw20-base/src/state.rs +++ /dev/null @@ -1,36 +0,0 @@ -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Uint128}; -use cw_storage_plus::{Item, Map}; - -use cw20::{AllowanceResponse, Logo, MarketingInfoResponse}; - -#[cw_serde] -pub struct TokenInfo { - pub name: String, - pub symbol: String, - pub decimals: u8, - pub total_supply: Uint128, - pub mint: Option, -} - -#[cw_serde] -pub struct MinterData { - pub minter: Addr, - /// cap is how many more tokens can be issued by the minter - pub cap: Option, -} - -impl TokenInfo { - pub fn get_cap(&self) -> Option { - self.mint.as_ref().and_then(|v| v.cap) - } -} - -pub const TOKEN_INFO: Item = Item::new("token_info"); -pub const MARKETING_INFO: Item = Item::new("marketing_info"); -pub const LOGO: Item = Item::new("logo"); -pub const BALANCES: Map<&Addr, Uint128> = Map::new("balance"); -pub const ALLOWANCES: Map<(&Addr, &Addr), AllowanceResponse> = Map::new("allowance"); -// TODO: After https://github.com/CosmWasm/cw-plus/issues/670 is implemented, replace this with a `MultiIndex` over `ALLOWANCES` -pub const ALLOWANCES_SPENDER: Map<(&Addr, &Addr), AllowanceResponse> = - Map::new("allowance_spender"); diff --git a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml index ea6c3fb..d7c6cff 100644 --- a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml +++ b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml @@ -41,6 +41,7 @@ cw20 = { version = "1.0.1", default-features = false } cw20-base = { version = "1.0.1", features = ["library"] } bytes = "1.0" common = { path = "../../../libraries/rust/common" } +cw-common = { path = "../../cw-common" } hex = "0.4.3" diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index 78d884b..baf6cad 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,16 +1,16 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, Uint128, + Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, Uint128, QueryRequest, WasmQuery, to_binary, Empty }; // use cw2::set_contract_version; use crate::constants::{REPLY_MSG_SUCCESS, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT}; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, XCallMsg}; -use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, XCallMsg, XCallQuery}; +use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS, TEST}; use bytes::Bytes; -use common::btpAddress::BTPAddress; +use cw_common::networkAddress::NetworkAddress; use cw20_base::contract::{execute_burn, execute_mint}; use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; @@ -55,11 +55,21 @@ pub fn instantiate( return Err(ContractError::Std(xcall.err().unwrap())); } let _x_call = &msg.x_call; + let query_message = XCallQuery::GetNetworkAddress { x_call: _x_call.to_string() }; + + let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: _x_call.to_string(), + msg: to_binary(&query_message).map_err(ContractError::Std)?, + }); + + let response: Vec = deps.querier.query(&query).map_err(ContractError::Std)?; + + let x_call_btp_address = String::from_utf8(response).unwrap(); // xCallBTPAddress = ICallService::get_btp_address(&x_call, _env)?; - let (nid, _) = BTPAddress::parse_btp_address(&"aaa")?; - let (hub_net, hub_address) = BTPAddress::parse_network_address(&msg.hub_address)?; + let (nid, _) = NetworkAddress::parse_btp_address(&x_call_btp_address)?; + let (hub_net, hub_address) = NetworkAddress::parse_network_address(&msg.hub_address)?; - X_CALL_BTP_ADDRESS.save(deps.storage, &"sss".to_string())?; + X_CALL_BTP_ADDRESS.save(deps.storage, &x_call_btp_address.to_string())?; NID.save(deps.storage, &nid.to_string())?; HUB_ADDRESS.save(deps.storage, &hub_address.to_string())?; HUB_NET.save(deps.storage, &hub_net.to_string())?; @@ -118,12 +128,15 @@ mod reply { _env: Env, _msg: Reply, ) -> Result { + + Ok(Response::default()) } } mod execute { - use cosmwasm_std::{to_binary, CosmosMsg, SubMsg}; + use cosmwasm_std::{to_binary, CosmosMsg, SubMsg, QueryRequest, WasmQuery, Empty}; + use super::*; @@ -137,11 +150,22 @@ mod execute { deps.api.addr_validate(&x_call).expect("ContractError::InvalidToAddress"); X_CALL.save(deps.storage, &x_call)?; //Network address call remaining + let query_message = XCallQuery::GetNetworkAddress { + x_call: x_call.to_string(), + }; + + let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: x_call, + msg: to_binary(&query_message).map_err(ContractError::Std)?, + }); + + let response: Vec = deps.querier.query(&query).map_err(ContractError::Std)?; + let x_call_btp_address = String::from_utf8(response).unwrap(); // xCallBTPAddress = ICallService::get_btp_address(&x_call, _env)?; - let (nid, _) = BTPAddress::parse_btp_address(&"xCallBTPAddress")?; - let (hub_net, hub_address) = BTPAddress::parse_network_address(&hub_address)?; + let (nid, _) = NetworkAddress::parse_btp_address(&x_call_btp_address)?; + let (hub_net, hub_address) = NetworkAddress::parse_network_address(&hub_address)?; - X_CALL_BTP_ADDRESS.save(deps.storage, &"xCallBTPAddress".to_string())?; + X_CALL_BTP_ADDRESS.save(deps.storage, &x_call_btp_address)?; NID.save(deps.storage, &nid.to_string())?; HUB_ADDRESS.save(deps.storage, &hub_address.to_string())?; HUB_NET.save(deps.storage, &hub_net.to_string())?; @@ -206,7 +230,7 @@ mod execute { let hub_net: String = HUB_NET.load(deps.storage)?; let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let from = BTPAddress::btp_address(&nid, &info.sender.to_string()); + let from = NetworkAddress::btp_address(&nid, &info.sender.to_string()); let _call_data = CrossTransfer { from: from.clone(), @@ -220,7 +244,7 @@ mod execute { value: amount, }; - let _hub_btp_address = BTPAddress::btp_address(&hub_net, &hub_address); + let _hub_btp_address = NetworkAddress::btp_address(&hub_net, &hub_address); let call_message = XCallMsg::SendCallMessage { to: _hub_btp_address, @@ -233,6 +257,7 @@ mod execute { msg: to_binary(&call_message)?, funds, }); + let sub_message = SubMsg::reply_always(wasm_execute_message, REPLY_MSG_SUCCESS); let _result = execute_burn(deps, env, info, amount.into()); match _result { @@ -260,13 +285,13 @@ mod execute { let hub_net: String = HUB_NET.load(deps.storage)?; let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let btp_address = BTPAddress::btp_address(&hub_net, &hub_address); + let btp_address = NetworkAddress::btp_address(&hub_net, &hub_address); if from != btp_address { return Err(ContractError::Unauthorized {}); } - let (net, account) = BTPAddress::parse_network_address(&cross_transfer_data.to)?; + let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_data.to)?; if net != nid { return Err(ContractError::WrongNetwork); } @@ -294,7 +319,7 @@ mod execute { return Err(ContractError::OnlyCallService); } - let (net, account) = BTPAddress::parse_network_address(&cross_transfer_revert_data.from)?; + let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_revert_data.from)?; if net != nid { return Err(ContractError::InvalidBTPAddress); } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs b/contracts/token-contracts/cw-hub-bnusd/src/msg.rs index 279a6a2..b6e4bbd 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/msg.rs @@ -37,10 +37,24 @@ pub enum ExecuteMsg { }, } +#[cw_serde] +struct NetworkAddress { + address: String, +} + #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg {} +#[cw_serde] +#[derive(QueryResponses)] +pub enum XCallQuery { + #[returns(NetworkAddress)] + GetNetworkAddress { + x_call: String, + }, +} + #[cw_serde] pub enum XCallMsg { SendCallMessage { diff --git a/contracts/token-contracts/cw-hub-bnusd/src/state.rs b/contracts/token-contracts/cw-hub-bnusd/src/state.rs index 7fa2b75..946f53a 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/state.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/state.rs @@ -8,6 +8,8 @@ pub const CROSS_CHAIN_SUPPLY: &'static str = "cross_chain_supply"; pub const CROSSCHAINSUPPLY: Map<&String,u128> = Map::new(CROSS_CHAIN_SUPPLY); pub const CONNECTEDCHAINS: Item> = Item::new(CONNECTED_CHAINS); +pub const TEST: Map<&Addr,Map<&Addr,u128>> = Map::new("test"); + // pub const NAME: Item = Item::new("name"); // pub const SYMBOL: Item = Item::new("symbol"); diff --git a/libraries/rust/common/src/btpAddress.rs b/libraries/rust/common/src/btpAddress.rs deleted file mode 100644 index a182c63..0000000 --- a/libraries/rust/common/src/btpAddress.rs +++ /dev/null @@ -1,94 +0,0 @@ -use cosmwasm_std::{StdError, StdResult}; - -pub struct BTPAddress; - -impl BTPAddress { - const PREFIX: &'static [u8] = b"btp://"; - const REVERT: &'static str = "invalidBTPAddress"; - const DELIMITER: &'static [u8] = b"/"; - - pub fn parse_btp_address(_str: &str) -> StdResult<(&str, &str)> { - let offset = BTPAddress::_validate(_str)?; - let network_address = &_str[6..offset]; - let account_address = &_str[offset + 1..]; - Ok((network_address, account_address)) - } - - pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { - let offset = BTPAddress::_validate_network(_str)?; - let network_address = &_str[0..offset]; - let account_address = &_str[offset + 1..]; - Ok((network_address, account_address)) - } - - pub fn network_address(_str: &str) -> StdResult<&str> { - let offset = BTPAddress::_validate(_str)?; - let network_address = &_str[6..offset]; - Ok(network_address) - } - - fn _validate(_str: &str) -> StdResult { - let bytes = _str.as_bytes(); - for (i, &byte) in bytes.iter().enumerate() { - if i < 6 { - if byte != BTPAddress::PREFIX[i] { - return Err(StdError::generic_err(BTPAddress::REVERT)); - } - } else if byte == BTPAddress::DELIMITER[0] { - if i > 6 && i < (bytes.len() - 1) { - return Ok(i); - } else { - return Err(StdError::generic_err(BTPAddress::REVERT)); - } - } - } - Err(StdError::generic_err(BTPAddress::REVERT)) - } - - fn _validate_network(_str: &str) -> StdResult { - let bytes = _str.as_bytes(); - for (i, &byte) in bytes.iter().enumerate() { - if byte == BTPAddress::DELIMITER[0] { - if i < (bytes.len() - 1) { - return Ok(i); - } else { - return Err(StdError::generic_err(BTPAddress::REVERT)); - } - } - } - Err(StdError::generic_err(BTPAddress::REVERT)) - } - - fn _slice(_str: &str, from: usize, to: usize) -> &str { - &_str[from..to] - } - - pub fn btp_address(network: &str, account: &str) -> String { - format!("{:?}{}{:?}{}", BTPAddress::PREFIX, network, BTPAddress::DELIMITER, account) - } -} - -mod tests{ - #[test] - fn test_parse_btp_address() { - let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let (network, account) = super::BTPAddress::parse_btp_address(btp_address).unwrap(); - assert_eq!(network, "0x38.bsc"); - assert_eq!(account, "0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); - } - - #[test] - fn test_parse_network_address() { - let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let (network, account) = super::BTPAddress::parse_network_address(btp_address).unwrap(); - assert_eq!(network, "btp:"); - assert_eq!(account, "/0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); - } - - #[test] - fn test_network_address() { - let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let network = super::BTPAddress::network_address(btp_address).unwrap(); - assert_eq!(network, "0x38.bsc"); - } -} \ No newline at end of file diff --git a/libraries/rust/common/src/lib.rs b/libraries/rust/common/src/lib.rs index fc8a6fd..b6b095d 100644 --- a/libraries/rust/common/src/lib.rs +++ b/libraries/rust/common/src/lib.rs @@ -1,2 +1 @@ -pub mod rlp; -pub mod btpAddress; +pub mod rlp; \ No newline at end of file From 5aa3f8ebf8be969e61dd169e0b586cab3c165564 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Tue, 20 Jun 2023 12:09:55 +0545 Subject: [PATCH 10/37] feat: query to xcall added --- contracts/cw-common/src/lib.rs | 2 +- .../{networkAddress.rs => network_address.rs} | 5 ++++- .../cw-hub-bnusd/src/contract.rs | 19 +++++++++++-------- .../token-contracts/cw-hub-bnusd/src/error.rs | 2 ++ 4 files changed, 18 insertions(+), 10 deletions(-) rename contracts/cw-common/src/{networkAddress.rs => network_address.rs} (93%) diff --git a/contracts/cw-common/src/lib.rs b/contracts/cw-common/src/lib.rs index 5750a01..c288f9a 100644 --- a/contracts/cw-common/src/lib.rs +++ b/contracts/cw-common/src/lib.rs @@ -1 +1 @@ -pub mod networkAddress; +pub mod network_address; diff --git a/contracts/cw-common/src/networkAddress.rs b/contracts/cw-common/src/network_address.rs similarity index 93% rename from contracts/cw-common/src/networkAddress.rs rename to contracts/cw-common/src/network_address.rs index c2fd31d..6c40d29 100644 --- a/contracts/cw-common/src/networkAddress.rs +++ b/contracts/cw-common/src/network_address.rs @@ -9,7 +9,7 @@ impl NetworkAddress { pub fn parse_btp_address(_str: &str) -> StdResult<(&str, &str)> { let offset = NetworkAddress::_validate(_str)?; - let network_address = &_str[6.._str.len()]; + let network_address = &_str[6..offset]; let account_address = &_str[offset + 1..]; Ok((network_address, account_address)) } @@ -29,8 +29,11 @@ impl NetworkAddress { fn _validate(_str: &str) -> StdResult { let bytes = _str.as_bytes(); + print!("{:?}", bytes); for (i, &byte) in bytes.iter().enumerate() { + print!("aa {:?},{} {} \n", byte,i, NetworkAddress::DELIMITER[0]); if i < 6 { + print!("bb {:?},{} \n", byte,NetworkAddress::PREFIX[i]); if byte != NetworkAddress::PREFIX[i] { return Err(StdError::generic_err(NetworkAddress::REVERT)); } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index baf6cad..5aa1bc1 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -7,10 +7,10 @@ use cosmwasm_std::{ use crate::constants::{REPLY_MSG_SUCCESS, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT}; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, XCallMsg, XCallQuery}; -use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS, TEST}; +use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; use bytes::Bytes; -use cw_common::networkAddress::NetworkAddress; +use cw_common::network_address::NetworkAddress; use cw20_base::contract::{execute_burn, execute_mint}; use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; @@ -62,10 +62,12 @@ pub fn instantiate( msg: to_binary(&query_message).map_err(ContractError::Std)?, }); - let response: Vec = deps.querier.query(&query).map_err(ContractError::Std)?; + let x_call_btp_address: String = deps.querier.query(&query).map_err(ContractError::Std)?; + + if x_call_btp_address.is_empty() { + return Err(ContractError::AddressNotFound); + } - let x_call_btp_address = String::from_utf8(response).unwrap(); - // xCallBTPAddress = ICallService::get_btp_address(&x_call, _env)?; let (nid, _) = NetworkAddress::parse_btp_address(&x_call_btp_address)?; let (hub_net, hub_address) = NetworkAddress::parse_network_address(&msg.hub_address)?; @@ -159,9 +161,10 @@ mod execute { msg: to_binary(&query_message).map_err(ContractError::Std)?, }); - let response: Vec = deps.querier.query(&query).map_err(ContractError::Std)?; - let x_call_btp_address = String::from_utf8(response).unwrap(); - // xCallBTPAddress = ICallService::get_btp_address(&x_call, _env)?; + let x_call_btp_address: String = deps.querier.query(&query).map_err(ContractError::Std)?; + if x_call_btp_address.is_empty() { + return Err(ContractError::AddressNotFound); + } let (nid, _) = NetworkAddress::parse_btp_address(&x_call_btp_address)?; let (hub_net, hub_address) = NetworkAddress::parse_network_address(&hub_address)?; diff --git a/contracts/token-contracts/cw-hub-bnusd/src/error.rs b/contracts/token-contracts/cw-hub-bnusd/src/error.rs index a670d6f..8142d4d 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/error.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/error.rs @@ -28,6 +28,8 @@ pub enum ContractError { MintError, #[error("Invalid Reply Data")] InvalidData, + #[error("Address Not Found")] + AddressNotFound, // Add any other custom errors you like here. // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. From a43bd1288fe78121542adf165407ce2b254d9e37 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Tue, 20 Jun 2023 12:19:49 +0545 Subject: [PATCH 11/37] style: msg seperated to cw_common crate --- Cargo.lock | 2 ++ contracts/cw-common/Cargo.toml | 4 +++ .../msg.rs => cw-common/src/hub_token_msg.rs} | 30 ++++--------------- contracts/cw-common/src/lib.rs | 3 ++ .../cw-hub-bnusd => cw-common}/src/types.rs | 6 ++-- contracts/cw-common/src/x_call_msg.rs | 25 ++++++++++++++++ .../cw-hub-bnusd/src/contract.rs | 5 ++-- .../token-contracts/cw-hub-bnusd/src/lib.rs | 2 -- 8 files changed, 45 insertions(+), 32 deletions(-) rename contracts/{token-contracts/cw-hub-bnusd/src/msg.rs => cw-common/src/hub_token_msg.rs} (57%) rename contracts/{token-contracts/cw-hub-bnusd => cw-common}/src/types.rs (93%) create mode 100644 contracts/cw-common/src/x_call_msg.rs diff --git a/Cargo.lock b/Cargo.lock index 3481e17..78b23cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,6 +224,8 @@ name = "cw-common" version = "0.1.0" dependencies = [ "bytes", + "common", + "cosmwasm-schema", "cosmwasm-std", "cw20", "hex", diff --git a/contracts/cw-common/Cargo.toml b/contracts/cw-common/Cargo.toml index dbb526e..790fe42 100644 --- a/contracts/cw-common/Cargo.toml +++ b/contracts/cw-common/Cargo.toml @@ -14,3 +14,7 @@ serde = { version = "1.0.156", default-features = false,features = ["derive"] } hex ={ version = "0.4.3", default-features = false } cosmwasm-std = { version = "1.2.5", default-features = false } rlp = { version = "0.5.2", default-features = false } +cosmwasm-schema = "1.2.6" +common = { path = "../../libraries/rust/common" } + + diff --git a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs b/contracts/cw-common/src/hub_token_msg.rs similarity index 57% rename from contracts/token-contracts/cw-hub-bnusd/src/msg.rs rename to contracts/cw-common/src/hub_token_msg.rs index b6e4bbd..9f07eb3 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -1,7 +1,9 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use crate::types::Types; +use crate::types::types; +pub const X_CROSS_TRANSFER: &str = "XCrossTransfer"; +pub const X_CROSS_TRANSFER_REVERT: &str = "XCrossTransferRevert"; #[cw_serde] pub struct InstantiateMsg { @@ -29,37 +31,15 @@ pub enum ExecuteMsg { }, XCrossTransfer { from: String, - cross_transfer_data: Types::CrossTransfer, + cross_transfer_data: types::CrossTransfer, }, XCrossTransferRevert { from: String, - cross_transfer_revert_data: Types::CrossTransferRevert, + cross_transfer_revert_data: types::CrossTransferRevert, }, } -#[cw_serde] -struct NetworkAddress { - address: String, -} - #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg {} -#[cw_serde] -#[derive(QueryResponses)] -pub enum XCallQuery { - #[returns(NetworkAddress)] - GetNetworkAddress { - x_call: String, - }, -} - -#[cw_serde] -pub enum XCallMsg { - SendCallMessage { - to: String, - data: Vec, - rollback: Option>, - }, -} diff --git a/contracts/cw-common/src/lib.rs b/contracts/cw-common/src/lib.rs index c288f9a..b2a122c 100644 --- a/contracts/cw-common/src/lib.rs +++ b/contracts/cw-common/src/lib.rs @@ -1 +1,4 @@ pub mod network_address; +pub mod hub_token_msg; +pub mod types; +pub mod x_call_msg; \ No newline at end of file diff --git a/contracts/token-contracts/cw-hub-bnusd/src/types.rs b/contracts/cw-common/src/types.rs similarity index 93% rename from contracts/token-contracts/cw-hub-bnusd/src/types.rs rename to contracts/cw-common/src/types.rs index 6a0ff7d..47b2785 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/types.rs +++ b/contracts/cw-common/src/types.rs @@ -1,7 +1,7 @@ use common::rlp::{RlpStream}; use cosmwasm_schema::{cw_serde}; -pub mod Types { +pub mod types { use super::*; #[cw_serde] @@ -19,7 +19,7 @@ pub mod Types { } } -impl Types::CrossTransfer { +impl types::CrossTransfer { pub fn encode_cross_transfer_message(self) -> Vec { let method = "xCrossTransfer"; @@ -35,7 +35,7 @@ impl Types::CrossTransfer { } } -impl Types::CrossTransferRevert { +impl types::CrossTransferRevert { pub fn encode_cross_transfer_revert_message(self) -> Vec { let method = "xCrossTransferRevert"; diff --git a/contracts/cw-common/src/x_call_msg.rs b/contracts/cw-common/src/x_call_msg.rs new file mode 100644 index 0000000..5c484ef --- /dev/null +++ b/contracts/cw-common/src/x_call_msg.rs @@ -0,0 +1,25 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; + + +#[cw_serde] +struct NetworkAddress { + address: String, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum XCallQuery { + #[returns(NetworkAddress)] + GetNetworkAddress { + x_call: String, + }, +} + +#[cw_serde] +pub enum XCallMsg { + SendCallMessage { + to: String, + data: Vec, + rollback: Option>, + }, +} \ No newline at end of file diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index 5aa1bc1..8080b24 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -6,7 +6,8 @@ use cosmwasm_std::{ // use cw2::set_contract_version; use crate::constants::{REPLY_MSG_SUCCESS, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT}; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, XCallMsg, XCallQuery}; +use cw_common::hub_token_msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cw_common::x_call_msg::{XCallMsg, XCallQuery}; use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; use bytes::Bytes; @@ -16,7 +17,7 @@ use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; use common::rlp::{DecoderError, Rlp}; -use crate::types::Types::{CrossTransfer, CrossTransferRevert}; +use cw_common::types::types::{CrossTransfer, CrossTransferRevert}; /* // version info for migration info diff --git a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs index 4992f5a..cd79de5 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs @@ -1,9 +1,7 @@ pub mod contract; mod error; pub mod helpers; -pub mod msg; pub mod state; pub mod constants; -pub mod types; pub use crate::error::ContractError; From 1c0b0f62d256d5cddb4f390e2bf5eab204aa5681 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Tue, 20 Jun 2023 12:28:26 +0545 Subject: [PATCH 12/37] fix: cargo fmt changes --- contracts/cw-common/src/hub_token_msg.rs | 1 - contracts/cw-common/src/lib.rs | 4 +- contracts/cw-common/src/network_address.rs | 20 +++-- contracts/cw-common/src/types.rs | 5 +- contracts/cw-common/src/x_call_msg.rs | 7 +- .../cw-hub-bnusd/src/contract.rs | 84 +++++++++++++------ .../token-contracts/cw-hub-bnusd/src/error.rs | 1 - .../token-contracts/cw-hub-bnusd/src/lib.rs | 2 +- .../token-contracts/cw-hub-bnusd/src/state.rs | 16 ++-- libraries/rust/common/src/lib.rs | 2 +- libraries/rust/common/src/rlp/mod.rs | 1 - 11 files changed, 84 insertions(+), 59 deletions(-) diff --git a/contracts/cw-common/src/hub_token_msg.rs b/contracts/cw-common/src/hub_token_msg.rs index 9f07eb3..aae01b8 100644 --- a/contracts/cw-common/src/hub_token_msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -42,4 +42,3 @@ pub enum ExecuteMsg { #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg {} - diff --git a/contracts/cw-common/src/lib.rs b/contracts/cw-common/src/lib.rs index b2a122c..d353ec1 100644 --- a/contracts/cw-common/src/lib.rs +++ b/contracts/cw-common/src/lib.rs @@ -1,4 +1,4 @@ -pub mod network_address; pub mod hub_token_msg; +pub mod network_address; pub mod types; -pub mod x_call_msg; \ No newline at end of file +pub mod x_call_msg; diff --git a/contracts/cw-common/src/network_address.rs b/contracts/cw-common/src/network_address.rs index 6c40d29..a6ab19d 100644 --- a/contracts/cw-common/src/network_address.rs +++ b/contracts/cw-common/src/network_address.rs @@ -29,11 +29,8 @@ impl NetworkAddress { fn _validate(_str: &str) -> StdResult { let bytes = _str.as_bytes(); - print!("{:?}", bytes); for (i, &byte) in bytes.iter().enumerate() { - print!("aa {:?},{} {} \n", byte,i, NetworkAddress::DELIMITER[0]); if i < 6 { - print!("bb {:?},{} \n", byte,NetworkAddress::PREFIX[i]); if byte != NetworkAddress::PREFIX[i] { return Err(StdError::generic_err(NetworkAddress::REVERT)); } @@ -67,11 +64,17 @@ impl NetworkAddress { } pub fn btp_address(network: &str, account: &str) -> String { - format!("{:?}{}{:?}{}", NetworkAddress::PREFIX, network, NetworkAddress::DELIMITER, account) + format!( + "{:?}{}{:?}{}", + NetworkAddress::PREFIX, + network, + NetworkAddress::DELIMITER, + account + ) } } -mod tests{ +mod tests { #[test] fn test_parse_btp_address() { let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; @@ -85,7 +88,10 @@ mod tests{ let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; let (network, account) = super::NetworkAddress::parse_network_address(btp_address).unwrap(); assert_eq!(network, "btp:"); - assert_eq!(account, "/0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); + assert_eq!( + account, + "/0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798" + ); } #[test] @@ -94,4 +100,4 @@ mod tests{ let network = super::NetworkAddress::network_address(btp_address).unwrap(); assert_eq!(network, "0x38.bsc"); } -} \ No newline at end of file +} diff --git a/contracts/cw-common/src/types.rs b/contracts/cw-common/src/types.rs index 47b2785..6c90a4e 100644 --- a/contracts/cw-common/src/types.rs +++ b/contracts/cw-common/src/types.rs @@ -1,5 +1,5 @@ -use common::rlp::{RlpStream}; -use cosmwasm_schema::{cw_serde}; +use common::rlp::RlpStream; +use cosmwasm_schema::cw_serde; pub mod types { use super::*; @@ -48,4 +48,3 @@ impl types::CrossTransferRevert { encoded } } - \ No newline at end of file diff --git a/contracts/cw-common/src/x_call_msg.rs b/contracts/cw-common/src/x_call_msg.rs index 5c484ef..2d72305 100644 --- a/contracts/cw-common/src/x_call_msg.rs +++ b/contracts/cw-common/src/x_call_msg.rs @@ -1,6 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; - #[cw_serde] struct NetworkAddress { address: String, @@ -10,9 +9,7 @@ struct NetworkAddress { #[derive(QueryResponses)] pub enum XCallQuery { #[returns(NetworkAddress)] - GetNetworkAddress { - x_call: String, - }, + GetNetworkAddress { x_call: String }, } #[cw_serde] @@ -22,4 +19,4 @@ pub enum XCallMsg { data: Vec, rollback: Option>, }, -} \ No newline at end of file +} diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index 8080b24..b2d8fae 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,19 +1,20 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, Uint128, QueryRequest, WasmQuery, to_binary, Empty + to_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryRequest, Reply, Response, + StdResult, Uint128, WasmQuery, }; // use cw2::set_contract_version; use crate::constants::{REPLY_MSG_SUCCESS, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT}; use crate::error::ContractError; -use cw_common::hub_token_msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use cw_common::x_call_msg::{XCallMsg, XCallQuery}; use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; use bytes::Bytes; +use cw_common::hub_token_msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cw_common::x_call_msg::{XCallMsg, XCallQuery}; -use cw_common::network_address::NetworkAddress; use cw20_base::contract::{execute_burn, execute_mint}; use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; +use cw_common::network_address::NetworkAddress; use common::rlp::{DecoderError, Rlp}; @@ -50,13 +51,17 @@ pub fn instantiate( if save_token.is_err() { return Err(ContractError::Std(save_token.err().unwrap())); } - deps.api.addr_validate(&msg.x_call).expect("ContractError::InvalidToAddress"); - let xcall=X_CALL.save(deps.storage, &msg.x_call); + deps.api + .addr_validate(&msg.x_call) + .expect("ContractError::InvalidToAddress"); + let xcall = X_CALL.save(deps.storage, &msg.x_call); if xcall.is_err() { return Err(ContractError::Std(xcall.err().unwrap())); } let _x_call = &msg.x_call; - let query_message = XCallQuery::GetNetworkAddress { x_call: _x_call.to_string() }; + let query_message = XCallQuery::GetNetworkAddress { + x_call: _x_call.to_string(), + }; let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { contract_addr: _x_call.to_string(), @@ -66,7 +71,7 @@ pub fn instantiate( let x_call_btp_address: String = deps.querier.query(&query).map_err(ContractError::Std)?; if x_call_btp_address.is_empty() { - return Err(ContractError::AddressNotFound); + return Err(ContractError::AddressNotFound); } let (nid, _) = NetworkAddress::parse_btp_address(&x_call_btp_address)?; @@ -131,15 +136,12 @@ mod reply { _env: Env, _msg: Reply, ) -> Result { - - Ok(Response::default()) } } mod execute { - use cosmwasm_std::{to_binary, CosmosMsg, SubMsg, QueryRequest, WasmQuery, Empty}; - + use cosmwasm_std::{to_binary, CosmosMsg, Empty, QueryRequest, SubMsg, WasmQuery}; use super::*; @@ -150,7 +152,9 @@ mod execute { x_call: String, hub_address: String, ) -> Result { - deps.api.addr_validate(&x_call).expect("ContractError::InvalidToAddress"); + deps.api + .addr_validate(&x_call) + .expect("ContractError::InvalidToAddress"); X_CALL.save(deps.storage, &x_call)?; //Network address call remaining let query_message = XCallQuery::GetNetworkAddress { @@ -164,7 +168,7 @@ mod execute { let x_call_btp_address: String = deps.querier.query(&query).map_err(ContractError::Std)?; if x_call_btp_address.is_empty() { - return Err(ContractError::AddressNotFound); + return Err(ContractError::AddressNotFound); } let (nid, _) = NetworkAddress::parse_btp_address(&x_call_btp_address)?; let (hub_net, hub_address) = NetworkAddress::parse_network_address(&hub_address)?; @@ -184,7 +188,9 @@ mod execute { from: String, data: Vec, ) -> Result { - deps.api.addr_validate(&from).expect("ContractError::InvalidToAddress"); + deps.api + .addr_validate(&from) + .expect("ContractError::InvalidToAddress"); let rlp: Rlp = Rlp::new(&data); let data: Result, DecoderError> = rlp.as_list(); match data { @@ -228,7 +234,9 @@ mod execute { data: Bytes, ) -> Result { use super::*; - deps.api.addr_validate(&to).expect("ContractError::InvalidToAddress"); + deps.api + .addr_validate(&to) + .expect("ContractError::InvalidToAddress"); let funds = info.funds.clone(); let nid = NID.load(deps.storage)?; let hub_net: String = HUB_NET.load(deps.storage)?; @@ -284,7 +292,9 @@ mod execute { from: String, cross_transfer_data: CrossTransfer, ) -> Result { - deps.api.addr_validate(&from).expect("ContractError::InvalidToAddress"); + deps.api + .addr_validate(&from) + .expect("ContractError::InvalidToAddress"); let nid = NID.load(deps.storage)?; let hub_net: String = HUB_NET.load(deps.storage)?; let hub_address: String = HUB_ADDRESS.load(deps.storage)?; @@ -300,10 +310,19 @@ mod execute { return Err(ContractError::WrongNetwork); } - let _to = deps.api.addr_validate(&account).expect("ContractError::InvalidToAddress"); + let _to = deps + .api + .addr_validate(&account) + .expect("ContractError::InvalidToAddress"); - let res = execute_mint(deps, env, info, account.to_string(), cross_transfer_data.value.into()) - .expect("Fail to mint"); + let res = execute_mint( + deps, + env, + info, + account.to_string(), + cross_transfer_data.value.into(), + ) + .expect("Fail to mint"); Ok(res) } @@ -315,7 +334,9 @@ mod execute { from: String, cross_transfer_revert_data: CrossTransferRevert, ) -> Result { - deps.api.addr_validate(&from).expect("ContractError::InvalidToAddress"); + deps.api + .addr_validate(&from) + .expect("ContractError::InvalidToAddress"); let nid = NID.load(deps.storage)?; let x_call_btp_address = X_CALL_BTP_ADDRESS.load(deps.storage)?; @@ -323,16 +344,25 @@ mod execute { return Err(ContractError::OnlyCallService); } - let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_revert_data.from)?; + let (net, account) = + NetworkAddress::parse_network_address(&cross_transfer_revert_data.from)?; if net != nid { return Err(ContractError::InvalidBTPAddress); } - let _to = deps.api.addr_validate(&account).expect("ContractError::InvalidToAddress"); - - - let res = execute_mint(deps, env, info, account.to_string(), cross_transfer_revert_data.value.into()) - .expect("Fail to mint"); + let _to = deps + .api + .addr_validate(&account) + .expect("ContractError::InvalidToAddress"); + + let res = execute_mint( + deps, + env, + info, + account.to_string(), + cross_transfer_revert_data.value.into(), + ) + .expect("Fail to mint"); Ok(res) } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/error.rs b/contracts/token-contracts/cw-hub-bnusd/src/error.rs index 8142d4d..59a510e 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/error.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/error.rs @@ -30,7 +30,6 @@ pub enum ContractError { InvalidData, #[error("Address Not Found")] AddressNotFound, - // Add any other custom errors you like here. // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs index cd79de5..e759573 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs @@ -1,7 +1,7 @@ +pub mod constants; pub mod contract; mod error; pub mod helpers; pub mod state; -pub mod constants; pub use crate::error::ContractError; diff --git a/contracts/token-contracts/cw-hub-bnusd/src/state.rs b/contracts/token-contracts/cw-hub-bnusd/src/state.rs index 946f53a..4400340 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/state.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/state.rs @@ -5,12 +5,9 @@ pub const CONNECTED_CHAINS: &'static str = "connected_chains"; pub const SPOKE_CONTRACTS: &'static str = "spoke_contract"; pub const CROSS_CHAIN_SUPPLY: &'static str = "cross_chain_supply"; -pub const CROSSCHAINSUPPLY: Map<&String,u128> = Map::new(CROSS_CHAIN_SUPPLY); +pub const CROSSCHAINSUPPLY: Map<&String, u128> = Map::new(CROSS_CHAIN_SUPPLY); pub const CONNECTEDCHAINS: Item> = Item::new(CONNECTED_CHAINS); -pub const TEST: Map<&Addr,Map<&Addr,u128>> = Map::new("test"); - - // pub const NAME: Item = Item::new("name"); // pub const SYMBOL: Item = Item::new("symbol"); // pub const DECIMAL: Item = Item::new("decimal"); @@ -21,9 +18,8 @@ pub const OWNER: Item = Item::new("owner"); // const ZERO_ADDRESSES: &'static str = "0000000000000000000000000000000000000000"; -pub const X_CALL : Item = Item::new("xCall"); -pub const X_CALL_BTP_ADDRESS : Item = Item::new("xCallBTPAddress"); -pub const NID : Item = Item::new("nid"); -pub const HUB_ADDRESS : Item = Item::new("hubAddress"); -pub const HUB_NET : Item = Item::new("hubNet"); - +pub const X_CALL: Item = Item::new("xCall"); +pub const X_CALL_BTP_ADDRESS: Item = Item::new("xCallBTPAddress"); +pub const NID: Item = Item::new("nid"); +pub const HUB_ADDRESS: Item = Item::new("hubAddress"); +pub const HUB_NET: Item = Item::new("hubNet"); diff --git a/libraries/rust/common/src/lib.rs b/libraries/rust/common/src/lib.rs index b6b095d..c465d3b 100644 --- a/libraries/rust/common/src/lib.rs +++ b/libraries/rust/common/src/lib.rs @@ -1 +1 @@ -pub mod rlp; \ No newline at end of file +pub mod rlp; diff --git a/libraries/rust/common/src/rlp/mod.rs b/libraries/rust/common/src/rlp/mod.rs index cb971d0..d2d1b67 100644 --- a/libraries/rust/common/src/rlp/mod.rs +++ b/libraries/rust/common/src/rlp/mod.rs @@ -1,4 +1,3 @@ - #![allow(unused)] // Copyright 2020 Parity Technologies // From e4eb61595a9e18c961ede0eca27ed8fcd6b90fb2 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Tue, 20 Jun 2023 12:38:37 +0545 Subject: [PATCH 13/37] fix: cargo clippy issues --- .../cw-common/src/{types.rs => data_types.rs} | 0 contracts/cw-common/src/hub_token_msg.rs | 2 +- contracts/cw-common/src/lib.rs | 2 +- .../cw-hub-bnusd/src/contract.rs | 18 +++++++++--------- .../token-contracts/cw-hub-bnusd/src/state.rs | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) rename contracts/cw-common/src/{types.rs => data_types.rs} (100%) diff --git a/contracts/cw-common/src/types.rs b/contracts/cw-common/src/data_types.rs similarity index 100% rename from contracts/cw-common/src/types.rs rename to contracts/cw-common/src/data_types.rs diff --git a/contracts/cw-common/src/hub_token_msg.rs b/contracts/cw-common/src/hub_token_msg.rs index aae01b8..2d007a4 100644 --- a/contracts/cw-common/src/hub_token_msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use crate::types::types; +use crate::data_types::types; pub const X_CROSS_TRANSFER: &str = "XCrossTransfer"; pub const X_CROSS_TRANSFER_REVERT: &str = "XCrossTransferRevert"; diff --git a/contracts/cw-common/src/lib.rs b/contracts/cw-common/src/lib.rs index d353ec1..2ff8468 100644 --- a/contracts/cw-common/src/lib.rs +++ b/contracts/cw-common/src/lib.rs @@ -1,4 +1,4 @@ pub mod hub_token_msg; pub mod network_address; -pub mod types; +pub mod data_types; pub mod x_call_msg; diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index b2d8fae..740a37b 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -18,7 +18,7 @@ use cw_common::network_address::NetworkAddress; use common::rlp::{DecoderError, Rlp}; -use cw_common::types::types::{CrossTransfer, CrossTransferRevert}; +use cw_common::data_types::types::{CrossTransfer, CrossTransferRevert}; /* // version info for migration info @@ -242,12 +242,12 @@ mod execute { let hub_net: String = HUB_NET.load(deps.storage)?; let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let from = NetworkAddress::btp_address(&nid, &info.sender.to_string()); + let from = NetworkAddress::btp_address(&nid, info.sender.as_ref()); let _call_data = CrossTransfer { from: from.clone(), - to: to.to_string().clone(), - value: amount.clone(), + to, + value: amount, data: data.to_vec(), }; @@ -312,7 +312,7 @@ mod execute { let _to = deps .api - .addr_validate(&account) + .addr_validate(account) .expect("ContractError::InvalidToAddress"); let res = execute_mint( @@ -352,7 +352,7 @@ mod execute { let _to = deps .api - .addr_validate(&account) + .addr_validate(account) .expect("ContractError::InvalidToAddress"); let res = execute_mint( @@ -370,7 +370,7 @@ mod execute { mod rlpdecode_struct { use super::*; - pub fn decode_cross_transfer(ls: &Vec) -> CrossTransfer { + pub fn decode_cross_transfer(ls: &[String]) -> CrossTransfer { CrossTransfer { from: ls[1].clone(), to: ls[2].clone(), @@ -379,9 +379,9 @@ mod rlpdecode_struct { } } - pub fn decode_cross_transfer_revert(ls: &Vec) -> CrossTransferRevert { + pub fn decode_cross_transfer_revert(ls: &[String]) -> CrossTransferRevert { CrossTransferRevert { - from: ls[1].clone().into(), + from: ls[1].clone(), value: ls[2].parse::().unwrap_or_default(), } } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/state.rs b/contracts/token-contracts/cw-hub-bnusd/src/state.rs index 4400340..1631e49 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/state.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/state.rs @@ -1,9 +1,9 @@ use cosmwasm_std::Addr; use cw_storage_plus::{Item, Map}; -pub const CONNECTED_CHAINS: &'static str = "connected_chains"; -pub const SPOKE_CONTRACTS: &'static str = "spoke_contract"; -pub const CROSS_CHAIN_SUPPLY: &'static str = "cross_chain_supply"; +pub const CONNECTED_CHAINS: &str = "connected_chains"; +pub const SPOKE_CONTRACTS: &str = "spoke_contract"; +pub const CROSS_CHAIN_SUPPLY: &str = "cross_chain_supply"; pub const CROSSCHAINSUPPLY: Map<&String, u128> = Map::new(CROSS_CHAIN_SUPPLY); pub const CONNECTEDCHAINS: Item> = Item::new(CONNECTED_CHAINS); From 6f1bbe7de0b5047a1aa9e222417e937a7a544054 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Tue, 20 Jun 2023 12:47:22 +0545 Subject: [PATCH 14/37] fix: cargo fmt issues --- contracts/cw-common/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/cw-common/src/lib.rs b/contracts/cw-common/src/lib.rs index 2ff8468..227e794 100644 --- a/contracts/cw-common/src/lib.rs +++ b/contracts/cw-common/src/lib.rs @@ -1,4 +1,4 @@ +pub mod data_types; pub mod hub_token_msg; pub mod network_address; -pub mod data_types; pub mod x_call_msg; From 4c878f47d39d6a5e504fa67a15c30bcf83ae6599 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Thu, 22 Jun 2023 09:53:10 +0545 Subject: [PATCH 15/37] fix: hub token instantiate msg fixed --- contracts/cw-common/src/hub_token_msg.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/contracts/cw-common/src/hub_token_msg.rs b/contracts/cw-common/src/hub_token_msg.rs index 2d007a4..8949d8f 100644 --- a/contracts/cw-common/src/hub_token_msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -9,9 +9,6 @@ pub const X_CROSS_TRANSFER_REVERT: &str = "XCrossTransferRevert"; pub struct InstantiateMsg { pub x_call: String, pub hub_address: String, - pub name: String, - pub symbol: String, - pub decimals: u8, } #[cw_serde] From 68247d2f642fa840baa7526ef1373815b18ba80e Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Fri, 23 Jun 2023 15:02:43 +0545 Subject: [PATCH 16/37] fix: changes after review --- Cargo.lock | 17 +++++++++++++++++ Cargo.toml | 2 ++ contracts/cw-common/src/data_types.rs | 2 +- contracts/cw-common/src/hub_token_msg.rs | 8 ++++---- contracts/cw-common/src/x_call_msg.rs | 5 ++++- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78b23cf..d60f052 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,6 +254,7 @@ dependencies = [ "schemars", "serde", "thiserror", + "x-call-mock", ] [[package]] @@ -901,6 +902,22 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "x-call-mock" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-common", + "cw-multi-test", + "cw-storage-plus", + "cw2", + "schemars", + "serde", + "thiserror", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 408269d..873d0e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,8 @@ members = [ # "contracts/core-contracts/*", "contracts/token-contracts/cw-hub-bnusd", + "contracts/cw-common", + "contracts/mock-contracts/x-call-mock", "libraries/rust/common",] diff --git a/contracts/cw-common/src/data_types.rs b/contracts/cw-common/src/data_types.rs index 6c90a4e..a53736a 100644 --- a/contracts/cw-common/src/data_types.rs +++ b/contracts/cw-common/src/data_types.rs @@ -23,7 +23,7 @@ impl types::CrossTransfer { pub fn encode_cross_transfer_message(self) -> Vec { let method = "xCrossTransfer"; - let mut calldata = RlpStream::new_list(4); + let mut calldata = RlpStream::new_list(5); calldata.append(&method.to_string()); calldata.append(&self.from); calldata.append(&self.to); diff --git a/contracts/cw-common/src/hub_token_msg.rs b/contracts/cw-common/src/hub_token_msg.rs index 8949d8f..b3d5d85 100644 --- a/contracts/cw-common/src/hub_token_msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -14,12 +14,12 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { Setup { - _x_call: String, - _hub_address: String, + x_call: String, + hub_address: String, }, HandleCallMessage { - _from: String, - _data: Vec, + from: String, + data: Vec, }, CrossTransfer { to: String, diff --git a/contracts/cw-common/src/x_call_msg.rs b/contracts/cw-common/src/x_call_msg.rs index 2d72305..55ad988 100644 --- a/contracts/cw-common/src/x_call_msg.rs +++ b/contracts/cw-common/src/x_call_msg.rs @@ -5,11 +5,14 @@ struct NetworkAddress { address: String, } +#[cw_serde] +pub struct InstantiateMsg {} + #[cw_serde] #[derive(QueryResponses)] pub enum XCallQuery { #[returns(NetworkAddress)] - GetNetworkAddress { x_call: String }, + GetNetworkAddress {}, } #[cw_serde] From 866330dfc376cb7d0a30226251825172571b1261 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Fri, 23 Jun 2023 15:04:46 +0545 Subject: [PATCH 17/37] feat(network_address): implementation changed --- contracts/cw-common/src/network_address.rs | 120 ++++++++++----------- 1 file changed, 54 insertions(+), 66 deletions(-) diff --git a/contracts/cw-common/src/network_address.rs b/contracts/cw-common/src/network_address.rs index a6ab19d..86f0f3b 100644 --- a/contracts/cw-common/src/network_address.rs +++ b/contracts/cw-common/src/network_address.rs @@ -1,74 +1,37 @@ -use cosmwasm_std::{StdError, StdResult}; +use cosmwasm_std::{StdResult}; pub struct NetworkAddress; impl NetworkAddress { - const PREFIX: &'static [u8] = b"btp://"; - const REVERT: &'static str = "invalidNetworkAddress"; - const DELIMITER: &'static [u8] = b"/"; - - pub fn parse_btp_address(_str: &str) -> StdResult<(&str, &str)> { - let offset = NetworkAddress::_validate(_str)?; - let network_address = &_str[6..offset]; - let account_address = &_str[offset + 1..]; - Ok((network_address, account_address)) - } - - pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { - let offset = NetworkAddress::_validate_network(_str)?; - let network_address = &_str[0..offset]; - let account_address = &_str[offset + 1..]; - Ok((network_address, account_address)) - } - - pub fn network_address(_str: &str) -> StdResult<&str> { - let offset = NetworkAddress::_validate(_str)?; - let network_address = &_str[6..offset]; - Ok(network_address) - } - - fn _validate(_str: &str) -> StdResult { - let bytes = _str.as_bytes(); - for (i, &byte) in bytes.iter().enumerate() { - if i < 6 { - if byte != NetworkAddress::PREFIX[i] { - return Err(StdError::generic_err(NetworkAddress::REVERT)); - } - } else if byte == NetworkAddress::DELIMITER[0] { - if i > 6 && i < (bytes.len() - 1) { - return Ok(i); - } else { - return Err(StdError::generic_err(NetworkAddress::REVERT)); - } - } - } - Err(StdError::generic_err(NetworkAddress::REVERT)) - } - - fn _validate_network(_str: &str) -> StdResult { - let bytes = _str.as_bytes(); - for (i, &byte) in bytes.iter().enumerate() { - if byte == NetworkAddress::DELIMITER[0] { - if i < (bytes.len() - 1) { - return Ok(i); - } else { - return Err(StdError::generic_err(NetworkAddress::REVERT)); - } - } - } - Err(StdError::generic_err(NetworkAddress::REVERT)) - } + +pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { + let mut iter = _str.splitn(2, "://"); + let _ = iter.next().unwrap_or(""); + let mut account = iter.next().unwrap_or("").splitn(2, "/"); + let network = account.next().unwrap_or(""); + let address = account.next().unwrap_or(""); + Ok((network, address)) +} - fn _slice(_str: &str, from: usize, to: usize) -> &str { - &_str[from..to] - } +pub fn parse_protocol_address(_str: &str) -> StdResult<(&str, &str)> { +let mut iter = _str.splitn(2, "://"); +let protocol = iter.next().unwrap_or(""); +let account = iter.next().unwrap_or(""); +Ok((protocol, account)) +} - pub fn btp_address(network: &str, account: &str) -> String { +pub fn protocol_address(_str: &str) -> StdResult<&str> { + let mut iter = _str.splitn(2, "://"); + let _ = iter.next().unwrap_or(""); + let mut address = iter.next().unwrap_or("").splitn(2, "/"); + let network = address.next().unwrap_or(""); + Ok(network) +} + pub fn get_network_address(protocol: &str,network: &str, account: &str) -> String { format!( - "{:?}{}{:?}{}", - NetworkAddress::PREFIX, + "{}://{}/{}", + protocol, network, - NetworkAddress::DELIMITER, account ) } @@ -78,7 +41,7 @@ mod tests { #[test] fn test_parse_btp_address() { let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let (network, account) = super::NetworkAddress::parse_btp_address(btp_address).unwrap(); + let (network, account) = super::NetworkAddress::parse_network_address(btp_address).unwrap(); assert_eq!(network, "0x38.bsc"); assert_eq!(account, "0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); } @@ -86,7 +49,7 @@ mod tests { #[test] fn test_parse_network_address() { let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let (network, account) = super::NetworkAddress::parse_network_address(btp_address).unwrap(); + let (network, account) = super::NetworkAddress::parse_protocol_address(btp_address).unwrap(); assert_eq!(network, "btp:"); assert_eq!( account, @@ -97,7 +60,32 @@ mod tests { #[test] fn test_network_address() { let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let network = super::NetworkAddress::network_address(btp_address).unwrap(); + let network = super::NetworkAddress::protocol_address(btp_address).unwrap(); assert_eq!(network, "0x38.bsc"); } } + + +// pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { +// let mut iter = _str.splitn(2, "://"); +// let protocol = iter.next().unwrap_or(""); +// let mut account = iter.next().unwrap_or("").splitn(2, "/"); +// let network = account.next().unwrap_or(""); +// let address = account.next().unwrap_or(""); +// Ok((network, address)) +// } + +// pub fn parse_protocol_address(_str: &str) -> StdResult<(&str, &str)> { +// let mut iter = _str.splitn(2, "://"); +// let protocol = iter.next().unwrap_or(""); +// let account = iter.next().unwrap_or(""); +// Ok((protocol, account)) +// } + +// pub fn protocol_address(_str: &str) -> StdResult<&str> { +// let mut iter = _str.splitn(2, "://"); +// let _ = iter.next().unwrap_or(""); +// let mut address = iter.next().unwrap_or("").splitn(2, "/"); +// let network = address.next().unwrap_or(""); +// Ok(network) +// } \ No newline at end of file From 6f24771006ced5eae49b52a899a9054a1124d001 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Fri, 23 Jun 2023 15:07:56 +0545 Subject: [PATCH 18/37] fix: clippy added to pre_commit script --- scripts/pre_commit.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pre_commit.sh b/scripts/pre_commit.sh index a697d68..bf79581 100755 --- a/scripts/pre_commit.sh +++ b/scripts/pre_commit.sh @@ -2,6 +2,6 @@ set -e cargo fmt --all -#cargo clippy --fix +cargo clippy --fix source ./scripts/run_in_subprojects.sh ./contracts/token-contracts/cw-hub-bnusd -cargo clean +# cargo clean From 52b531ff77092076cc37ddd7e6642c600b2481ab Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Fri, 23 Jun 2023 15:36:47 +0545 Subject: [PATCH 19/37] fix: xcall query fixed according to the change in msg --- .../cw-hub-bnusd/src/contract.rs | 107 ++++++++++++++---- 1 file changed, 86 insertions(+), 21 deletions(-) diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index 740a37b..e5736ed 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,8 +1,7 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryRequest, Reply, Response, - StdResult, Uint128, WasmQuery, + to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, Uint128, QueryRequest, WasmQuery, Empty, }; // use cw2::set_contract_version; use crate::constants::{REPLY_MSG_SUCCESS, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT}; @@ -46,7 +45,6 @@ pub fn instantiate( cap: None, }), }; - let save_token = TOKEN_INFO.save(deps.storage, &data); if save_token.is_err() { return Err(ContractError::Std(save_token.err().unwrap())); @@ -60,7 +58,6 @@ pub fn instantiate( } let _x_call = &msg.x_call; let query_message = XCallQuery::GetNetworkAddress { - x_call: _x_call.to_string(), }; let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { @@ -70,12 +67,12 @@ pub fn instantiate( let x_call_btp_address: String = deps.querier.query(&query).map_err(ContractError::Std)?; + // let x_call_btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; if x_call_btp_address.is_empty() { return Err(ContractError::AddressNotFound); } - - let (nid, _) = NetworkAddress::parse_btp_address(&x_call_btp_address)?; - let (hub_net, hub_address) = NetworkAddress::parse_network_address(&msg.hub_address)?; + let (nid, _) = NetworkAddress::parse_network_address(&x_call_btp_address)?; + let (hub_net, hub_address) = NetworkAddress::parse_protocol_address(&msg.hub_address)?; X_CALL_BTP_ADDRESS.save(deps.storage, &x_call_btp_address.to_string())?; NID.save(deps.storage, &nid.to_string())?; @@ -95,11 +92,11 @@ pub fn execute( use ExecuteMsg::*; match msg { Setup { - _x_call, - _hub_address, - } => execute::setup(deps, _env, info, _x_call, _hub_address), - HandleCallMessage { _from, _data } => { - execute::handle_call_message(deps, _env, info, _from, _data) + x_call, + hub_address, + } => execute::setup(deps, _env, info, x_call, hub_address), + HandleCallMessage { from, data } => { + execute::handle_call_message(deps, _env, info, from, data) } CrossTransfer { to, amount, data } => { execute::cross_transfer(deps, _env, info, to, amount, data.into()) @@ -158,7 +155,6 @@ mod execute { X_CALL.save(deps.storage, &x_call)?; //Network address call remaining let query_message = XCallQuery::GetNetworkAddress { - x_call: x_call.to_string(), }; let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { @@ -170,8 +166,8 @@ mod execute { if x_call_btp_address.is_empty() { return Err(ContractError::AddressNotFound); } - let (nid, _) = NetworkAddress::parse_btp_address(&x_call_btp_address)?; - let (hub_net, hub_address) = NetworkAddress::parse_network_address(&hub_address)?; + let (nid, _) = NetworkAddress::parse_network_address(&x_call_btp_address)?; + let (hub_net, hub_address) = NetworkAddress::parse_protocol_address(&hub_address)?; X_CALL_BTP_ADDRESS.save(deps.storage, &x_call_btp_address)?; NID.save(deps.storage, &nid.to_string())?; @@ -242,7 +238,7 @@ mod execute { let hub_net: String = HUB_NET.load(deps.storage)?; let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let from = NetworkAddress::btp_address(&nid, info.sender.as_ref()); + let from = NetworkAddress::get_network_address("btp",&nid, info.sender.as_ref()); let _call_data = CrossTransfer { from: from.clone(), @@ -256,7 +252,7 @@ mod execute { value: amount, }; - let _hub_btp_address = NetworkAddress::btp_address(&hub_net, &hub_address); + let _hub_btp_address = NetworkAddress::get_network_address("btp",&hub_net, &hub_address); let call_message = XCallMsg::SendCallMessage { to: _hub_btp_address, @@ -299,13 +295,13 @@ mod execute { let hub_net: String = HUB_NET.load(deps.storage)?; let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let btp_address = NetworkAddress::btp_address(&hub_net, &hub_address); + let btp_address = NetworkAddress::get_network_address("btp",&hub_net, &hub_address); if from != btp_address { return Err(ContractError::Unauthorized {}); } - let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_data.to)?; + let (net, account) = NetworkAddress::parse_protocol_address(&cross_transfer_data.to)?; if net != nid { return Err(ContractError::WrongNetwork); } @@ -345,7 +341,7 @@ mod execute { } let (net, account) = - NetworkAddress::parse_network_address(&cross_transfer_revert_data.from)?; + NetworkAddress::parse_protocol_address(&cross_transfer_revert_data.from)?; if net != nid { return Err(ContractError::InvalidBTPAddress); } @@ -387,7 +383,7 @@ mod rlpdecode_struct { } } #[cfg(test)] -mod tests { +mod rlp_test { use common::rlp::{DecoderError, Rlp, RlpStream}; #[test] @@ -426,3 +422,72 @@ mod tests { print!("this is {:?}", encoded) } } + +#[cfg(test)] +mod tests_instantiate { + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + + use super::*; + + #[test] + fn setup() { + let mut deps = mock_dependencies(); + let env = mock_env(); + let info = mock_info("SENDER", &[]); + let msg = InstantiateMsg{ + x_call: "todo!()".to_owned(), + hub_address: "todo!()".to_owned(), + }; + + let _res: Response = instantiate(deps.as_mut(), env, info.clone(), msg).unwrap(); + // let mut deps = mock_dependencies(); + // let env = mock_env(); + + // instantiate( + // deps.as_mut(), + // env.clone(), + // mock_info("sender", &[]), + // InstantiateMsg { x_call: "()".to_owned(), hub_address: "()".to_owned() }, + // ) + // .unwrap(); + + // let resp = query(deps.as_ref(), env, QueryMsg::Greet {}).unwrap(); + // let resp: GreetResp = from_binary(&resp).unwrap(); + // assert_eq!( + // resp, + // GreetResp { + // message: "Hello World".to_owned() + // } + // ); + } +} +#[cfg(test)] +mod contract_test { + use cosmwasm_std::Addr; + use cw_multi_test::{ContractWrapper, App, Executor}; + + use super::*; + + +#[test] +fn init() { + let mut app = App::default(); + + let code: ContractWrapper = ContractWrapper::new(execute, instantiate, query); + let code_id = app.store_code(Box::new(code)); + + let _addr = app. + instantiate_contract(code_id, + Addr::unchecked("owner"), + &InstantiateMsg{ + x_call: "archway13zjt2swjk0un2fpp3259szed7dsfmv3etdfkumrstlrdcq3szx9szucncp".to_owned(), + hub_address: "btp://0x1.icon/0x03AaDE86BF402F023Aa17E5725fABC4ab9E9798".to_owned(), + }, + &[], + "Contract", + None) + .unwrap(); + +} + +} From 354137f4324dfee757c8ebcca791f4e9c569b8e8 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Sun, 25 Jun 2023 09:36:45 +0545 Subject: [PATCH 20/37] feat: add xcall mock contract for testing --- .../mock-contracts/x-call-mock/.cargo/config | 4 + .../x-call-mock/.circleci/config.yml | 61 ++++++ .../mock-contracts/x-call-mock/.editorconfig | 11 + .../x-call-mock/.github/workflows/Basic.yml | 75 +++++++ .../x-call-mock/.github/workflows/Release.yml | 35 +++ .../mock-contracts/x-call-mock/.gitignore | 16 ++ .../mock-contracts/x-call-mock/Cargo.toml | 54 +++++ contracts/mock-contracts/x-call-mock/LICENSE | 202 ++++++++++++++++++ contracts/mock-contracts/x-call-mock/NOTICE | 13 ++ .../mock-contracts/x-call-mock/README.md | 99 +++++++++ .../x-call-mock/src/bin/schema.rs | 11 + .../x-call-mock/src/contract.rs | 54 +++++ .../mock-contracts/x-call-mock/src/error.rs | 13 ++ .../mock-contracts/x-call-mock/src/helpers.rs | 27 +++ .../mock-contracts/x-call-mock/src/lib.rs | 6 + .../mock-contracts/x-call-mock/src/state.rs | 1 + 16 files changed, 682 insertions(+) create mode 100644 contracts/mock-contracts/x-call-mock/.cargo/config create mode 100644 contracts/mock-contracts/x-call-mock/.circleci/config.yml create mode 100644 contracts/mock-contracts/x-call-mock/.editorconfig create mode 100644 contracts/mock-contracts/x-call-mock/.github/workflows/Basic.yml create mode 100644 contracts/mock-contracts/x-call-mock/.github/workflows/Release.yml create mode 100644 contracts/mock-contracts/x-call-mock/.gitignore create mode 100644 contracts/mock-contracts/x-call-mock/Cargo.toml create mode 100644 contracts/mock-contracts/x-call-mock/LICENSE create mode 100644 contracts/mock-contracts/x-call-mock/NOTICE create mode 100644 contracts/mock-contracts/x-call-mock/README.md create mode 100644 contracts/mock-contracts/x-call-mock/src/bin/schema.rs create mode 100644 contracts/mock-contracts/x-call-mock/src/contract.rs create mode 100644 contracts/mock-contracts/x-call-mock/src/error.rs create mode 100644 contracts/mock-contracts/x-call-mock/src/helpers.rs create mode 100644 contracts/mock-contracts/x-call-mock/src/lib.rs create mode 100644 contracts/mock-contracts/x-call-mock/src/state.rs diff --git a/contracts/mock-contracts/x-call-mock/.cargo/config b/contracts/mock-contracts/x-call-mock/.cargo/config new file mode 100644 index 0000000..af5698e --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema" diff --git a/contracts/mock-contracts/x-call-mock/.circleci/config.yml b/contracts/mock-contracts/x-call-mock/.circleci/config.yml new file mode 100644 index 0000000..9b07669 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/.circleci/config.yml @@ -0,0 +1,61 @@ +version: 2.1 + +executors: + builder: + docker: + - image: buildpack-deps:trusty + +jobs: + docker-image: + executor: builder + steps: + - checkout + - setup_remote_docker + docker_layer_caching: true + - run: + name: Build Docker artifact + command: docker build --pull -t "cosmwasm/cw-gitpod-base:${CIRCLE_SHA1}" . + - run: + name: Push application Docker image to docker hub + command: | + if [ "${CIRCLE_BRANCH}" = "master" ]; then + docker tag "cosmwasm/cw-gitpod-base:${CIRCLE_SHA1}" cosmwasm/cw-gitpod-base:latest + docker login --password-stdin -u "$DOCKER_USER" \<<<"$DOCKER_PASS" + docker push cosmwasm/cw-gitpod-base:latest + docker logout + fi + + docker-tagged: + executor: builder + steps: + - checkout + - setup_remote_docker + docker_layer_caching: true + - run: + name: Push application Docker image to docker hub + command: | + docker tag "cosmwasm/cw-gitpod-base:${CIRCLE_SHA1}" "cosmwasm/cw-gitpod-base:${CIRCLE_TAG}" + docker login --password-stdin -u "$DOCKER_USER" \<<<"$DOCKER_PASS" + docker push + docker logout + +workflows: + version: 2 + test-suite: + jobs: + # this is now a slow process... let's only run on master + - docker-image: + filters: + branches: + only: + - master + - docker-tagged: + filters: + tags: + only: + - /^v.*/ + branches: + ignore: + - /.*/ + requires: + - docker-image diff --git a/contracts/mock-contracts/x-call-mock/.editorconfig b/contracts/mock-contracts/x-call-mock/.editorconfig new file mode 100644 index 0000000..3d36f20 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.rs] +indent_size = 4 diff --git a/contracts/mock-contracts/x-call-mock/.github/workflows/Basic.yml b/contracts/mock-contracts/x-call-mock/.github/workflows/Basic.yml new file mode 100644 index 0000000..3890a07 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/.github/workflows/Basic.yml @@ -0,0 +1,75 @@ +# Based on https://github.com/actions-rs/example/blob/master/.github/workflows/quickstart.yml + +on: [push, pull_request] + +name: Basic + +jobs: + + test: + name: Test Suite + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.60.0 + target: wasm32-unknown-unknown + override: true + + - name: Run unit tests + uses: actions-rs/cargo@v1 + with: + command: unit-test + args: --locked + env: + RUST_BACKTRACE: 1 + + - name: Compile WASM contract + uses: actions-rs/cargo@v1 + with: + command: wasm + args: --locked + env: + RUSTFLAGS: "-C link-arg=-s" + + lints: + name: Lints + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.60.0 + override: true + components: rustfmt, clippy + + - name: Run cargo fmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + - name: Run cargo clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: -- -D warnings + + - name: Generate Schema + uses: actions-rs/cargo@v1 + with: + command: schema + args: --locked + + - name: Schema Changes + # fails if any changes not committed + run: git diff --exit-code schema diff --git a/contracts/mock-contracts/x-call-mock/.github/workflows/Release.yml b/contracts/mock-contracts/x-call-mock/.github/workflows/Release.yml new file mode 100644 index 0000000..a9d5979 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/.github/workflows/Release.yml @@ -0,0 +1,35 @@ +name: release wasm + +on: + release: + types: [created] + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + - name: Install cargo-run-script + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-run-script + - name: Run cargo optimize + uses: actions-rs/cargo@v1 + with: + command: run-script + args: optimize + - name: Get release ID + id: get_release + uses: bruceadams/get-release@v1.2.3 + env: + GITHUB_TOKEN: ${{ github.token }} + - name: Upload optimized wasm + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./artifacts/*.wasm + tag: ${{ github.ref }} + overwrite: true + file_glob: true diff --git a/contracts/mock-contracts/x-call-mock/.gitignore b/contracts/mock-contracts/x-call-mock/.gitignore new file mode 100644 index 0000000..9095dea --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/.gitignore @@ -0,0 +1,16 @@ +# Build results +/target +/schema + +# Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) +.cargo-ok + +# Text file backups +**/*.rs.bk + +# macOS +.DS_Store + +# IDEs +*.iml +.idea diff --git a/contracts/mock-contracts/x-call-mock/Cargo.toml b/contracts/mock-contracts/x-call-mock/Cargo.toml new file mode 100644 index 0000000..54e5a28 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "x-call-mock" +version = "0.1.0" +authors = ["qwerty0789 "] +edition = "2021" + +exclude = [ + # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. + "contract.wasm", + "hash.txt", +] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +opt-level = 3 +debug = false +rpath = false +lto = true +debug-assertions = false +codegen-units = 1 +panic = 'abort' +incremental = false +overflow-checks = true + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[package.metadata.scripts] +optimize = """docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.12.10 +""" + +[dependencies] +cosmwasm-schema = "1.1.3" +cosmwasm-std = "1.1.3" +cosmwasm-storage = "1.1.3" +cw-storage-plus = "1.0.1" +cw2 = "1.0.1" +schemars = "0.8.10" +serde = { version = "1.0.145", default-features = false, features = ["derive"] } +thiserror = { version = "1.0.31" } +cw-common = { path = "../../cw-common" } + +[dev-dependencies] +cw-multi-test = "0.16.2" diff --git a/contracts/mock-contracts/x-call-mock/LICENSE b/contracts/mock-contracts/x-call-mock/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/contracts/mock-contracts/x-call-mock/NOTICE b/contracts/mock-contracts/x-call-mock/NOTICE new file mode 100644 index 0000000..489ac61 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/NOTICE @@ -0,0 +1,13 @@ +Copyright 2023 qwerty0789 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/contracts/mock-contracts/x-call-mock/README.md b/contracts/mock-contracts/x-call-mock/README.md new file mode 100644 index 0000000..054ea48 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/README.md @@ -0,0 +1,99 @@ +# CosmWasm Starter Pack + +This is a template to build smart contracts in Rust to run inside a +[Cosmos SDK](https://github.com/cosmos/cosmos-sdk) module on all chains that enable it. +To understand the framework better, please read the overview in the +[cosmwasm repo](https://github.com/CosmWasm/cosmwasm/blob/master/README.md), +and dig into the [cosmwasm docs](https://www.cosmwasm.com). +This assumes you understand the theory and just want to get coding. + +## Creating a new repo from template + +Assuming you have a recent version of Rust and Cargo installed +(via [rustup](https://rustup.rs/)), +then the following should get you a new repo to start a contract: + +Install [cargo-generate](https://github.com/ashleygwilliams/cargo-generate) and cargo-run-script. +Unless you did that before, run this line now: + +```sh +cargo install cargo-generate --features vendored-openssl +cargo install cargo-run-script +``` + +Now, use it to create your new contract. +Go to the folder in which you want to place it and run: + +**Latest** + +```sh +cargo generate --git https://github.com/CosmWasm/cw-template.git --name PROJECT_NAME +``` + +For cloning minimal code repo: + +```sh +cargo generate --git https://github.com/CosmWasm/cw-template.git --name PROJECT_NAME -d minimal=true +``` + +**Older Version** + +Pass version as branch flag: + +```sh +cargo generate --git https://github.com/CosmWasm/cw-template.git --branch --name PROJECT_NAME +``` + +Example: + +```sh +cargo generate --git https://github.com/CosmWasm/cw-template.git --branch 0.16 --name PROJECT_NAME +``` + +You will now have a new folder called `PROJECT_NAME` (I hope you changed that to something else) +containing a simple working contract and build system that you can customize. + +## Create a Repo + +After generating, you have a initialized local git repo, but no commits, and no remote. +Go to a server (eg. github) and create a new upstream repo (called `YOUR-GIT-URL` below). +Then run the following: + +```sh +# this is needed to create a valid Cargo.lock file (see below) +cargo check +git branch -M main +git add . +git commit -m 'Initial Commit' +git remote add origin YOUR-GIT-URL +git push -u origin main +``` + +## CI Support + +We have template configurations for both [GitHub Actions](.github/workflows/Basic.yml) +and [Circle CI](.circleci/config.yml) in the generated project, so you can +get up and running with CI right away. + +One note is that the CI runs all `cargo` commands +with `--locked` to ensure it uses the exact same versions as you have locally. This also means +you must have an up-to-date `Cargo.lock` file, which is not auto-generated. +The first time you set up the project (or after adding any dep), you should ensure the +`Cargo.lock` file is updated, so the CI will test properly. This can be done simply by +running `cargo check` or `cargo unit-test`. + +## Using your project + +Once you have your custom repo, you should check out [Developing](./Developing.md) to explain +more on how to run tests and develop code. Or go through the +[online tutorial](https://docs.cosmwasm.com/) to get a better feel +of how to develop. + +[Publishing](./Publishing.md) contains useful information on how to publish your contract +to the world, once you are ready to deploy it on a running blockchain. And +[Importing](./Importing.md) contains information about pulling in other contracts or crates +that have been published. + +Please replace this README file with information about your specific project. You can keep +the `Developing.md` and `Publishing.md` files as useful referenced, but please set some +proper description in the README. diff --git a/contracts/mock-contracts/x-call-mock/src/bin/schema.rs b/contracts/mock-contracts/x-call-mock/src/bin/schema.rs new file mode 100644 index 0000000..b1569be --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/src/bin/schema.rs @@ -0,0 +1,11 @@ +use cosmwasm_schema::write_api; + +use cw_common::x_call_msg::{InstantiateMsg, XCallMsg, XCallQuery}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: XCallMsg, + query: XCallQuery, + } +} diff --git a/contracts/mock-contracts/x-call-mock/src/contract.rs b/contracts/mock-contracts/x-call-mock/src/contract.rs new file mode 100644 index 0000000..bf384fb --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/src/contract.rs @@ -0,0 +1,54 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +// use cw2::set_contract_version; + +use crate::error::ContractError; +use cw_common::{ + x_call_msg::{InstantiateMsg, XCallMsg, XCallQuery}, +}; + +/* +// version info for migration info +const CONTRACT_NAME: &str = "crates.io:x-call-mock"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +*/ + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: InstantiateMsg, +) -> Result { + Ok(Response::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: XCallMsg, +) -> Result { + match _msg { + XCallMsg::SendCallMessage { to, data, rollback } => { + let _network_address = to; + Ok(Response::default()) + } + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(_deps: Deps, _env: Env, _msg: XCallQuery) -> StdResult { + match _msg { + XCallQuery::GetNetworkAddress { } => { + Ok(to_binary( + "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798", + )?) + } + } +} + +#[cfg(test)] +mod tests {} diff --git a/contracts/mock-contracts/x-call-mock/src/error.rs b/contracts/mock-contracts/x-call-mock/src/error.rs new file mode 100644 index 0000000..4a69d8f --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/src/error.rs @@ -0,0 +1,13 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Unauthorized")] + Unauthorized {}, + // Add any other custom errors you like here. + // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. +} diff --git a/contracts/mock-contracts/x-call-mock/src/helpers.rs b/contracts/mock-contracts/x-call-mock/src/helpers.rs new file mode 100644 index 0000000..5da9242 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/src/helpers.rs @@ -0,0 +1,27 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; + +use cw_common::x_call_msg::XCallMsg; + +/// CwTemplateContract is a wrapper around Addr that provides a lot of helpers +/// for working with this. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct CwTemplateContract(pub Addr); + +impl CwTemplateContract { + pub fn addr(&self) -> Addr { + self.0.clone() + } + + pub fn call>(&self, msg: T) -> StdResult { + let msg = to_binary(&msg.into())?; + Ok(WasmMsg::Execute { + contract_addr: self.addr().into(), + msg, + funds: vec![], + } + .into()) + } +} diff --git a/contracts/mock-contracts/x-call-mock/src/lib.rs b/contracts/mock-contracts/x-call-mock/src/lib.rs new file mode 100644 index 0000000..596ab8b --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/src/lib.rs @@ -0,0 +1,6 @@ +pub mod contract; +mod error; +pub mod helpers; +pub mod state; + +pub use crate::error::ContractError; diff --git a/contracts/mock-contracts/x-call-mock/src/state.rs b/contracts/mock-contracts/x-call-mock/src/state.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/contracts/mock-contracts/x-call-mock/src/state.rs @@ -0,0 +1 @@ + From e677bdf26d9c26c1483c3207db17a763f5fe94cf Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Mon, 26 Jun 2023 13:30:24 +0545 Subject: [PATCH 21/37] fix: netwrok address test updated --- contracts/cw-common/src/network_address.rs | 37 ++++------------------ 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/contracts/cw-common/src/network_address.rs b/contracts/cw-common/src/network_address.rs index 86f0f3b..160121e 100644 --- a/contracts/cw-common/src/network_address.rs +++ b/contracts/cw-common/src/network_address.rs @@ -14,10 +14,10 @@ pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { } pub fn parse_protocol_address(_str: &str) -> StdResult<(&str, &str)> { -let mut iter = _str.splitn(2, "://"); -let protocol = iter.next().unwrap_or(""); -let account = iter.next().unwrap_or(""); -Ok((protocol, account)) + let mut iter = _str.splitn(2, "://"); + let protocol = iter.next().unwrap_or(""); + let account = iter.next().unwrap_or(""); + Ok((protocol, account)) } pub fn protocol_address(_str: &str) -> StdResult<&str> { @@ -50,10 +50,10 @@ mod tests { fn test_parse_network_address() { let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; let (network, account) = super::NetworkAddress::parse_protocol_address(btp_address).unwrap(); - assert_eq!(network, "btp:"); + assert_eq!(network, "btp"); assert_eq!( account, - "/0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798" + "0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798" ); } @@ -64,28 +64,3 @@ mod tests { assert_eq!(network, "0x38.bsc"); } } - - -// pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { -// let mut iter = _str.splitn(2, "://"); -// let protocol = iter.next().unwrap_or(""); -// let mut account = iter.next().unwrap_or("").splitn(2, "/"); -// let network = account.next().unwrap_or(""); -// let address = account.next().unwrap_or(""); -// Ok((network, address)) -// } - -// pub fn parse_protocol_address(_str: &str) -> StdResult<(&str, &str)> { -// let mut iter = _str.splitn(2, "://"); -// let protocol = iter.next().unwrap_or(""); -// let account = iter.next().unwrap_or(""); -// Ok((protocol, account)) -// } - -// pub fn protocol_address(_str: &str) -> StdResult<&str> { -// let mut iter = _str.splitn(2, "://"); -// let _ = iter.next().unwrap_or(""); -// let mut address = iter.next().unwrap_or("").splitn(2, "/"); -// let network = address.next().unwrap_or(""); -// Ok(network) -// } \ No newline at end of file From 51e029ccc58d23fdd09883c40d8c43e6cd9d9d03 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Mon, 26 Jun 2023 19:08:20 +0545 Subject: [PATCH 22/37] feat(hub_token) : rlp implementation changed, unit tests completed --- contracts/cw-common/src/data_types.rs | 77 ++- contracts/cw-common/src/hub_token_msg.rs | 13 - .../x-call-mock/src/contract.rs | 3 + .../token-contracts/cw-hub-bnusd/Cargo.toml | 2 + .../cw-hub-bnusd/src/bin/schema.rs | 14 +- .../cw-hub-bnusd/src/constants.rs | 10 +- .../cw-hub-bnusd/src/contract.rs | 520 +++++++++--------- .../token-contracts/cw-hub-bnusd/src/error.rs | 16 +- .../token-contracts/cw-hub-bnusd/src/lib.rs | 1 - .../token-contracts/cw-hub-bnusd/src/state.rs | 9 - 10 files changed, 362 insertions(+), 303 deletions(-) diff --git a/contracts/cw-common/src/data_types.rs b/contracts/cw-common/src/data_types.rs index a53736a..02c4ee6 100644 --- a/contracts/cw-common/src/data_types.rs +++ b/contracts/cw-common/src/data_types.rs @@ -1,25 +1,68 @@ -use common::rlp::RlpStream; +use common::rlp::{RlpStream, Encodable, Decodable}; use cosmwasm_schema::cw_serde; -pub mod types { - use super::*; +#[cw_serde] +pub struct CrossTransfer { + pub method: String, + pub from: String, + pub to: String, + pub value: u128, + pub data: Vec, +} - #[cw_serde] - pub struct CrossTransfer { - pub from: String, - pub to: String, - pub value: u128, - pub data: Vec, +#[cw_serde] +pub struct CrossTransferRevert { + pub method: String, + pub from: String, + pub value: u128, +} + +impl Encodable for CrossTransfer { + fn rlp_append(&self, stream: &mut common::rlp::RlpStream) { + stream + .begin_list(5) + .append(&self.method) + .append(&self.from) + .append(&self.to) + .append(&self.value) + .append(&self.data); } +} - #[cw_serde] - pub struct CrossTransferRevert { - pub from: String, - pub value: u128, +impl Decodable for CrossTransfer { + fn decode(rlp: &common::rlp::Rlp<'_>) -> Result { + Ok(Self { + method: rlp.val_at(0)?, + from: rlp.val_at(1)?, + to: rlp.val_at(2)?, + value: rlp.val_at(3)?, + data: rlp.val_at(4)?, + }) } } -impl types::CrossTransfer { + +impl Encodable for CrossTransferRevert { + fn rlp_append(&self, stream: &mut common::rlp::RlpStream) { + + stream + .begin_list(3) + .append(&self.method) + .append(&self.from) + .append(&self.value); +} +} + +impl Decodable for CrossTransferRevert { + fn decode(rlp: &common::rlp::Rlp<'_>) -> Result { + Ok(Self { + method: rlp.val_at(0)?, + from: rlp.val_at(1)?, + value: rlp.val_at(2)?, + }) + } +} +impl CrossTransfer { pub fn encode_cross_transfer_message(self) -> Vec { let method = "xCrossTransfer"; @@ -27,7 +70,7 @@ impl types::CrossTransfer { calldata.append(&method.to_string()); calldata.append(&self.from); calldata.append(&self.to); - calldata.append(&self.value); + calldata.append(&self.value.to_string()); calldata.append(&self.data); let encoded = calldata.as_raw().to_vec(); @@ -35,14 +78,14 @@ impl types::CrossTransfer { } } -impl types::CrossTransferRevert { +impl CrossTransferRevert { pub fn encode_cross_transfer_revert_message(self) -> Vec { let method = "xCrossTransferRevert"; let mut calldata = RlpStream::new_list(3); calldata.append(&method.to_string()); calldata.append(&self.from); - calldata.append(&self.value); + calldata.append(&self.value.to_string()); let encoded = calldata.as_raw().to_vec(); encoded diff --git a/contracts/cw-common/src/hub_token_msg.rs b/contracts/cw-common/src/hub_token_msg.rs index b3d5d85..3cb7981 100644 --- a/contracts/cw-common/src/hub_token_msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -1,10 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use crate::data_types::types; - -pub const X_CROSS_TRANSFER: &str = "XCrossTransfer"; -pub const X_CROSS_TRANSFER_REVERT: &str = "XCrossTransferRevert"; - #[cw_serde] pub struct InstantiateMsg { pub x_call: String, @@ -26,14 +21,6 @@ pub enum ExecuteMsg { amount: u128, data: Vec, }, - XCrossTransfer { - from: String, - cross_transfer_data: types::CrossTransfer, - }, - XCrossTransferRevert { - from: String, - cross_transfer_revert_data: types::CrossTransferRevert, - }, } #[cw_serde] diff --git a/contracts/mock-contracts/x-call-mock/src/contract.rs b/contracts/mock-contracts/x-call-mock/src/contract.rs index bf384fb..964347a 100644 --- a/contracts/mock-contracts/x-call-mock/src/contract.rs +++ b/contracts/mock-contracts/x-call-mock/src/contract.rs @@ -33,6 +33,9 @@ pub fn execute( ) -> Result { match _msg { XCallMsg::SendCallMessage { to, data, rollback } => { + print!("to: {}", to); + print!("data: {:?}", data); + print!("rollback: {:?}", rollback); let _network_address = to; Ok(Response::default()) } diff --git a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml index d7c6cff..145d9ef 100644 --- a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml +++ b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml @@ -43,10 +43,12 @@ bytes = "1.0" common = { path = "../../../libraries/rust/common" } cw-common = { path = "../../cw-common" } hex = "0.4.3" +# cw20-base = { path = "../../cw20-base" } [dev-dependencies] cw-multi-test = "0.16.2" +x-call-mock = { path = "../../mock-contracts/x-call-mock" } [profile.release] # Do not perform backtrace for panic on release builds. diff --git a/contracts/token-contracts/cw-hub-bnusd/src/bin/schema.rs b/contracts/token-contracts/cw-hub-bnusd/src/bin/schema.rs index b7a9d63..680b7e6 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/bin/schema.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/bin/schema.rs @@ -1,11 +1,11 @@ -// use cosmwasm_schema::write_api; +use cosmwasm_schema::write_api; -// use cw_hub_bnusd::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cw_common::hub_token_msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { - // write_api! { - // instantiate: InstantiateMsg, - // execute: ExecuteMsg, - // query: QueryMsg, - // } + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg, + } } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/constants.rs b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs index aad31f1..4b15de3 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/constants.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs @@ -1,5 +1,11 @@ //Constants for Reply messages +use cosmwasm_std::Uint128; + pub const REPLY_MSG_SUCCESS: u64 = 1; -pub const X_CROSS_TRANSFER: &str = "XCrossTransfer"; -pub const X_CROSS_TRANSFER_REVERT: &str = "XCrossTransferRevert"; +pub const X_CROSS_TRANSFER: &str = "xCrossTransfer"; +pub const X_CROSS_TRANSFER_REVERT: &str = "xCrossTransferRevert"; +pub const TOKEN_NAME: &str = "HubToken"; +pub const TOKEN_SYMBOL: &str = "HUB"; +pub const TOKEN_DECIMALS: u8 = 18; +pub const TOKEN_TOTAL_SUPPLY: Uint128 = Uint128::zero(); \ No newline at end of file diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index e5736ed..8ca0a8e 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,10 +1,14 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, Uint128, QueryRequest, WasmQuery, Empty, + Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, + StdResult, }; // use cw2::set_contract_version; -use crate::constants::{REPLY_MSG_SUCCESS, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT}; +use crate::constants::{ + REPLY_MSG_SUCCESS, TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL, X_CROSS_TRANSFER, + X_CROSS_TRANSFER_REVERT, TOKEN_TOTAL_SUPPLY, +}; use crate::error::ContractError; use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; use bytes::Bytes; @@ -15,9 +19,9 @@ use cw20_base::contract::{execute_burn, execute_mint}; use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; use cw_common::network_address::NetworkAddress; -use common::rlp::{DecoderError, Rlp}; +use common::rlp::Rlp; -use cw_common::data_types::types::{CrossTransfer, CrossTransferRevert}; +use cw_common::data_types::{CrossTransfer, CrossTransferRevert}; /* // version info for migration info @@ -34,81 +38,42 @@ pub fn instantiate( ) -> Result { // create initial accounts // store token info using cw20-base format + let x_call_addr = deps + .api + .addr_validate(&msg.x_call) + .map_err(ContractError::Std)?; let data = TokenInfo { - name: "HubToken".to_string(), - symbol: "HUBT".to_string(), - decimals: 18, - total_supply: Uint128::zero(), - // set self as minter, so we can properly execute mint and burn + name: TOKEN_NAME.to_string(), + symbol: TOKEN_SYMBOL.to_string(), + decimals: TOKEN_DECIMALS, + total_supply: TOKEN_TOTAL_SUPPLY, mint: Some(MinterData { - minter: _env.contract.address, + minter: x_call_addr, cap: None, }), }; - let save_token = TOKEN_INFO.save(deps.storage, &data); - if save_token.is_err() { - return Err(ContractError::Std(save_token.err().unwrap())); - } - deps.api - .addr_validate(&msg.x_call) - .expect("ContractError::InvalidToAddress"); - let xcall = X_CALL.save(deps.storage, &msg.x_call); - if xcall.is_err() { - return Err(ContractError::Std(xcall.err().unwrap())); - } - let _x_call = &msg.x_call; - let query_message = XCallQuery::GetNetworkAddress { - }; - - let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: _x_call.to_string(), - msg: to_binary(&query_message).map_err(ContractError::Std)?, - }); - - let x_call_btp_address: String = deps.querier.query(&query).map_err(ContractError::Std)?; - - // let x_call_btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - if x_call_btp_address.is_empty() { - return Err(ContractError::AddressNotFound); - } - let (nid, _) = NetworkAddress::parse_network_address(&x_call_btp_address)?; - let (hub_net, hub_address) = NetworkAddress::parse_protocol_address(&msg.hub_address)?; - - X_CALL_BTP_ADDRESS.save(deps.storage, &x_call_btp_address.to_string())?; - NID.save(deps.storage, &nid.to_string())?; - HUB_ADDRESS.save(deps.storage, &hub_address.to_string())?; - HUB_NET.save(deps.storage, &hub_net.to_string())?; - OWNER.save(deps.storage, &_info.sender)?; + let _save_token = TOKEN_INFO.save(deps.storage, &data).map_err(ContractError::Std)?; Ok(Response::default()) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, - _env: Env, + env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { - use ExecuteMsg::*; match msg { - Setup { + ExecuteMsg::Setup { x_call, hub_address, - } => execute::setup(deps, _env, info, x_call, hub_address), - HandleCallMessage { from, data } => { - execute::handle_call_message(deps, _env, info, from, data) + } => execute::setup(deps, env, info, x_call, hub_address), + ExecuteMsg::HandleCallMessage { from, data } => { + execute::handle_call_message(deps, env, info, from, data) } - CrossTransfer { to, amount, data } => { - execute::cross_transfer(deps, _env, info, to, amount, data.into()) + ExecuteMsg::CrossTransfer { to, amount, data } => { + execute::cross_transfer(deps, env, info, to, amount, data.into()) } - XCrossTransfer { - from, - cross_transfer_data, - } => execute::x_cross_transfer(deps, _env, info, from, cross_transfer_data), - XCrossTransferRevert { - from, - cross_transfer_revert_data, - } => execute::x_cross_transfer_revert(deps, _env, info, from, cross_transfer_revert_data), } } @@ -131,13 +96,23 @@ mod reply { pub fn reply_msg_success( _deps: DepsMut, _env: Env, - _msg: Reply, + msg: Reply, ) -> Result { + match msg.result { + cosmwasm_std::SubMsgResult::Ok(_) => {}, + cosmwasm_std::SubMsgResult::Err(_) => { + + }, + } Ok(Response::default()) } } mod execute { + use std::str::from_utf8; + + use bytes::BytesMut; + use common::rlp::{encode, decode}; use cosmwasm_std::{to_binary, CosmosMsg, Empty, QueryRequest, SubMsg, WasmQuery}; use super::*; @@ -145,78 +120,78 @@ mod execute { pub fn setup( deps: DepsMut, _env: Env, - _info: MessageInfo, + info: MessageInfo, x_call: String, hub_address: String, ) -> Result { deps.api .addr_validate(&x_call) - .expect("ContractError::InvalidToAddress"); - X_CALL.save(deps.storage, &x_call)?; - //Network address call remaining - let query_message = XCallQuery::GetNetworkAddress { - }; + .map_err(ContractError::Std)?; + + X_CALL.save(deps.storage, &x_call).map_err(ContractError::Std)?; + + let query_message = XCallQuery::GetNetworkAddress {}; let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: x_call, + contract_addr: x_call.to_string(), msg: to_binary(&query_message).map_err(ContractError::Std)?, }); let x_call_btp_address: String = deps.querier.query(&query).map_err(ContractError::Std)?; + if x_call_btp_address.is_empty() { return Err(ContractError::AddressNotFound); } let (nid, _) = NetworkAddress::parse_network_address(&x_call_btp_address)?; - let (hub_net, hub_address) = NetworkAddress::parse_protocol_address(&hub_address)?; + let (hub_net, hub_address) = NetworkAddress::parse_network_address(&hub_address)?; - X_CALL_BTP_ADDRESS.save(deps.storage, &x_call_btp_address)?; + X_CALL_BTP_ADDRESS.save(deps.storage, &x_call_btp_address.to_string())?; NID.save(deps.storage, &nid.to_string())?; HUB_ADDRESS.save(deps.storage, &hub_address.to_string())?; HUB_NET.save(deps.storage, &hub_net.to_string())?; - OWNER.save(deps.storage, &_info.sender)?; + OWNER.save(deps.storage, &info.sender)?; + Ok(Response::default()) } pub fn handle_call_message( deps: DepsMut, - _env: Env, + env: Env, info: MessageInfo, from: String, data: Vec, ) -> Result { - deps.api - .addr_validate(&from) - .expect("ContractError::InvalidToAddress"); + let xcall = X_CALL.load(deps.storage)?; + if info.sender != xcall { + return Err(ContractError::OnlyCallService); + } + let rlp: Rlp = Rlp::new(&data); - let data: Result, DecoderError> = rlp.as_list(); - match data { - Ok(decoded_data) => { - let method = &decoded_data[0]; - - match method.as_str() { - X_CROSS_TRANSFER => { - let cross_transfer_data: CrossTransfer = - rlpdecode_struct::decode_cross_transfer(&decoded_data); - x_cross_transfer(deps, _env, info, from, cross_transfer_data)?; - } - X_CROSS_TRANSFER_REVERT => { - let cross_transfer_revert_data: CrossTransferRevert = - rlpdecode_struct::decode_cross_transfer_revert(&decoded_data); - x_cross_transfer_revert( - deps, - _env, - info, - from, - cross_transfer_revert_data, - )?; - } - _ => { - return Err(ContractError::InvalidMethod); - } - } + let data_list: Vec = rlp.as_list().unwrap(); + let data_list = &data_list[0].to_vec(); + let method = from_utf8(&data_list).unwrap(); + + match method { + X_CROSS_TRANSFER => { + let cross_transfer_data: CrossTransfer = + decode(&data).unwrap(); + x_cross_transfer(deps, env, info, from, cross_transfer_data)?; } - Err(_error) => return Err(ContractError::InvalidData), - } + X_CROSS_TRANSFER_REVERT => { + let cross_transfer_revert_data: CrossTransferRevert = + decode(&data).unwrap(); + x_cross_transfer_revert( + deps, + env, + info, + from, + cross_transfer_revert_data, + )?; + } + _ => { + return Err(ContractError::InvalidMethod); + } + } Ok(Response::default()) } @@ -229,35 +204,31 @@ mod execute { amount: u128, data: Bytes, ) -> Result { - use super::*; - deps.api - .addr_validate(&to) - .expect("ContractError::InvalidToAddress"); let funds = info.funds.clone(); let nid = NID.load(deps.storage)?; let hub_net: String = HUB_NET.load(deps.storage)?; let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let from = NetworkAddress::get_network_address("btp",&nid, info.sender.as_ref()); + let from = NetworkAddress::get_network_address("btp", &nid, info.sender.as_ref()); - let _call_data = CrossTransfer { + let call_data = CrossTransfer { + method: X_CROSS_TRANSFER.to_string(), from: from.clone(), to, value: amount, data: data.to_vec(), }; - - let _rollback_data = CrossTransferRevert { + let rollback_data = CrossTransferRevert { + method: X_CROSS_TRANSFER_REVERT.to_string(), from, value: amount, }; - - let _hub_btp_address = NetworkAddress::get_network_address("btp",&hub_net, &hub_address); + let hub_btp_address = NetworkAddress::get_network_address("btp", &hub_net, &hub_address); let call_message = XCallMsg::SendCallMessage { - to: _hub_btp_address, - data: _call_data.encode_cross_transfer_message(), - rollback: Some(_rollback_data.encode_cross_transfer_revert_message()), + to: hub_btp_address, + data: encode(&call_data).to_vec(), + rollback: Some(encode(&rollback_data).to_vec()), }; let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { @@ -267,16 +238,10 @@ mod execute { }); let sub_message = SubMsg::reply_always(wasm_execute_message, REPLY_MSG_SUCCESS); - let _result = execute_burn(deps, env, info, amount.into()); - match _result { - Ok(resp) => { - print!("this is {:?}", resp) - } - Err(_error) => { - return Err(ContractError::MintError); - } - } - Ok(Response::new() + + let result = execute_burn(deps, env, info, amount.into()).map_err(ContractError::Cw20BaseError)?; + + Ok(result .add_submessage(sub_message) .add_attribute("method", "cross_transfer")) } @@ -288,28 +253,25 @@ mod execute { from: String, cross_transfer_data: CrossTransfer, ) -> Result { - deps.api - .addr_validate(&from) - .expect("ContractError::InvalidToAddress"); let nid = NID.load(deps.storage)?; - let hub_net: String = HUB_NET.load(deps.storage)?; - let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let btp_address = NetworkAddress::get_network_address("btp",&hub_net, &hub_address); + let hub_net: String = HUB_NET.load(deps.storage)?; + let hub_address: String = HUB_ADDRESS.load(deps.storage)?; + let btp_address = NetworkAddress::get_network_address("btp", &hub_net, &hub_address); if from != btp_address { - return Err(ContractError::Unauthorized {}); + return Err(ContractError::WrongAddress {}); } - let (net, account) = NetworkAddress::parse_protocol_address(&cross_transfer_data.to)?; + let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_data.to)?; + if net != nid { return Err(ContractError::WrongNetwork); } - let _to = deps - .api - .addr_validate(account) - .expect("ContractError::InvalidToAddress"); + deps.api + .addr_validate(&account) + .map_err(ContractError::Std)?; let res = execute_mint( deps, @@ -330,26 +292,21 @@ mod execute { from: String, cross_transfer_revert_data: CrossTransferRevert, ) -> Result { - deps.api - .addr_validate(&from) - .expect("ContractError::InvalidToAddress"); let nid = NID.load(deps.storage)?; let x_call_btp_address = X_CALL_BTP_ADDRESS.load(deps.storage)?; - if from != x_call_btp_address { return Err(ContractError::OnlyCallService); } let (net, account) = - NetworkAddress::parse_protocol_address(&cross_transfer_revert_data.from)?; + NetworkAddress::parse_network_address(&cross_transfer_revert_data.from)?; if net != nid { return Err(ContractError::InvalidBTPAddress); } - let _to = deps - .api - .addr_validate(account) - .expect("ContractError::InvalidToAddress"); + deps.api + .addr_validate(&account) + .map_err(ContractError::Std)?; let res = execute_mint( deps, @@ -364,130 +321,189 @@ mod execute { } } -mod rlpdecode_struct { - use super::*; - pub fn decode_cross_transfer(ls: &[String]) -> CrossTransfer { - CrossTransfer { - from: ls[1].clone(), - to: ls[2].clone(), - value: ls[3].parse::().unwrap_or_default(), - data: ls[4].clone().into(), - } - } - - pub fn decode_cross_transfer_revert(ls: &[String]) -> CrossTransferRevert { - CrossTransferRevert { - from: ls[1].clone(), - value: ls[2].parse::().unwrap_or_default(), - } - } -} #[cfg(test)] mod rlp_test { - use common::rlp::{DecoderError, Rlp, RlpStream}; + use std::str::from_utf8; - #[test] - fn test() { - let bytes: Vec = [ - 248, 133, 142, 120, 67, 114, 111, 115, 115, 84, 114, 97, 110, 115, 102, 101, 114, 184, - 57, 98, 116, 112, 58, 47, 47, 48, 120, 51, 56, 46, 98, 115, 99, 47, 48, 120, 48, 51, - 52, 65, 97, 68, 69, 56, 54, 66, 70, 52, 48, 50, 70, 48, 50, 51, 65, 97, 49, 55, 69, 53, - 55, 50, 53, 102, 65, 66, 67, 52, 97, 98, 57, 69, 57, 55, 57, 56, 184, 56, 98, 116, 112, - 58, 47, 47, 48, 120, 49, 46, 105, 99, 111, 110, 47, 48, 120, 48, 51, 65, 97, 68, 69, - 56, 54, 66, 70, 52, 48, 50, 70, 48, 50, 51, 65, 97, 49, 55, 69, 53, 55, 50, 53, 102, - 65, 66, 67, 52, 97, 98, 57, 69, 57, 55, 57, 56, 100, 132, 100, 97, 116, 97, - ] - .into(); - let rlp: Rlp = Rlp::new(&bytes); - let ddata: Result, DecoderError> = rlp.as_list(); - - let mut _decoded_data: Vec = Vec::new(); - - print!("this is {:?} {:?} {:?}", bytes, ddata, rlp) - } - #[test] + use bytes::BytesMut; + use common::rlp::{Rlp, encode, decode}; + use cw_common::data_types::CrossTransfer; + #[test] fn encodetest() { - let method = "xCrossTransfer"; - let val: u32 = 100; - - let mut calldata = RlpStream::new_list(4); - calldata.append(&method.to_string()); - calldata.append(&"btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); - calldata.append(&"btp://0x1.icon/0x03AaDE86BF402F023Aa17E5725fABC4ab9E9798"); - calldata.append(&val); - calldata.append(&"data"); - - let encoded = calldata.as_raw().to_vec(); - print!("this is {:?}", encoded) + let call_data = CrossTransfer { + method: "xCrossTransfer".to_string(), + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + to: "btp://btp/archway123fdth".to_string(), + value: 1000, + data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], + }; + + // let mut stream = RlpStream::new(); + let encoded_bytes = encode(&call_data).to_vec(); + + // let encoded_data: Vec = stream.out().to_vec(); + + let data: CrossTransfer = decode(&encoded_bytes).unwrap(); + + print!("this is {:?}", data); + + let rlp: Rlp = Rlp::new(&encoded_bytes); + let data: Vec = rlp.as_list().unwrap(); + let data = &data[0].to_vec(); + let method = from_utf8(&data).unwrap(); + + print!("this is {:?}",method) + } } #[cfg(test)] -mod tests_instantiate { - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; +mod tests { + use std::vec; + + use common::rlp::{encode}; + use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}, + ContractResult, MemoryStorage, OwnedDeps, SystemResult, WasmQuery, to_binary, + }; use super::*; - #[test] - fn setup() { - let mut deps = mock_dependencies(); + fn setup() -> ( + OwnedDeps, + Env, + MessageInfo, + ) { + let mut deps: OwnedDeps< + MemoryStorage, + MockApi, + MockQuerier, + > = mock_dependencies(); let env = mock_env(); - let info = mock_info("SENDER", &[]); - let msg = InstantiateMsg{ - x_call: "todo!()".to_owned(), - hub_address: "todo!()".to_owned(), + let info = mock_info("archway123fdth", &[]); + let msg = InstantiateMsg { + x_call: "archway123fdth".to_owned(), + hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), }; - let _res: Response = instantiate(deps.as_mut(), env, info.clone(), msg).unwrap(); - // let mut deps = mock_dependencies(); - // let env = mock_env(); - - // instantiate( - // deps.as_mut(), - // env.clone(), - // mock_info("sender", &[]), - // InstantiateMsg { x_call: "()".to_owned(), hub_address: "()".to_owned() }, - // ) - // .unwrap(); - - // let resp = query(deps.as_ref(), env, QueryMsg::Greet {}).unwrap(); - // let resp: GreetResp = from_binary(&resp).unwrap(); - // assert_eq!( - // resp, - // GreetResp { - // message: "Hello World".to_owned() - // } - // ); + deps.querier.update_wasm(|r| match r { + WasmQuery::Smart { + contract_addr: _, + msg: _, + } => SystemResult::Ok(ContractResult::Ok( + to_binary("btp://0x38.bsc/archway192kfvz2vrxv4hhaz3tjdk39maa69xs75n5cea8").unwrap(), + )), + _ => todo!(), + }); + + let _res: Response = instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + + let setup_message = ExecuteMsg::Setup { + x_call: "archway123fdth".to_owned(), + hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + }; + + execute(deps.as_mut(), env.clone(), info.clone(), setup_message).unwrap(); + + (deps, env, info) } -} -#[cfg(test)] -mod contract_test { - use cosmwasm_std::Addr; - use cw_multi_test::{ContractWrapper, App, Executor}; - use super::*; + #[test] + fn instantiate_test() { + setup(); + } + #[test] + fn execute_handle_call_xcrosstransfer_test() { + let (mut deps, env, info) = setup(); + + let call_data = CrossTransfer { + method: "xCrossTransfer".to_string(), + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + to: "btp://0x38.bsc/archway123fdth".to_string(), + value: 1000, + data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], + }; -#[test] -fn init() { - let mut app = App::default(); - - let code: ContractWrapper = ContractWrapper::new(execute, instantiate, query); - let code_id = app.store_code(Box::new(code)); - - let _addr = app. - instantiate_contract(code_id, - Addr::unchecked("owner"), - &InstantiateMsg{ - x_call: "archway13zjt2swjk0un2fpp3259szed7dsfmv3etdfkumrstlrdcq3szx9szucncp".to_owned(), - hub_address: "btp://0x1.icon/0x03AaDE86BF402F023Aa17E5725fABC4ab9E9798".to_owned(), - }, - &[], - "Contract", - None) - .unwrap(); + // let mut stream = RlpStream::new(); + let data = encode(&call_data).to_vec(); -} + let _res: Response = execute( + deps.as_mut(), + env, + info.clone(), + ExecuteMsg::HandleCallMessage { + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + data, + }, + ) + .unwrap(); + } + + #[test] + fn cross_transfer_test() { + let (mut deps, env, info) = setup(); + + let call_data = CrossTransfer { + method: "xCrossTransfer".to_string(), + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + to: "btp://0x38.bsc/archway123fdth".to_string(), + value: 1000, + data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], + }; + + // let mut stream = RlpStream::new(); + let data = encode(&call_data).to_vec(); + + let _res: Response = execute( + deps.as_mut(), + env.clone(), + info.clone(), + ExecuteMsg::HandleCallMessage { + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + data, + }, + ) + .unwrap(); + let _res: Response = execute( + deps.as_mut(), + env, + info.clone(), + ExecuteMsg::CrossTransfer { + to: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + amount: 1000, + data: vec![1, 2, 3, 4, 5], + }, + ) + .unwrap(); + } + + #[test] + fn execute_handle_call_test_xcrossrevert() { + let (mut deps, env, info) = setup(); + + let call_data = CrossTransfer { + method: "xCrossTransfer".to_string(), + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + to: "btp://0x38.bsc/archway123fdth".to_string(), + value: 1000, + data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], + }; + + // let mut stream = RlpStream::new(); + let data = encode(&call_data).to_vec(); + + let _res: Response = execute( + deps.as_mut(), + env, + info.clone(), + ExecuteMsg::HandleCallMessage { + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + data, + }, + ) + .unwrap(); + } } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/error.rs b/contracts/token-contracts/cw-hub-bnusd/src/error.rs index 59a510e..448d6df 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/error.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/error.rs @@ -24,12 +24,24 @@ pub enum ContractError { InvalidMethod, #[error("Invalid Reply")] InvalidReply, - #[error("Invalid Reply Data")] + #[error("Issue in Minting of Token")] MintError, - #[error("Invalid Reply Data")] + #[error("Issue in Burning of Token")] + BurnError, + #[error("Invalid Data")] InvalidData, #[error("Address Not Found")] AddressNotFound, + #[error("{0}")] + Cw20BaseError(#[from] cw20_base::ContractError), + // Add any other custom errors you like here. // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. } + + +// impl From for ContractError { +// fn from(value: cw20_base::ContractError) -> Self { +// todo!() +// } +// } \ No newline at end of file diff --git a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs index e759573..39e6d15 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs @@ -3,5 +3,4 @@ pub mod contract; mod error; pub mod helpers; pub mod state; - pub use crate::error::ContractError; diff --git a/contracts/token-contracts/cw-hub-bnusd/src/state.rs b/contracts/token-contracts/cw-hub-bnusd/src/state.rs index 1631e49..1a01145 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/state.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/state.rs @@ -8,16 +8,7 @@ pub const CROSS_CHAIN_SUPPLY: &str = "cross_chain_supply"; pub const CROSSCHAINSUPPLY: Map<&String, u128> = Map::new(CROSS_CHAIN_SUPPLY); pub const CONNECTEDCHAINS: Item> = Item::new(CONNECTED_CHAINS); -// pub const NAME: Item = Item::new("name"); -// pub const SYMBOL: Item = Item::new("symbol"); -// pub const DECIMAL: Item = Item::new("decimal"); pub const OWNER: Item = Item::new("owner"); -// pub const TOTALSUPPLY: Item = Item::new("total_supply"); - -// pub const BALANCES: Map<&Addr,u128> = Map::new("balances"); - -// const ZERO_ADDRESSES: &'static str = "0000000000000000000000000000000000000000"; - pub const X_CALL: Item = Item::new("xCall"); pub const X_CALL_BTP_ADDRESS: Item = Item::new("xCallBTPAddress"); pub const NID: Item = Item::new("nid"); From 834711e47eb321ccae6dc294d6b8e0a322fe3218 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Tue, 27 Jun 2023 15:28:13 +0545 Subject: [PATCH 23/37] feat: message added in x-call-mock --- contracts/cw-common/src/x_call_msg.rs | 6 +++ .../x-call-mock/src/contract.rs | 53 +++++++++++++++++-- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/contracts/cw-common/src/x_call_msg.rs b/contracts/cw-common/src/x_call_msg.rs index 55ad988..249fd25 100644 --- a/contracts/cw-common/src/x_call_msg.rs +++ b/contracts/cw-common/src/x_call_msg.rs @@ -22,4 +22,10 @@ pub enum XCallMsg { data: Vec, rollback: Option>, }, + + TestHandleCallMessage { + from: String, + data: Vec, + hub_token: String, + }, } diff --git a/contracts/mock-contracts/x-call-mock/src/contract.rs b/contracts/mock-contracts/x-call-mock/src/contract.rs index 964347a..1dbb8cc 100644 --- a/contracts/mock-contracts/x-call-mock/src/contract.rs +++ b/contracts/mock-contracts/x-call-mock/src/contract.rs @@ -1,12 +1,13 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, CosmosMsg, SubMsg}; // use cw2::set_contract_version; use crate::error::ContractError; use cw_common::{ - x_call_msg::{InstantiateMsg, XCallMsg, XCallQuery}, + x_call_msg::{InstantiateMsg, XCallMsg, XCallQuery}, hub_token_msg::ExecuteMsg, }; +use cosmwasm_std::{StdError, Reply}; /* // version info for migration info @@ -14,6 +15,8 @@ const CONTRACT_NAME: &str = "crates.io:x-call-mock"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); */ +const REPLY_MSG_SUCCESS: u64 = 1; + #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( _deps: DepsMut, @@ -29,15 +32,29 @@ pub fn execute( _deps: DepsMut, _env: Env, _info: MessageInfo, - _msg: XCallMsg, + msg: XCallMsg, ) -> Result { - match _msg { + match msg { XCallMsg::SendCallMessage { to, data, rollback } => { print!("to: {}", to); print!("data: {:?}", data); print!("rollback: {:?}", rollback); let _network_address = to; Ok(Response::default()) + }, + XCallMsg::TestHandleCallMessage { from, data, hub_token } => { + let call_message = ExecuteMsg::HandleCallMessage { from, data: data.clone() }; + + let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { + contract_addr: hub_token, + msg: to_binary(&call_message)?, + funds: vec![], + }); + let sub_message = SubMsg::reply_always(wasm_execute_message, REPLY_MSG_SUCCESS); + + Ok(Response::new() + .add_submessage(sub_message) + .add_attribute("method", "testhandlecallmessage")) } } } @@ -47,11 +64,37 @@ pub fn query(_deps: Deps, _env: Env, _msg: XCallQuery) -> StdResult { match _msg { XCallQuery::GetNetworkAddress { } => { Ok(to_binary( - "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798", + "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e", )?) } } } + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { + match msg.id { + REPLY_MSG_SUCCESS => reply_msg_success(deps, env, msg), + _ => Err(ContractError::Std(StdError::generic_err( + "reply id not found", + ))), + } +} + + +pub fn reply_msg_success( + _deps: DepsMut, + _env: Env, + msg: Reply, +) -> Result { + match msg.result { + cosmwasm_std::SubMsgResult::Ok(_) => {}, + cosmwasm_std::SubMsgResult::Err(error) => { + Err(StdError::GenericErr { msg: error }).map_err(Into::::into)? + } +} +Ok(Response::default()) +} + #[cfg(test)] mod tests {} From f221767564933585f014212f731a028da8cc7a31 Mon Sep 17 00:00:00 2001 From: qwerty0789 Date: Tue, 27 Jun 2023 15:30:10 +0545 Subject: [PATCH 24/37] feat: integration test setup added --- Cargo.lock | 8 + Cargo.toml | 3 + .../mock-contracts/x-call-mock/Cargo.toml | 3 +- .../token-contracts/cw-hub-bnusd/Cargo.toml | 3 +- .../cw-hub-bnusd/src/constants.rs | 3 +- .../cw-hub-bnusd/src/contract.rs | 164 +++++++------- .../cw-hub-bnusd/tests/setup.rs | 205 ++++++++++++++++++ 7 files changed, 303 insertions(+), 86 deletions(-) create mode 100644 contracts/token-contracts/cw-hub-bnusd/tests/setup.rs diff --git a/Cargo.lock b/Cargo.lock index d60f052..771cb90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -250,6 +250,7 @@ dependencies = [ "cw2", "cw20", "cw20-base", + "debug_print", "hex", "schemars", "serde", @@ -346,6 +347,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "debug_print" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f215f9b7224f49fb73256115331f677d868b34d18b65dbe4db392e6021eea90" + [[package]] name = "der" version = "0.6.1" @@ -906,6 +913,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" name = "x-call-mock" version = "0.1.0" dependencies = [ + "anyhow", "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", diff --git a/Cargo.toml b/Cargo.toml index 873d0e9..c6d3010 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,6 @@ codegen-units = 1 panic = 'abort' incremental = false overflow-checks = true + +[workspace.dependencies] +debug_print = "1.0.0" \ No newline at end of file diff --git a/contracts/mock-contracts/x-call-mock/Cargo.toml b/contracts/mock-contracts/x-call-mock/Cargo.toml index 54e5a28..c76b097 100644 --- a/contracts/mock-contracts/x-call-mock/Cargo.toml +++ b/contracts/mock-contracts/x-call-mock/Cargo.toml @@ -49,6 +49,7 @@ schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } thiserror = { version = "1.0.31" } cw-common = { path = "../../cw-common" } +anyhow = "1.0.44" [dev-dependencies] -cw-multi-test = "0.16.2" +cw-multi-test = "0.16.4" diff --git a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml index 145d9ef..073db1c 100644 --- a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml +++ b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml @@ -43,11 +43,12 @@ bytes = "1.0" common = { path = "../../../libraries/rust/common" } cw-common = { path = "../../cw-common" } hex = "0.4.3" +debug_print = {workspace=true} # cw20-base = { path = "../../cw20-base" } [dev-dependencies] -cw-multi-test = "0.16.2" +cw-multi-test = "0.16.4" x-call-mock = { path = "../../mock-contracts/x-call-mock" } [profile.release] diff --git a/contracts/token-contracts/cw-hub-bnusd/src/constants.rs b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs index 4b15de3..392f350 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/constants.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs @@ -8,4 +8,5 @@ pub const X_CROSS_TRANSFER_REVERT: &str = "xCrossTransferRevert"; pub const TOKEN_NAME: &str = "HubToken"; pub const TOKEN_SYMBOL: &str = "HUB"; pub const TOKEN_DECIMALS: u8 = 18; -pub const TOKEN_TOTAL_SUPPLY: Uint128 = Uint128::zero(); \ No newline at end of file +pub const TOKEN_TOTAL_SUPPLY: Uint128 = Uint128::zero(); +pub const PROTOCOL: &str = "btp"; \ No newline at end of file diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index 8ca0a8e..d73ec23 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,13 +1,10 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{ - Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, - StdResult, -}; +use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, StdError}; // use cw2::set_contract_version; use crate::constants::{ - REPLY_MSG_SUCCESS, TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL, X_CROSS_TRANSFER, - X_CROSS_TRANSFER_REVERT, TOKEN_TOTAL_SUPPLY, + REPLY_MSG_SUCCESS, TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, + X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT, }; use crate::error::ContractError; use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; @@ -52,7 +49,9 @@ pub fn instantiate( cap: None, }), }; - let _save_token = TOKEN_INFO.save(deps.storage, &data).map_err(ContractError::Std)?; + let _save_token = TOKEN_INFO + .save(deps.storage, &data) + .map_err(ContractError::Std)?; Ok(Response::default()) } @@ -68,12 +67,10 @@ pub fn execute( x_call, hub_address, } => execute::setup(deps, env, info, x_call, hub_address), - ExecuteMsg::HandleCallMessage { from, data } => { - execute::handle_call_message(deps, env, info, from, data) - } - ExecuteMsg::CrossTransfer { to, amount, data } => { - execute::cross_transfer(deps, env, info, to, amount, data.into()) - } + ExecuteMsg::HandleCallMessage { from, data } => + execute::handle_call_message(deps, env, info, from, data), + ExecuteMsg::CrossTransfer { to, amount, data } => + execute::cross_transfer(deps, env, info, to, amount, data.into()), } } @@ -85,35 +82,30 @@ pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { match msg.id { - REPLY_MSG_SUCCESS => reply::reply_msg_success(deps, env, msg), + REPLY_MSG_SUCCESS => reply_msg_success(deps, env, msg), _ => Err(ContractError::InvalidReply), } } -mod reply { - use super::*; - - pub fn reply_msg_success( - _deps: DepsMut, - _env: Env, - msg: Reply, - ) -> Result { - match msg.result { - cosmwasm_std::SubMsgResult::Ok(_) => {}, - cosmwasm_std::SubMsgResult::Err(_) => { - - }, +pub fn reply_msg_success(_deps: DepsMut, _env: Env, msg: Reply) -> Result { + match msg.result { + cosmwasm_std::SubMsgResult::Ok(_) => {} + cosmwasm_std::SubMsgResult::Err(error) => { + Err(StdError::GenericErr { msg: error }).map_err(Into::::into)? } - Ok(Response::default()) } + Ok(Response::default()) } mod execute { use std::str::from_utf8; use bytes::BytesMut; - use common::rlp::{encode, decode}; + use common::rlp::{decode, encode}; use cosmwasm_std::{to_binary, CosmosMsg, Empty, QueryRequest, SubMsg, WasmQuery}; + use debug_print::debug_println; + + use crate::constants::PROTOCOL; use super::*; @@ -128,7 +120,9 @@ mod execute { .addr_validate(&x_call) .map_err(ContractError::Std)?; - X_CALL.save(deps.storage, &x_call).map_err(ContractError::Std)?; + X_CALL + .save(deps.storage, &x_call) + .map_err(ContractError::Std)?; let query_message = XCallQuery::GetNetworkAddress {}; @@ -165,33 +159,30 @@ mod execute { if info.sender != xcall { return Err(ContractError::OnlyCallService); } - let rlp: Rlp = Rlp::new(&data); let data_list: Vec = rlp.as_list().unwrap(); + + if data_list.len() <= 2 { + return Err(ContractError::InvalidData); + } + debug_println!("this is {:?}", data_list); + let data_list = &data_list[0].to_vec(); let method = from_utf8(&data_list).unwrap(); - + debug_println!("this is {:?}", method); match method { X_CROSS_TRANSFER => { - let cross_transfer_data: CrossTransfer = - decode(&data).unwrap(); + let cross_transfer_data: CrossTransfer = decode(&data).unwrap(); x_cross_transfer(deps, env, info, from, cross_transfer_data)?; } X_CROSS_TRANSFER_REVERT => { - let cross_transfer_revert_data: CrossTransferRevert = - decode(&data).unwrap(); - x_cross_transfer_revert( - deps, - env, - info, - from, - cross_transfer_revert_data, - )?; + let cross_transfer_revert_data: CrossTransferRevert = decode(&data).unwrap(); + x_cross_transfer_revert(deps, env, info, from, cross_transfer_revert_data)?; } _ => { return Err(ContractError::InvalidMethod); } - } + } Ok(Response::default()) } @@ -209,7 +200,7 @@ mod execute { let hub_net: String = HUB_NET.load(deps.storage)?; let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let from = NetworkAddress::get_network_address("btp", &nid, info.sender.as_ref()); + let from = NetworkAddress::get_network_address(PROTOCOL, &nid, info.sender.as_ref()); let call_data = CrossTransfer { method: X_CROSS_TRANSFER.to_string(), @@ -223,7 +214,7 @@ mod execute { from, value: amount, }; - let hub_btp_address = NetworkAddress::get_network_address("btp", &hub_net, &hub_address); + let hub_btp_address = NetworkAddress::get_network_address(PROTOCOL, &hub_net, &hub_address); let call_message = XCallMsg::SendCallMessage { to: hub_btp_address, @@ -238,10 +229,12 @@ mod execute { }); let sub_message = SubMsg::reply_always(wasm_execute_message, REPLY_MSG_SUCCESS); + debug_println!("this is {:?}", info.sender); - let result = execute_burn(deps, env, info, amount.into()).map_err(ContractError::Cw20BaseError)?; - - Ok(result + let _result = + execute_burn(deps, env, info, amount.into()).map_err(ContractError::Cw20BaseError)?; + + Ok(Response::new() .add_submessage(sub_message) .add_attribute("method", "cross_transfer")) } @@ -258,13 +251,14 @@ mod execute { let hub_net: String = HUB_NET.load(deps.storage)?; let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let btp_address = NetworkAddress::get_network_address("btp", &hub_net, &hub_address); + let btp_address = NetworkAddress::get_network_address(PROTOCOL, &hub_net, &hub_address); + if from != btp_address { return Err(ContractError::WrongAddress {}); } let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_data.to)?; - + debug_println!("this is {:?},{:?}", net, nid); if net != nid { return Err(ContractError::WrongNetwork); } @@ -282,7 +276,8 @@ mod execute { ) .expect("Fail to mint"); - Ok(res) + Ok(res + .add_attribute("method", "x_cross_transfer")) } pub fn x_cross_transfer_revert( @@ -294,12 +289,16 @@ mod execute { ) -> Result { let nid = NID.load(deps.storage)?; let x_call_btp_address = X_CALL_BTP_ADDRESS.load(deps.storage)?; + debug_println!("this is {:?}, {:?}", from, x_call_btp_address); + if from != x_call_btp_address { return Err(ContractError::OnlyCallService); } let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_revert_data.from)?; + debug_println!("this is {:?},{:?}", net, nid); + if net != nid { return Err(ContractError::InvalidBTPAddress); } @@ -326,7 +325,7 @@ mod rlp_test { use std::str::from_utf8; use bytes::BytesMut; - use common::rlp::{Rlp, encode, decode}; + use common::rlp::{decode, encode, Rlp}; use cw_common::data_types::CrossTransfer; #[test] @@ -336,12 +335,14 @@ mod rlp_test { from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), to: "btp://btp/archway123fdth".to_string(), value: 1000, - data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], + data: vec![ + 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, + ], }; // let mut stream = RlpStream::new(); let encoded_bytes = encode(&call_data).to_vec(); - + // let encoded_data: Vec = stream.out().to_vec(); let data: CrossTransfer = decode(&encoded_bytes).unwrap(); @@ -353,8 +354,7 @@ mod rlp_test { let data = &data[0].to_vec(); let method = from_utf8(&data).unwrap(); - print!("this is {:?}",method) - + print!("this is {:?}", method) } } @@ -362,10 +362,10 @@ mod rlp_test { mod tests { use std::vec; - use common::rlp::{encode}; + use common::rlp::encode; use cosmwasm_std::{ testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}, - ContractResult, MemoryStorage, OwnedDeps, SystemResult, WasmQuery, to_binary, + to_binary, ContractResult, MemoryStorage, OwnedDeps, SystemResult, WasmQuery, }; use super::*; @@ -375,11 +375,7 @@ mod tests { Env, MessageInfo, ) { - let mut deps: OwnedDeps< - MemoryStorage, - MockApi, - MockQuerier, - > = mock_dependencies(); + let mut deps: OwnedDeps = mock_dependencies(); let env = mock_env(); let info = mock_info("archway123fdth", &[]); let msg = InstantiateMsg { @@ -387,6 +383,13 @@ mod tests { hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), }; + let _res: Response = instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + + let setup_message = ExecuteMsg::Setup { + x_call: "archway123fdth".to_owned(), + hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + }; + deps.querier.update_wasm(|r| match r { WasmQuery::Smart { contract_addr: _, @@ -397,15 +400,8 @@ mod tests { _ => todo!(), }); - let _res: Response = instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - - let setup_message = ExecuteMsg::Setup { - x_call: "archway123fdth".to_owned(), - hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - }; - execute(deps.as_mut(), env.clone(), info.clone(), setup_message).unwrap(); - + (deps, env, info) } @@ -423,13 +419,15 @@ mod tests { from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), to: "btp://0x38.bsc/archway123fdth".to_string(), value: 1000, - data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], + data: vec![ + 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, + ], }; // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); - - let _res: Response = execute( + + let _res: Response = execute( deps.as_mut(), env, info.clone(), @@ -450,12 +448,14 @@ mod tests { from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), to: "btp://0x38.bsc/archway123fdth".to_string(), value: 1000, - data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], + data: vec![ + 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, + ], }; // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); - + let _res: Response = execute( deps.as_mut(), env.clone(), @@ -484,23 +484,21 @@ mod tests { fn execute_handle_call_test_xcrossrevert() { let (mut deps, env, info) = setup(); - let call_data = CrossTransfer { - method: "xCrossTransfer".to_string(), + let call_data = CrossTransferRevert { + method: "xCrossTransferRevert".to_string(), from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - to: "btp://0x38.bsc/archway123fdth".to_string(), value: 1000, - data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], }; // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); - + let _res: Response = execute( deps.as_mut(), env, info.clone(), ExecuteMsg::HandleCallMessage { - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + from: "btp://0x38.bsc/archway192kfvz2vrxv4hhaz3tjdk39maa69xs75n5cea8".to_owned(), data, }, ) diff --git a/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs new file mode 100644 index 0000000..e509a46 --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs @@ -0,0 +1,205 @@ +use std::collections::HashMap; + +use cosmwasm_std::Addr; +use cw_multi_test::App; + + +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum TestApps { + XCall, + HubToken, +} +pub struct TestContext { + pub app: App, + pub contracts: HashMap, + pub sender: Addr, + pub admin: Option, + pub caller: Option, +} + +impl TestContext { + pub fn set_xcall_app(&mut self, addr: Addr) -> Option { + self.contracts.insert(TestApps::XCall, addr) + } + + pub fn set_hubtoken_app(&mut self, addr: Addr) -> Option { + self.contracts.insert(TestApps::HubToken, addr) + } + + pub fn get_xcall_app(&self) -> Addr { + return self.contracts.get(&TestApps::XCall).unwrap().clone(); + } + + pub fn get_hubtoken_app(&self) -> Addr { + return self.contracts.get(&TestApps::HubToken).unwrap().clone(); + } +} + +pub fn setup_context() -> TestContext { + let router = App::default(); + let sender = Addr::unchecked("sender"); + TestContext { + app: router, + contracts: HashMap::new(), + sender, + admin: None, + caller: None, + } +} + +mod instantiate_test { + use common::rlp::encode; + use cosmwasm_std::{Addr, Empty}; + use cw_common::{x_call_msg::{self, XCallMsg}, hub_token_msg::{self, ExecuteMsg}, data_types::{CrossTransfer, CrossTransferRevert}}; + use cw_multi_test::{ContractWrapper, Executor, Contract}; + use x_call_mock::contract::{execute, instantiate, query}; + + use super::*; + + fn init_x_call(mut ctx: TestContext) -> TestContext { + + let code: Box> = + Box::new(ContractWrapper::new(execute, instantiate, query).with_reply(x_call_mock::contract::reply)); + let code_id = ctx.app.store_code(code); + + let _addr = ctx.app. + instantiate_contract(code_id, + ctx.sender.clone(), + &x_call_msg::InstantiateMsg{ + }, + &[], + "XCall", + None) + .unwrap(); + ctx.set_xcall_app(_addr); + ctx + } + +fn init_token(mut ctx:TestContext,_x_call_address: String) -> TestContext { + use hub_token_msg::{InstantiateMsg}; + use cw_hub_bnusd::{contract::{execute, instantiate, query, reply}}; + + let code: Box> = Box::new(ContractWrapper::new(execute, instantiate, query).with_reply(reply)); + let code_id = ctx.app.store_code(code); + + let _addr = ctx.app. + instantiate_contract(code_id, + ctx.sender.clone(), + &InstantiateMsg{ + x_call: Addr::unchecked(_x_call_address).into_string(), + hub_address: "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + }, + &[], + "HubToken", + None) + .unwrap(); + ctx.set_hubtoken_app(_addr); + ctx +} + +fn execute_setup(mut ctx: TestContext) -> TestContext { + let _resp = ctx.app + .execute_contract( + ctx.sender.clone(), + ctx.get_hubtoken_app(), + &ExecuteMsg:: Setup { + x_call: Addr::unchecked(ctx.get_xcall_app()).into_string(), + hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + }, + &[], + ) + .unwrap(); + + ctx +} + +fn handle_call_message(mut ctx: TestContext) -> TestContext { + let call_data = CrossTransfer { + method: "xCrossTransfer".to_string(), + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + to: "btp://0x1.icon/archway123fdth".to_string(), + value: 1000, + data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], + }; + + // let mut stream = RlpStream::new(); + let data = encode(&call_data).to_vec(); + + + let _resp = ctx.app + .execute_contract( + ctx.sender.clone(), + ctx.get_xcall_app(), + &XCallMsg:: TestHandleCallMessage { + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + data, + hub_token: ctx.get_hubtoken_app().into_string(), + }, + &[], + ) + .unwrap(); + + let call_data = CrossTransferRevert { + method: "xCrossTransferRevert".to_string(), + from: "btp://0x1.icon/".to_owned()+ctx.sender.as_str(), + value: 1000, + }; + + // let mut stream = RlpStream::new(); + let data = encode(&call_data).to_vec(); + + let _resp = ctx.app + .execute_contract( + ctx.sender.clone(), + ctx.get_xcall_app(), + &XCallMsg:: TestHandleCallMessage { + from: "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + data, + hub_token: ctx.get_hubtoken_app().into_string(), + }, + &[], + ) + .unwrap(); + + ctx +} + +fn cross_transfer(mut ctx: TestContext) -> TestContext { + let _resp = ctx.app + .execute_contract( + ctx.sender.clone(), + ctx.get_hubtoken_app(), + &ExecuteMsg:: CrossTransfer { + to: "archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + amount: 100, + data: vec![], + }, + &[], + ) + .unwrap(); + + ctx +} + +fn setup_contracts(mut ctx: TestContext) -> TestContext{ + ctx = init_x_call(ctx); + let x_call_address = ctx.get_xcall_app().into_string(); + ctx = init_token(ctx, x_call_address); + ctx +} + +fn setup_test() -> TestContext { + let mut context: TestContext = setup_context(); + context = setup_contracts(context); + context = execute_setup(context); + context = handle_call_message(context); + context = cross_transfer(context); + context +} + +#[test] +fn contract_test() { + setup_test(); +} + +} From 346431aab154022b8b8607a481cccb969d05be8d Mon Sep 17 00:00:00 2001 From: Night Owl Date: Mon, 3 Jul 2023 13:45:42 +0545 Subject: [PATCH 25/37] feat: review changes --- contracts/token-contracts/cw-hub-bnusd/src/constants.rs | 6 +++--- contracts/token-contracts/cw-hub-bnusd/src/contract.rs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/contracts/token-contracts/cw-hub-bnusd/src/constants.rs b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs index 392f350..475f938 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/constants.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/constants.rs @@ -5,8 +5,8 @@ use cosmwasm_std::Uint128; pub const REPLY_MSG_SUCCESS: u64 = 1; pub const X_CROSS_TRANSFER: &str = "xCrossTransfer"; pub const X_CROSS_TRANSFER_REVERT: &str = "xCrossTransferRevert"; -pub const TOKEN_NAME: &str = "HubToken"; -pub const TOKEN_SYMBOL: &str = "HUB"; +pub const TOKEN_NAME: &str = "Balanced Dollar"; +pub const TOKEN_SYMBOL: &str = "bnUSD"; pub const TOKEN_DECIMALS: u8 = 18; pub const TOKEN_TOTAL_SUPPLY: Uint128 = Uint128::zero(); -pub const PROTOCOL: &str = "btp"; \ No newline at end of file +pub const PROTOCOL: &str = "btp"; diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index d73ec23..f92db39 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -89,12 +89,13 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result { match msg.result { - cosmwasm_std::SubMsgResult::Ok(_) => {} + cosmwasm_std::SubMsgResult::Ok(_) => { + Ok(Response::default()) + } cosmwasm_std::SubMsgResult::Err(error) => { - Err(StdError::GenericErr { msg: error }).map_err(Into::::into)? + Err(StdError::GenericErr { msg: error }).map_err(Into::::into) } } - Ok(Response::default()) } mod execute { From c1c1722cf6bf4e0f86b5ccad8fbcc6e900dd6367 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Mon, 3 Jul 2023 13:46:54 +0545 Subject: [PATCH 26/37] feat: add review comments --- contracts/cw-common/src/data_types.rs | 6 +-- contracts/cw-common/src/hub_token_msg.rs | 2 + contracts/cw-common/src/network_address.rs | 49 ++++++++++--------- contracts/cw-common/src/x_call_msg.rs | 1 + .../cw-hub-bnusd/src/contract.rs | 19 +++++-- 5 files changed, 45 insertions(+), 32 deletions(-) diff --git a/contracts/cw-common/src/data_types.rs b/contracts/cw-common/src/data_types.rs index 02c4ee6..fc16eec 100644 --- a/contracts/cw-common/src/data_types.rs +++ b/contracts/cw-common/src/data_types.rs @@ -8,7 +8,7 @@ pub struct CrossTransfer { pub to: String, pub value: u128, pub data: Vec, -} +} #[cw_serde] pub struct CrossTransferRevert { @@ -18,7 +18,7 @@ pub struct CrossTransferRevert { } impl Encodable for CrossTransfer { - fn rlp_append(&self, stream: &mut common::rlp::RlpStream) { + fn rlp_append(&self, stream: &mut RlpStream) { stream .begin_list(5) .append(&self.method) @@ -43,7 +43,7 @@ impl Decodable for CrossTransfer { impl Encodable for CrossTransferRevert { - fn rlp_append(&self, stream: &mut common::rlp::RlpStream) { + fn rlp_append(&self, stream: &mut RlpStream) { stream .begin_list(3) diff --git a/contracts/cw-common/src/hub_token_msg.rs b/contracts/cw-common/src/hub_token_msg.rs index 3cb7981..24855af 100644 --- a/contracts/cw-common/src/hub_token_msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -6,9 +6,11 @@ pub struct InstantiateMsg { pub hub_address: String, } +//TODO: Add network address as a parameter for xcall network address #[cw_serde] pub enum ExecuteMsg { Setup { + //TODO: x_call should be of addr type x_call: String, hub_address: String, }, diff --git a/contracts/cw-common/src/network_address.rs b/contracts/cw-common/src/network_address.rs index 160121e..3471755 100644 --- a/contracts/cw-common/src/network_address.rs +++ b/contracts/cw-common/src/network_address.rs @@ -1,33 +1,34 @@ -use cosmwasm_std::{StdResult}; +use cosmwasm_std::StdResult; pub struct NetworkAddress; +//TODO: use network address library from ibc-integration/contracts/cosmwasm-vm/cw-common impl NetworkAddress { - -pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { - let mut iter = _str.splitn(2, "://"); - let _ = iter.next().unwrap_or(""); - let mut account = iter.next().unwrap_or("").splitn(2, "/"); - let network = account.next().unwrap_or(""); - let address = account.next().unwrap_or(""); - Ok((network, address)) -} + pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { + let mut iter = _str.splitn(2, "://"); + let _ = iter.next().unwrap_or(""); + let mut account = iter.next().unwrap_or("").splitn(2, "/"); + let network = account.next().unwrap_or(""); + let address = account.next().unwrap_or(""); + Ok((network, address)) + } -pub fn parse_protocol_address(_str: &str) -> StdResult<(&str, &str)> { - let mut iter = _str.splitn(2, "://"); - let protocol = iter.next().unwrap_or(""); - let account = iter.next().unwrap_or(""); - Ok((protocol, account)) -} + pub fn parse_protocol_address(_str: &str) -> StdResult<(&str, &str)> { + let mut iter = _str.splitn(2, "://"); + let protocol = iter.next().unwrap_or(""); + let account = iter.next().unwrap_or(""); + Ok((protocol, account)) + } -pub fn protocol_address(_str: &str) -> StdResult<&str> { - let mut iter = _str.splitn(2, "://"); - let _ = iter.next().unwrap_or(""); - let mut address = iter.next().unwrap_or("").splitn(2, "/"); - let network = address.next().unwrap_or(""); - Ok(network) -} - pub fn get_network_address(protocol: &str,network: &str, account: &str) -> String { + pub fn protocol_address(_str: &str) -> StdResult<&str> { + let mut iter = _str.splitn(2, "://"); + let _ = iter.next().unwrap_or(""); + let mut address = iter.next().unwrap_or("").splitn(2, "/"); + let network = address.next().unwrap_or(""); + Ok(network) + } + + pub fn get_network_address(protocol: &str, network: &str, account: &str) -> String { format!( "{}://{}/{}", protocol, diff --git a/contracts/cw-common/src/x_call_msg.rs b/contracts/cw-common/src/x_call_msg.rs index 249fd25..fd32ed7 100644 --- a/contracts/cw-common/src/x_call_msg.rs +++ b/contracts/cw-common/src/x_call_msg.rs @@ -15,6 +15,7 @@ pub enum XCallQuery { GetNetworkAddress {}, } +//TODO: Use the ibc-integration/xcallmsg and xcall contract from ibc #[cw_serde] pub enum XCallMsg { SendCallMessage { diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index f92db39..b2c99b4 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -67,10 +67,10 @@ pub fn execute( x_call, hub_address, } => execute::setup(deps, env, info, x_call, hub_address), - ExecuteMsg::HandleCallMessage { from, data } => + ExecuteMsg::HandleCallMessage { from, data } => execute::handle_call_message(deps, env, info, from, data), - ExecuteMsg::CrossTransfer { to, amount, data } => - execute::cross_transfer(deps, env, info, to, amount, data.into()), + ExecuteMsg::CrossTransfer { to, amount, data } => + execute::cross_transfer(deps, env, info, to, amount, data.into()), } } @@ -140,6 +140,7 @@ mod execute { let (nid, _) = NetworkAddress::parse_network_address(&x_call_btp_address)?; let (hub_net, hub_address) = NetworkAddress::parse_network_address(&hub_address)?; + //TODO: remove btp specification X_CALL_BTP_ADDRESS.save(deps.storage, &x_call_btp_address.to_string())?; NID.save(deps.storage, &nid.to_string())?; HUB_ADDRESS.save(deps.storage, &hub_address.to_string())?; @@ -215,6 +216,7 @@ mod execute { from, value: amount, }; + //TODO: rename to hub_token_address let hub_btp_address = NetworkAddress::get_network_address(PROTOCOL, &hub_net, &hub_address); let call_message = XCallMsg::SendCallMessage { @@ -235,6 +237,7 @@ mod execute { let _result = execute_burn(deps, env, info, amount.into()).map_err(ContractError::Cw20BaseError)?; + //TODO: emit a event log for cross transfer Ok(Response::new() .add_submessage(sub_message) .add_attribute("method", "cross_transfer")) @@ -251,6 +254,7 @@ mod execute { let hub_net: String = HUB_NET.load(deps.storage)?; + //TODO: rename hub address to DESTINATION_TOKEN_ADDRESS let hub_address: String = HUB_ADDRESS.load(deps.storage)?; let btp_address = NetworkAddress::get_network_address(PROTOCOL, &hub_net, &hub_address); @@ -258,6 +262,7 @@ mod execute { return Err(ContractError::WrongAddress {}); } + //TODO: add a validation check for ICON address in network address library let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_data.to)?; debug_println!("this is {:?},{:?}", net, nid); if net != nid { @@ -277,6 +282,7 @@ mod execute { ) .expect("Fail to mint"); + //TODO: add event for cross transfer with relevant parameters Ok(res .add_attribute("method", "x_cross_transfer")) } @@ -296,10 +302,12 @@ mod execute { return Err(ContractError::OnlyCallService); } + //TODO: from can be a native archway address let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_revert_data.from)?; debug_println!("this is {:?},{:?}", net, nid); + //TODO: this can be removed if net != nid { return Err(ContractError::InvalidBTPAddress); } @@ -356,6 +364,7 @@ mod rlp_test { let method = from_utf8(&data).unwrap(); print!("this is {:?}", method) + // TODO: Add fixed values for tests from java tests for encoding and decoding } } @@ -385,12 +394,12 @@ mod tests { }; let _res: Response = instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - + let setup_message = ExecuteMsg::Setup { x_call: "archway123fdth".to_owned(), hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), }; - + deps.querier.update_wasm(|r| match r { WasmQuery::Smart { contract_addr: _, From 50f6667090b3c6c5bca9eb32ceb27f3dba2be189 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Mon, 3 Jul 2023 13:54:43 +0545 Subject: [PATCH 27/37] chore: run pre commit checks --- contracts/cw-common/src/data_types.rs | 12 +- contracts/cw-common/src/network_address.rs | 10 +- .../x-call-mock/src/contract.rs | 46 +++-- .../cw-hub-bnusd/src/contract.rs | 19 +- .../token-contracts/cw-hub-bnusd/src/error.rs | 4 +- .../cw-hub-bnusd/tests/setup.rs | 190 ++++++++++-------- 6 files changed, 147 insertions(+), 134 deletions(-) diff --git a/contracts/cw-common/src/data_types.rs b/contracts/cw-common/src/data_types.rs index fc16eec..125811b 100644 --- a/contracts/cw-common/src/data_types.rs +++ b/contracts/cw-common/src/data_types.rs @@ -1,4 +1,4 @@ -use common::rlp::{RlpStream, Encodable, Decodable}; +use common::rlp::{Decodable, Encodable, RlpStream}; use cosmwasm_schema::cw_serde; #[cw_serde] @@ -41,25 +41,25 @@ impl Decodable for CrossTransfer { } } - impl Encodable for CrossTransferRevert { fn rlp_append(&self, stream: &mut RlpStream) { - stream .begin_list(3) .append(&self.method) .append(&self.from) .append(&self.value); -} + } } impl Decodable for CrossTransferRevert { - fn decode(rlp: &common::rlp::Rlp<'_>) -> Result { + fn decode( + rlp: &common::rlp::Rlp<'_>, + ) -> Result { Ok(Self { method: rlp.val_at(0)?, from: rlp.val_at(1)?, value: rlp.val_at(2)?, - }) + }) } } impl CrossTransfer { diff --git a/contracts/cw-common/src/network_address.rs b/contracts/cw-common/src/network_address.rs index 3471755..0ac1c78 100644 --- a/contracts/cw-common/src/network_address.rs +++ b/contracts/cw-common/src/network_address.rs @@ -29,12 +29,7 @@ impl NetworkAddress { } pub fn get_network_address(protocol: &str, network: &str, account: &str) -> String { - format!( - "{}://{}/{}", - protocol, - network, - account - ) + format!("{}://{}/{}", protocol, network, account) } } @@ -50,7 +45,8 @@ mod tests { #[test] fn test_parse_network_address() { let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let (network, account) = super::NetworkAddress::parse_protocol_address(btp_address).unwrap(); + let (network, account) = + super::NetworkAddress::parse_protocol_address(btp_address).unwrap(); assert_eq!(network, "btp"); assert_eq!( account, diff --git a/contracts/mock-contracts/x-call-mock/src/contract.rs b/contracts/mock-contracts/x-call-mock/src/contract.rs index 1dbb8cc..9cd3255 100644 --- a/contracts/mock-contracts/x-call-mock/src/contract.rs +++ b/contracts/mock-contracts/x-call-mock/src/contract.rs @@ -1,13 +1,16 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, CosmosMsg, SubMsg}; +use cosmwasm_std::{ + to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Response, StdResult, SubMsg, +}; // use cw2::set_contract_version; use crate::error::ContractError; +use cosmwasm_std::{Reply, StdError}; use cw_common::{ - x_call_msg::{InstantiateMsg, XCallMsg, XCallQuery}, hub_token_msg::ExecuteMsg, + hub_token_msg::ExecuteMsg, + x_call_msg::{InstantiateMsg, XCallMsg, XCallQuery}, }; -use cosmwasm_std::{StdError, Reply}; /* // version info for migration info @@ -41,10 +44,17 @@ pub fn execute( print!("rollback: {:?}", rollback); let _network_address = to; Ok(Response::default()) - }, - XCallMsg::TestHandleCallMessage { from, data, hub_token } => { - let call_message = ExecuteMsg::HandleCallMessage { from, data: data.clone() }; - + } + XCallMsg::TestHandleCallMessage { + from, + data, + hub_token, + } => { + let call_message = ExecuteMsg::HandleCallMessage { + from, + data: data.clone(), + }; + let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { contract_addr: hub_token, msg: to_binary(&call_message)?, @@ -62,15 +72,12 @@ pub fn execute( #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(_deps: Deps, _env: Env, _msg: XCallQuery) -> StdResult { match _msg { - XCallQuery::GetNetworkAddress { } => { - Ok(to_binary( - "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e", - )?) - } + XCallQuery::GetNetworkAddress {} => Ok(to_binary( + "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e", + )?), } } - #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { match msg.id { @@ -81,19 +88,14 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result { +pub fn reply_msg_success(_deps: DepsMut, _env: Env, msg: Reply) -> Result { match msg.result { - cosmwasm_std::SubMsgResult::Ok(_) => {}, + cosmwasm_std::SubMsgResult::Ok(_) => {} cosmwasm_std::SubMsgResult::Err(error) => { Err(StdError::GenericErr { msg: error }).map_err(Into::::into)? + } } -} -Ok(Response::default()) + Ok(Response::default()) } #[cfg(test)] diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index b2c99b4..4ac83c3 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,6 +1,6 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, StdError}; +use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult}; // use cw2::set_contract_version; use crate::constants::{ REPLY_MSG_SUCCESS, TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, @@ -67,10 +67,12 @@ pub fn execute( x_call, hub_address, } => execute::setup(deps, env, info, x_call, hub_address), - ExecuteMsg::HandleCallMessage { from, data } => - execute::handle_call_message(deps, env, info, from, data), - ExecuteMsg::CrossTransfer { to, amount, data } => - execute::cross_transfer(deps, env, info, to, amount, data.into()), + ExecuteMsg::HandleCallMessage { from, data } => { + execute::handle_call_message(deps, env, info, from, data) + } + ExecuteMsg::CrossTransfer { to, amount, data } => { + execute::cross_transfer(deps, env, info, to, amount, data.into()) + } } } @@ -89,9 +91,7 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result { match msg.result { - cosmwasm_std::SubMsgResult::Ok(_) => { - Ok(Response::default()) - } + cosmwasm_std::SubMsgResult::Ok(_) => Ok(Response::default()), cosmwasm_std::SubMsgResult::Err(error) => { Err(StdError::GenericErr { msg: error }).map_err(Into::::into) } @@ -283,8 +283,7 @@ mod execute { .expect("Fail to mint"); //TODO: add event for cross transfer with relevant parameters - Ok(res - .add_attribute("method", "x_cross_transfer")) + Ok(res.add_attribute("method", "x_cross_transfer")) } pub fn x_cross_transfer_revert( diff --git a/contracts/token-contracts/cw-hub-bnusd/src/error.rs b/contracts/token-contracts/cw-hub-bnusd/src/error.rs index 448d6df..79217a3 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/error.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/error.rs @@ -34,14 +34,12 @@ pub enum ContractError { AddressNotFound, #[error("{0}")] Cw20BaseError(#[from] cw20_base::ContractError), - // Add any other custom errors you like here. // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. } - // impl From for ContractError { // fn from(value: cw20_base::ContractError) -> Self { // todo!() // } -// } \ No newline at end of file +// } diff --git a/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs index e509a46..9dc0310 100644 --- a/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs +++ b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use cosmwasm_std::Addr; use cw_multi_test::App; - #[derive(Debug, PartialEq, Eq, Hash)] pub enum TestApps { XCall, @@ -50,88 +49,105 @@ pub fn setup_context() -> TestContext { mod instantiate_test { use common::rlp::encode; use cosmwasm_std::{Addr, Empty}; - use cw_common::{x_call_msg::{self, XCallMsg}, hub_token_msg::{self, ExecuteMsg}, data_types::{CrossTransfer, CrossTransferRevert}}; - use cw_multi_test::{ContractWrapper, Executor, Contract}; + use cw_common::{ + data_types::{CrossTransfer, CrossTransferRevert}, + hub_token_msg::{self, ExecuteMsg}, + x_call_msg::{self, XCallMsg}, + }; + use cw_multi_test::{Contract, ContractWrapper, Executor}; use x_call_mock::contract::{execute, instantiate, query}; use super::*; fn init_x_call(mut ctx: TestContext) -> TestContext { - - let code: Box> = - Box::new(ContractWrapper::new(execute, instantiate, query).with_reply(x_call_mock::contract::reply)); - let code_id = ctx.app.store_code(code); - - let _addr = ctx.app. - instantiate_contract(code_id, - ctx.sender.clone(), - &x_call_msg::InstantiateMsg{ - }, - &[], - "XCall", - None) - .unwrap(); - ctx.set_xcall_app(_addr); + let code: Box> = Box::new( + ContractWrapper::new(execute, instantiate, query) + .with_reply(x_call_mock::contract::reply), + ); + let code_id = ctx.app.store_code(code); + + let _addr = ctx + .app + .instantiate_contract( + code_id, + ctx.sender.clone(), + &x_call_msg::InstantiateMsg {}, + &[], + "XCall", + None, + ) + .unwrap(); + ctx.set_xcall_app(_addr); ctx } -fn init_token(mut ctx:TestContext,_x_call_address: String) -> TestContext { - use hub_token_msg::{InstantiateMsg}; - use cw_hub_bnusd::{contract::{execute, instantiate, query, reply}}; + fn init_token(mut ctx: TestContext, _x_call_address: String) -> TestContext { + use cw_hub_bnusd::contract::{execute, instantiate, query, reply}; + use hub_token_msg::InstantiateMsg; - let code: Box> = Box::new(ContractWrapper::new(execute, instantiate, query).with_reply(reply)); + let code: Box> = + Box::new(ContractWrapper::new(execute, instantiate, query).with_reply(reply)); let code_id = ctx.app.store_code(code); - let _addr = ctx.app. - instantiate_contract(code_id, - ctx.sender.clone(), - &InstantiateMsg{ - x_call: Addr::unchecked(_x_call_address).into_string(), - hub_address: "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - }, - &[], - "HubToken", - None) - .unwrap(); + let _addr = ctx + .app + .instantiate_contract( + code_id, + ctx.sender.clone(), + &InstantiateMsg { + x_call: Addr::unchecked(_x_call_address).into_string(), + hub_address: "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" + .to_owned(), + }, + &[], + "HubToken", + None, + ) + .unwrap(); ctx.set_hubtoken_app(_addr); ctx -} + } -fn execute_setup(mut ctx: TestContext) -> TestContext { - let _resp = ctx.app + fn execute_setup(mut ctx: TestContext) -> TestContext { + let _resp = ctx + .app .execute_contract( ctx.sender.clone(), ctx.get_hubtoken_app(), - &ExecuteMsg:: Setup { + &ExecuteMsg::Setup { x_call: Addr::unchecked(ctx.get_xcall_app()).into_string(), - hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" + .to_owned(), }, &[], ) .unwrap(); - ctx -} + ctx + } -fn handle_call_message(mut ctx: TestContext) -> TestContext { - let call_data = CrossTransfer { - method: "xCrossTransfer".to_string(), - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - to: "btp://0x1.icon/archway123fdth".to_string(), - value: 1000, - data: vec![118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93], - }; + fn handle_call_message(mut ctx: TestContext) -> TestContext { + let call_data = CrossTransfer { + method: "xCrossTransfer".to_string(), + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + to: "btp://0x1.icon/archway123fdth".to_string(), + value: 1000, + data: vec![ + 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, + ], + }; - // let mut stream = RlpStream::new(); - let data = encode(&call_data).to_vec(); - - - let _resp = ctx.app + // let mut stream = RlpStream::new(); + let data = encode(&call_data).to_vec(); + + let _resp = ctx + .app .execute_contract( ctx.sender.clone(), ctx.get_xcall_app(), - &XCallMsg:: TestHandleCallMessage { - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + &XCallMsg::TestHandleCallMessage { + from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" + .to_owned(), data, hub_token: ctx.get_hubtoken_app().into_string(), }, @@ -141,35 +157,38 @@ fn handle_call_message(mut ctx: TestContext) -> TestContext { let call_data = CrossTransferRevert { method: "xCrossTransferRevert".to_string(), - from: "btp://0x1.icon/".to_owned()+ctx.sender.as_str(), + from: "btp://0x1.icon/".to_owned() + ctx.sender.as_str(), value: 1000, }; // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); - let _resp = ctx.app + let _resp = ctx + .app .execute_contract( ctx.sender.clone(), ctx.get_xcall_app(), - &XCallMsg:: TestHandleCallMessage { - from: "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + &XCallMsg::TestHandleCallMessage { + from: "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" + .to_owned(), data, hub_token: ctx.get_hubtoken_app().into_string(), }, &[], ) - .unwrap(); + .unwrap(); - ctx -} + ctx + } -fn cross_transfer(mut ctx: TestContext) -> TestContext { - let _resp = ctx.app + fn cross_transfer(mut ctx: TestContext) -> TestContext { + let _resp = ctx + .app .execute_contract( ctx.sender.clone(), ctx.get_hubtoken_app(), - &ExecuteMsg:: CrossTransfer { + &ExecuteMsg::CrossTransfer { to: "archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), amount: 100, data: vec![], @@ -178,28 +197,27 @@ fn cross_transfer(mut ctx: TestContext) -> TestContext { ) .unwrap(); - ctx -} - -fn setup_contracts(mut ctx: TestContext) -> TestContext{ - ctx = init_x_call(ctx); - let x_call_address = ctx.get_xcall_app().into_string(); - ctx = init_token(ctx, x_call_address); - ctx -} + ctx + } -fn setup_test() -> TestContext { - let mut context: TestContext = setup_context(); - context = setup_contracts(context); - context = execute_setup(context); - context = handle_call_message(context); - context = cross_transfer(context); - context -} + fn setup_contracts(mut ctx: TestContext) -> TestContext { + ctx = init_x_call(ctx); + let x_call_address = ctx.get_xcall_app().into_string(); + ctx = init_token(ctx, x_call_address); + ctx + } -#[test] -fn contract_test() { - setup_test(); -} + fn setup_test() -> TestContext { + let mut context: TestContext = setup_context(); + context = setup_contracts(context); + context = execute_setup(context); + context = handle_call_message(context); + context = cross_transfer(context); + context + } + #[test] + fn contract_test() { + setup_test(); + } } From ed8440efca8660572e57cf8500b7f778d2a3b705 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Mon, 3 Jul 2023 13:58:04 +0545 Subject: [PATCH 28/37] feat: update schema for external methods --- contracts/cw-common/src/network_address.rs | 4 +- .../x-call-mock/src/contract.rs | 2 +- .../cw-hub-bnusd/schema/cw-hub-bnusd.json | 107 +++++++++++++++++- .../cw-hub-bnusd/schema/raw/execute.json | 95 +++++++++++++++- .../cw-hub-bnusd/schema/raw/instantiate.json | 12 ++ .../cw-hub-bnusd/src/contract.rs | 18 +-- 6 files changed, 222 insertions(+), 16 deletions(-) diff --git a/contracts/cw-common/src/network_address.rs b/contracts/cw-common/src/network_address.rs index 0ac1c78..b4efd30 100644 --- a/contracts/cw-common/src/network_address.rs +++ b/contracts/cw-common/src/network_address.rs @@ -7,7 +7,7 @@ impl NetworkAddress { pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { let mut iter = _str.splitn(2, "://"); let _ = iter.next().unwrap_or(""); - let mut account = iter.next().unwrap_or("").splitn(2, "/"); + let mut account = iter.next().unwrap_or("").splitn(2, '/'); let network = account.next().unwrap_or(""); let address = account.next().unwrap_or(""); Ok((network, address)) @@ -23,7 +23,7 @@ impl NetworkAddress { pub fn protocol_address(_str: &str) -> StdResult<&str> { let mut iter = _str.splitn(2, "://"); let _ = iter.next().unwrap_or(""); - let mut address = iter.next().unwrap_or("").splitn(2, "/"); + let mut address = iter.next().unwrap_or("").splitn(2, '/'); let network = address.next().unwrap_or(""); Ok(network) } diff --git a/contracts/mock-contracts/x-call-mock/src/contract.rs b/contracts/mock-contracts/x-call-mock/src/contract.rs index 9cd3255..6a023c3 100644 --- a/contracts/mock-contracts/x-call-mock/src/contract.rs +++ b/contracts/mock-contracts/x-call-mock/src/contract.rs @@ -52,7 +52,7 @@ pub fn execute( } => { let call_message = ExecuteMsg::HandleCallMessage { from, - data: data.clone(), + data, }; let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json b/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json index 329662a..892d165 100644 --- a/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json +++ b/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json @@ -6,13 +6,116 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "InstantiateMsg", "type": "object", + "required": [ + "hub_address", + "x_call" + ], + "properties": { + "hub_address": { + "type": "string" + }, + "x_call": { + "type": "string" + } + }, "additionalProperties": false }, "execute": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ExecuteMsg", - "type": "string", - "enum": [] + "oneOf": [ + { + "type": "object", + "required": [ + "setup" + ], + "properties": { + "setup": { + "type": "object", + "required": [ + "hub_address", + "x_call" + ], + "properties": { + "hub_address": { + "type": "string" + }, + "x_call": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "handle_call_message" + ], + "properties": { + "handle_call_message": { + "type": "object", + "required": [ + "data", + "from" + ], + "properties": { + "data": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "from": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cross_transfer" + ], + "properties": { + "cross_transfer": { + "type": "object", + "required": [ + "amount", + "data", + "to" + ], + "properties": { + "amount": { + "type": "integer", + "format": "uint128", + "minimum": 0.0 + }, + "data": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "to": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] }, "query": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json index b3d18b4..3964827 100644 --- a/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json @@ -1,6 +1,97 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ExecuteMsg", - "type": "string", - "enum": [] + "oneOf": [ + { + "type": "object", + "required": [ + "setup" + ], + "properties": { + "setup": { + "type": "object", + "required": [ + "hub_address", + "x_call" + ], + "properties": { + "hub_address": { + "type": "string" + }, + "x_call": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "handle_call_message" + ], + "properties": { + "handle_call_message": { + "type": "object", + "required": [ + "data", + "from" + ], + "properties": { + "data": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "from": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cross_transfer" + ], + "properties": { + "cross_transfer": { + "type": "object", + "required": [ + "amount", + "data", + "to" + ], + "properties": { + "amount": { + "type": "integer", + "format": "uint128", + "minimum": 0.0 + }, + "data": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "to": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] } diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/instantiate.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/instantiate.json index 1352613..c3edff2 100644 --- a/contracts/token-contracts/cw-hub-bnusd/schema/raw/instantiate.json +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/instantiate.json @@ -2,5 +2,17 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "InstantiateMsg", "type": "object", + "required": [ + "hub_address", + "x_call" + ], + "properties": { + "hub_address": { + "type": "string" + }, + "x_call": { + "type": "string" + } + }, "additionalProperties": false } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index 4ac83c3..f6a4e79 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -49,7 +49,7 @@ pub fn instantiate( cap: None, }), }; - let _save_token = TOKEN_INFO + TOKEN_INFO .save(deps.storage, &data) .map_err(ContractError::Std)?; Ok(Response::default()) @@ -128,7 +128,7 @@ mod execute { let query_message = XCallQuery::GetNetworkAddress {}; let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: x_call.to_string(), + contract_addr: x_call, msg: to_binary(&query_message).map_err(ContractError::Std)?, }); @@ -170,7 +170,7 @@ mod execute { debug_println!("this is {:?}", data_list); let data_list = &data_list[0].to_vec(); - let method = from_utf8(&data_list).unwrap(); + let method = from_utf8(data_list).unwrap(); debug_println!("this is {:?}", method); match method { X_CROSS_TRANSFER => { @@ -270,7 +270,7 @@ mod execute { } deps.api - .addr_validate(&account) + .addr_validate(account) .map_err(ContractError::Std)?; let res = execute_mint( @@ -312,7 +312,7 @@ mod execute { } deps.api - .addr_validate(&account) + .addr_validate(account) .map_err(ContractError::Std)?; let res = execute_mint( @@ -360,7 +360,7 @@ mod rlp_test { let rlp: Rlp = Rlp::new(&encoded_bytes); let data: Vec = rlp.as_list().unwrap(); let data = &data[0].to_vec(); - let method = from_utf8(&data).unwrap(); + let method = from_utf8(data).unwrap(); print!("this is {:?}", method) // TODO: Add fixed values for tests from java tests for encoding and decoding @@ -439,7 +439,7 @@ mod tests { let _res: Response = execute( deps.as_mut(), env, - info.clone(), + info, ExecuteMsg::HandleCallMessage { from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), data, @@ -479,7 +479,7 @@ mod tests { let _res: Response = execute( deps.as_mut(), env, - info.clone(), + info, ExecuteMsg::CrossTransfer { to: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), amount: 1000, @@ -505,7 +505,7 @@ mod tests { let _res: Response = execute( deps.as_mut(), env, - info.clone(), + info, ExecuteMsg::HandleCallMessage { from: "btp://0x38.bsc/archway192kfvz2vrxv4hhaz3tjdk39maa69xs75n5cea8".to_owned(), data, From d27975318c1335adf8f097c2b1112f9760f7db8a Mon Sep 17 00:00:00 2001 From: Night Owl Date: Tue, 4 Jul 2023 17:18:03 +0545 Subject: [PATCH 29/37] chore: run pre commit checks Signed-off-by: Night Owl --- contracts/mock-contracts/x-call-mock/src/contract.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/mock-contracts/x-call-mock/src/contract.rs b/contracts/mock-contracts/x-call-mock/src/contract.rs index 6a023c3..13203e8 100644 --- a/contracts/mock-contracts/x-call-mock/src/contract.rs +++ b/contracts/mock-contracts/x-call-mock/src/contract.rs @@ -50,10 +50,7 @@ pub fn execute( data, hub_token, } => { - let call_message = ExecuteMsg::HandleCallMessage { - from, - data, - }; + let call_message = ExecuteMsg::HandleCallMessage { from, data }; let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { contract_addr: hub_token, From b029d126afe228869fe85dd89bbc54ef1512585c Mon Sep 17 00:00:00 2001 From: Night Owl Date: Tue, 4 Jul 2023 17:22:44 +0545 Subject: [PATCH 30/37] fix: cw-check for cw_hub_bnusd.wasm Signed-off-by: Night Owl --- scripts/generate_wasm.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/generate_wasm.sh b/scripts/generate_wasm.sh index 3e34392..3aaf081 100755 --- a/scripts/generate_wasm.sh +++ b/scripts/generate_wasm.sh @@ -21,5 +21,6 @@ for WASM in ./target/wasm32-unknown-unknown/release/*.wasm; do echo "########Optimizing $NAME ...########" wasm-opt -Oz "$WASM" -o "artifacts/$NAME" echo "########Verifying $NAME file with cosmwasm-check ...########" - cosmwasm-check "artifacts/$NAME" done + +cosmwasm-check "artifacts/cw_hub_bnusd.wasm" From ec9f468e8dae43331263b3c768bab31d968aff65 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Tue, 4 Jul 2023 17:30:04 +0545 Subject: [PATCH 31/37] feat: add cargo clean command Signed-off-by: Night Owl --- scripts/pre_commit.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pre_commit.sh b/scripts/pre_commit.sh index bf79581..e2bd6e0 100755 --- a/scripts/pre_commit.sh +++ b/scripts/pre_commit.sh @@ -4,4 +4,4 @@ set -e cargo fmt --all cargo clippy --fix source ./scripts/run_in_subprojects.sh ./contracts/token-contracts/cw-hub-bnusd -# cargo clean +cargo clean From a1c7474b22b2f469dacc7a6bd5aadca071a05625 Mon Sep 17 00:00:00 2001 From: qwerty0789 <134275268+qwerty0789@users.noreply.github.com> Date: Tue, 4 Jul 2023 22:30:33 +0545 Subject: [PATCH 32/37] feat: add cw hub bnUSD, changes after review (#19) * style: cargo fix and clippy * feat: changes after the review * fix: emitting data(vec) in event removed * fix: cargo check * fix: cargo clippy run * feat: change common/rlp to crate rlp --- Cargo.lock | 55 ++- Cargo.toml | 3 +- contracts/cw-common/Cargo.toml | 3 +- contracts/cw-common/src/data_types.rs | 59 +-- contracts/cw-common/src/hub_token_msg.rs | 11 +- contracts/cw-common/src/network_address.rs | 176 +++++-- .../x-call-mock/src/contract.rs | 10 +- .../token-contracts/cw-hub-bnusd/Cargo.toml | 2 +- .../cw-hub-bnusd/schema/cw-hub-bnusd.json | 19 +- .../cw-hub-bnusd/schema/raw/execute.json | 19 +- .../cw-hub-bnusd/src/contract.rs | 201 ++++---- .../token-contracts/cw-hub-bnusd/src/error.rs | 2 +- .../token-contracts/cw-hub-bnusd/src/state.rs | 11 +- .../cw-hub-bnusd/tests/setup.rs | 30 +- libraries/rust/common/Cargo.toml | 16 - libraries/rust/common/src/lib.rs | 1 - libraries/rust/common/src/rlp/error.rs | 51 -- libraries/rust/common/src/rlp/impls.rs | 266 ---------- libraries/rust/common/src/rlp/mod.rs | 126 ----- libraries/rust/common/src/rlp/nullable.rs | 44 -- libraries/rust/common/src/rlp/rlpin.rs | 461 ----------------- libraries/rust/common/src/rlp/stream.rs | 466 ------------------ libraries/rust/common/src/rlp/traits.rs | 31 -- 23 files changed, 364 insertions(+), 1699 deletions(-) delete mode 100644 libraries/rust/common/Cargo.toml delete mode 100644 libraries/rust/common/src/lib.rs delete mode 100644 libraries/rust/common/src/rlp/error.rs delete mode 100644 libraries/rust/common/src/rlp/impls.rs delete mode 100644 libraries/rust/common/src/rlp/mod.rs delete mode 100644 libraries/rust/common/src/rlp/nullable.rs delete mode 100644 libraries/rust/common/src/rlp/rlpin.rs delete mode 100644 libraries/rust/common/src/rlp/stream.rs delete mode 100644 libraries/rust/common/src/rlp/traits.rs diff --git a/Cargo.lock b/Cargo.lock index 771cb90..442a4bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.71" @@ -73,20 +82,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "common" -version = "0.1.0" -dependencies = [ - "bytes", - "cosmwasm-std", - "cw20", - "hex", - "rlp", - "rlp-derive", - "rustc-hex", - "serde", -] - [[package]] name = "const-oid" version = "0.9.2" @@ -224,11 +219,12 @@ name = "cw-common" version = "0.1.0" dependencies = [ "bytes", - "common", "cosmwasm-schema", "cosmwasm-std", + "cw-storage-plus", "cw20", "hex", + "regex", "rlp", "rlp-derive", "rustc-hex", @@ -240,7 +236,6 @@ name = "cw-hub-bnusd" version = "0.1.0" dependencies = [ "bytes", - "common", "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", @@ -252,6 +247,7 @@ dependencies = [ "cw20-base", "debug_print", "hex", + "rlp", "schemars", "serde", "thiserror", @@ -279,9 +275,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" dependencies = [ "cosmwasm-std", "schemars", @@ -558,6 +554,12 @@ version = "0.2.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "once_cell" version = "1.18.0" @@ -636,6 +638,23 @@ dependencies = [ "getrandom", ] +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + [[package]] name = "rfc6979" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index c6d3010..dbdd22b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,7 @@ members = [ # "contracts/core-contracts/*", "contracts/token-contracts/cw-hub-bnusd", "contracts/cw-common", - "contracts/mock-contracts/x-call-mock", - "libraries/rust/common",] + "contracts/mock-contracts/x-call-mock",] [profile.release] diff --git a/contracts/cw-common/Cargo.toml b/contracts/cw-common/Cargo.toml index 790fe42..9aa609c 100644 --- a/contracts/cw-common/Cargo.toml +++ b/contracts/cw-common/Cargo.toml @@ -15,6 +15,7 @@ hex ={ version = "0.4.3", default-features = false } cosmwasm-std = { version = "1.2.5", default-features = false } rlp = { version = "0.5.2", default-features = false } cosmwasm-schema = "1.2.6" -common = { path = "../../libraries/rust/common" } +cw-storage-plus = "1.1.0" +regex = "1.5.4" diff --git a/contracts/cw-common/src/data_types.rs b/contracts/cw-common/src/data_types.rs index 125811b..81135bf 100644 --- a/contracts/cw-common/src/data_types.rs +++ b/contracts/cw-common/src/data_types.rs @@ -1,11 +1,14 @@ -use common::rlp::{Decodable, Encodable, RlpStream}; use cosmwasm_schema::cw_serde; +use cosmwasm_std::Addr; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; + +use crate::network_address::NetworkAddress; #[cw_serde] pub struct CrossTransfer { pub method: String, - pub from: String, - pub to: String, + pub from: NetworkAddress, + pub to: NetworkAddress, pub value: u128, pub data: Vec, } @@ -13,7 +16,7 @@ pub struct CrossTransfer { #[cw_serde] pub struct CrossTransferRevert { pub method: String, - pub from: String, + pub from: Addr, pub value: u128, } @@ -22,19 +25,19 @@ impl Encodable for CrossTransfer { stream .begin_list(5) .append(&self.method) - .append(&self.from) - .append(&self.to) + .append(&self.from.to_string()) + .append(&self.to.to_string()) .append(&self.value) .append(&self.data); } } impl Decodable for CrossTransfer { - fn decode(rlp: &common::rlp::Rlp<'_>) -> Result { + fn decode(rlp: &Rlp<'_>) -> Result { Ok(Self { method: rlp.val_at(0)?, - from: rlp.val_at(1)?, - to: rlp.val_at(2)?, + from: NetworkAddress(rlp.val_at(1)?), + to: NetworkAddress(rlp.val_at(2)?), value: rlp.val_at(3)?, data: rlp.val_at(4)?, }) @@ -46,48 +49,18 @@ impl Encodable for CrossTransferRevert { stream .begin_list(3) .append(&self.method) - .append(&self.from) + .append(&self.from.to_string()) .append(&self.value); } } impl Decodable for CrossTransferRevert { - fn decode( - rlp: &common::rlp::Rlp<'_>, - ) -> Result { + fn decode(rlp: &Rlp<'_>) -> Result { + let from: String = rlp.val_at(1)?; Ok(Self { method: rlp.val_at(0)?, - from: rlp.val_at(1)?, + from: Addr::unchecked(from), value: rlp.val_at(2)?, }) } } -impl CrossTransfer { - pub fn encode_cross_transfer_message(self) -> Vec { - let method = "xCrossTransfer"; - - let mut calldata = RlpStream::new_list(5); - calldata.append(&method.to_string()); - calldata.append(&self.from); - calldata.append(&self.to); - calldata.append(&self.value.to_string()); - calldata.append(&self.data); - - let encoded = calldata.as_raw().to_vec(); - encoded - } -} - -impl CrossTransferRevert { - pub fn encode_cross_transfer_revert_message(self) -> Vec { - let method = "xCrossTransferRevert"; - - let mut calldata = RlpStream::new_list(3); - calldata.append(&method.to_string()); - calldata.append(&self.from); - calldata.append(&self.value.to_string()); - - let encoded = calldata.as_raw().to_vec(); - encoded - } -} diff --git a/contracts/cw-common/src/hub_token_msg.rs b/contracts/cw-common/src/hub_token_msg.rs index 24855af..e325cbe 100644 --- a/contracts/cw-common/src/hub_token_msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -1,4 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::Addr; + +use crate::network_address::NetworkAddress; #[cw_serde] pub struct InstantiateMsg { @@ -11,15 +14,15 @@ pub struct InstantiateMsg { pub enum ExecuteMsg { Setup { //TODO: x_call should be of addr type - x_call: String, - hub_address: String, + x_call: Addr, + hub_address: NetworkAddress, }, HandleCallMessage { - from: String, + from: NetworkAddress, data: Vec, }, CrossTransfer { - to: String, + to: NetworkAddress, amount: u128, data: Vec, }, diff --git a/contracts/cw-common/src/network_address.rs b/contracts/cw-common/src/network_address.rs index b4efd30..1bd5ec6 100644 --- a/contracts/cw-common/src/network_address.rs +++ b/contracts/cw-common/src/network_address.rs @@ -1,63 +1,147 @@ -use cosmwasm_std::StdResult; +use regex::Regex; +use std::str::FromStr; -pub struct NetworkAddress; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, StdError}; +use cw_storage_plus::{Key, KeyDeserialize, PrimaryKey}; -//TODO: use network address library from ibc-integration/contracts/cosmwasm-vm/cw-common -impl NetworkAddress { - pub fn parse_network_address(_str: &str) -> StdResult<(&str, &str)> { - let mut iter = _str.splitn(2, "://"); - let _ = iter.next().unwrap_or(""); - let mut account = iter.next().unwrap_or("").splitn(2, '/'); - let network = account.next().unwrap_or(""); - let address = account.next().unwrap_or(""); - Ok((network, address)) +#[cw_serde] +#[derive(Eq)] +pub struct NetId(String); + +impl From for NetId { + fn from(value: String) -> Self { + Self(value) } +} - pub fn parse_protocol_address(_str: &str) -> StdResult<(&str, &str)> { - let mut iter = _str.splitn(2, "://"); - let protocol = iter.next().unwrap_or(""); - let account = iter.next().unwrap_or(""); - Ok((protocol, account)) +impl ToString for NetId { + fn to_string(&self) -> String { + self.0.to_string() } +} - pub fn protocol_address(_str: &str) -> StdResult<&str> { - let mut iter = _str.splitn(2, "://"); - let _ = iter.next().unwrap_or(""); - let mut address = iter.next().unwrap_or("").splitn(2, '/'); - let network = address.next().unwrap_or(""); - Ok(network) +impl NetId { + pub fn as_str(&self) -> &str { + &self.0 } +} - pub fn get_network_address(protocol: &str, network: &str, account: &str) -> String { - format!("{}://{}/{}", protocol, network, account) +impl FromStr for NetId { + type Err = StdError; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_owned())) } } -mod tests { - #[test] - fn test_parse_btp_address() { - let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let (network, account) = super::NetworkAddress::parse_network_address(btp_address).unwrap(); - assert_eq!(network, "0x38.bsc"); - assert_eq!(account, "0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"); +impl<'a> PrimaryKey<'a> for NetId { + type Prefix = (); + + type SubPrefix = (); + + type Suffix = Self; + + type SuperSuffix = Self; + + fn key(&self) -> Vec { + vec![Key::Ref(self.0.as_bytes())] + } +} + +impl KeyDeserialize for NetId { + type Output = NetId; + + fn from_vec(value: Vec) -> cosmwasm_std::StdResult { + let result = String::from_utf8(value) + .map_err(StdError::invalid_utf8) + .unwrap(); + let net_id = NetId::from_str(&result).unwrap(); + Ok(net_id) + } +} + +#[cw_serde] +#[derive(Eq)] +pub struct NetworkAddress(pub String); + +impl NetworkAddress { + pub fn new(nid: &str, address: &str) -> Self { + Self(format!("{}/{}", nid, address)) } - #[test] - fn test_parse_network_address() { - let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let (network, account) = - super::NetworkAddress::parse_protocol_address(btp_address).unwrap(); - assert_eq!(network, "btp"); - assert_eq!( - account, - "0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798" - ); + pub fn nid(&self) -> NetId { + NetId(self.get_parts()[0].to_string()) } - #[test] - fn test_network_address() { - let btp_address = "btp://0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798"; - let network = super::NetworkAddress::protocol_address(btp_address).unwrap(); - assert_eq!(network, "0x38.bsc"); + pub fn is_empty(&self) -> bool { + self.0.is_empty() } + + pub fn account(&self) -> Addr { + Addr::unchecked(self.get_parts()[1]) + } + + pub fn get_parts(&self) -> Vec<&str> { + let parts = self.0.split('/').collect::>(); + parts + } + + pub fn parse_parts(&self) -> (NetId, Addr) { + let parts = self.0.split('/').collect::>(); + (NetId(parts[0].to_string()), Addr::unchecked(parts[1])) + } + + pub fn validate(&self) -> bool { + let address = self.get_parts()[1]; + if !address.is_ascii() { + return false; + } + let lowercase_address = address.to_lowercase(); + if !(lowercase_address.starts_with("hx") || lowercase_address.starts_with("cx")) { + return false; + } + let address_without_prefix = &lowercase_address[2..]; + let address_length = address_without_prefix.len(); + + if address_length == 40 { + // Check if the address contains only valid characters [0-9, a-f] + let regex = Regex::new("^[0-9a-f]+$").unwrap(); + return regex.is_match(address_without_prefix); + } + false + } +} + +impl ToString for NetworkAddress { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl FromStr for NetworkAddress { + type Err = StdError; + + fn from_str(s: &str) -> Result { + let parts = s.split('/').collect::>(); + if parts.len() != 2 { + return Err(StdError::GenericErr { + msg: "Invalid Network Address".to_owned(), + }); + } + let na = format!("{}/{}", parts[0], parts[1]); + Ok(Self(na)) + } +} + +#[test] +fn test_parse_btp_address() { + let btp_address = + NetworkAddress("0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798".to_string()); + let (network, account) = btp_address.parse_parts(); + assert_eq!(network, NetId("0x38.bsc".to_string())); + assert_eq!( + account, + Addr::unchecked("0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798") + ); } diff --git a/contracts/mock-contracts/x-call-mock/src/contract.rs b/contracts/mock-contracts/x-call-mock/src/contract.rs index 13203e8..7e9dd22 100644 --- a/contracts/mock-contracts/x-call-mock/src/contract.rs +++ b/contracts/mock-contracts/x-call-mock/src/contract.rs @@ -50,7 +50,10 @@ pub fn execute( data, hub_token, } => { - let call_message = ExecuteMsg::HandleCallMessage { from, data }; + let call_message = ExecuteMsg::HandleCallMessage { + from: cw_common::network_address::NetworkAddress(from), + data, + }; let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { contract_addr: hub_token, @@ -70,7 +73,7 @@ pub fn execute( pub fn query(_deps: Deps, _env: Env, _msg: XCallQuery) -> StdResult { match _msg { XCallQuery::GetNetworkAddress {} => Ok(to_binary( - "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e", + "0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e", )?), } } @@ -87,12 +90,11 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result { match msg.result { - cosmwasm_std::SubMsgResult::Ok(_) => {} + cosmwasm_std::SubMsgResult::Ok(_) => Ok(Response::default()), cosmwasm_std::SubMsgResult::Err(error) => { Err(StdError::GenericErr { msg: error }).map_err(Into::::into)? } } - Ok(Response::default()) } #[cfg(test)] diff --git a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml index 073db1c..ad149af 100644 --- a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml +++ b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml @@ -40,10 +40,10 @@ thiserror = { version = "1.0.40" } cw20 = { version = "1.0.1", default-features = false } cw20-base = { version = "1.0.1", features = ["library"] } bytes = "1.0" -common = { path = "../../../libraries/rust/common" } cw-common = { path = "../../cw-common" } hex = "0.4.3" debug_print = {workspace=true} +rlp = "0.5.2" # cw20-base = { path = "../../cw20-base" } diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json b/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json index 892d165..6031b81 100644 --- a/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json +++ b/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json @@ -38,10 +38,10 @@ ], "properties": { "hub_address": { - "type": "string" + "$ref": "#/definitions/NetworkAddress" }, "x_call": { - "type": "string" + "$ref": "#/definitions/Addr" } }, "additionalProperties": false @@ -71,7 +71,7 @@ } }, "from": { - "type": "string" + "$ref": "#/definitions/NetworkAddress" } }, "additionalProperties": false @@ -107,7 +107,7 @@ } }, "to": { - "type": "string" + "$ref": "#/definitions/NetworkAddress" } }, "additionalProperties": false @@ -115,7 +115,16 @@ }, "additionalProperties": false } - ] + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "NetworkAddress": { + "type": "string" + } + } }, "query": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json index 3964827..899fe07 100644 --- a/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json @@ -16,10 +16,10 @@ ], "properties": { "hub_address": { - "type": "string" + "$ref": "#/definitions/NetworkAddress" }, "x_call": { - "type": "string" + "$ref": "#/definitions/Addr" } }, "additionalProperties": false @@ -49,7 +49,7 @@ } }, "from": { - "type": "string" + "$ref": "#/definitions/NetworkAddress" } }, "additionalProperties": false @@ -85,7 +85,7 @@ } }, "to": { - "type": "string" + "$ref": "#/definitions/NetworkAddress" } }, "additionalProperties": false @@ -93,5 +93,14 @@ }, "additionalProperties": false } - ] + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "NetworkAddress": { + "type": "string" + } + } } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index f6a4e79..6211957 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,14 +1,16 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult}; +use cw2::set_contract_version; // use cw2::set_contract_version; use crate::constants::{ REPLY_MSG_SUCCESS, TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT, }; use crate::error::ContractError; -use crate::state::{HUB_ADDRESS, HUB_NET, NID, OWNER, X_CALL, X_CALL_BTP_ADDRESS}; -use bytes::Bytes; +use crate::state::{ + DESTINATION_TOKEN_ADDRESS, DESTINATION_TOKEN_NET, NID, OWNER, X_CALL, X_CALL_NETWORK_ADDRESS, +}; use cw_common::hub_token_msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use cw_common::x_call_msg::{XCallMsg, XCallQuery}; @@ -16,15 +18,13 @@ use cw20_base::contract::{execute_burn, execute_mint}; use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; use cw_common::network_address::NetworkAddress; -use common::rlp::Rlp; +use rlp::Rlp; use cw_common::data_types::{CrossTransfer, CrossTransferRevert}; -/* // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw-hub-bnusd"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -*/ #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -35,6 +35,9 @@ pub fn instantiate( ) -> Result { // create initial accounts // store token info using cw20-base format + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION) + .map_err(ContractError::Std)?; + let x_call_addr = deps .api .addr_validate(&msg.x_call) @@ -71,7 +74,7 @@ pub fn execute( execute::handle_call_message(deps, env, info, from, data) } ExecuteMsg::CrossTransfer { to, amount, data } => { - execute::cross_transfer(deps, env, info, to, amount, data.into()) + execute::cross_transfer(deps, env, info, to, amount, data) } } } @@ -102,11 +105,10 @@ mod execute { use std::str::from_utf8; use bytes::BytesMut; - use common::rlp::{decode, encode}; - use cosmwasm_std::{to_binary, CosmosMsg, Empty, QueryRequest, SubMsg, WasmQuery}; + use cosmwasm_std::{to_binary, Addr, CosmosMsg, Empty, Event, QueryRequest, SubMsg, WasmQuery}; + use cw_common::network_address::NetId; use debug_print::debug_println; - - use crate::constants::PROTOCOL; + use rlp::{decode, encode}; use super::*; @@ -114,11 +116,11 @@ mod execute { deps: DepsMut, _env: Env, info: MessageInfo, - x_call: String, - hub_address: String, + x_call: Addr, + hub_network_address: NetworkAddress, ) -> Result { deps.api - .addr_validate(&x_call) + .addr_validate(x_call.as_ref()) .map_err(ContractError::Std)?; X_CALL @@ -128,23 +130,23 @@ mod execute { let query_message = XCallQuery::GetNetworkAddress {}; let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: x_call, + contract_addr: x_call.to_string(), msg: to_binary(&query_message).map_err(ContractError::Std)?, }); - let x_call_btp_address: String = deps.querier.query(&query).map_err(ContractError::Std)?; + let x_call_network_address: NetworkAddress = + deps.querier.query(&query).map_err(ContractError::Std)?; - if x_call_btp_address.is_empty() { + if x_call_network_address.is_empty() { return Err(ContractError::AddressNotFound); } - let (nid, _) = NetworkAddress::parse_network_address(&x_call_btp_address)?; - let (hub_net, hub_address) = NetworkAddress::parse_network_address(&hub_address)?; - - //TODO: remove btp specification - X_CALL_BTP_ADDRESS.save(deps.storage, &x_call_btp_address.to_string())?; - NID.save(deps.storage, &nid.to_string())?; - HUB_ADDRESS.save(deps.storage, &hub_address.to_string())?; - HUB_NET.save(deps.storage, &hub_net.to_string())?; + let (nid, _) = x_call_network_address.parse_parts(); + let (hub_net, hub_address) = hub_network_address.parse_parts(); + debug_println!("setup {:?},{:?},{:?}", hub_net, hub_address, nid); + X_CALL_NETWORK_ADDRESS.save(deps.storage, &x_call_network_address)?; + NID.save(deps.storage, &nid)?; + DESTINATION_TOKEN_ADDRESS.save(deps.storage, &hub_address)?; + DESTINATION_TOKEN_NET.save(deps.storage, &hub_net)?; OWNER.save(deps.storage, &info.sender)?; Ok(Response::default()) @@ -154,7 +156,7 @@ mod execute { deps: DepsMut, env: Env, info: MessageInfo, - from: String, + from: NetworkAddress, data: Vec, ) -> Result { let xcall = X_CALL.load(deps.storage)?; @@ -193,40 +195,41 @@ mod execute { deps: DepsMut, env: Env, info: MessageInfo, - to: String, + to: NetworkAddress, amount: u128, - data: Bytes, + data: Vec, ) -> Result { let funds = info.funds.clone(); let nid = NID.load(deps.storage)?; - let hub_net: String = HUB_NET.load(deps.storage)?; - let hub_address: String = HUB_ADDRESS.load(deps.storage)?; + let hub_net: NetId = DESTINATION_TOKEN_NET.load(deps.storage)?; + let hub_address: Addr = DESTINATION_TOKEN_ADDRESS.load(deps.storage)?; + let sender = &info.sender; - let from = NetworkAddress::get_network_address(PROTOCOL, &nid, info.sender.as_ref()); + let from = NetworkAddress::new(&nid.to_string(), info.sender.as_ref()); let call_data = CrossTransfer { method: X_CROSS_TRANSFER.to_string(), from: from.clone(), - to, + to: to.clone(), value: amount, - data: data.to_vec(), + data, }; let rollback_data = CrossTransferRevert { method: X_CROSS_TRANSFER_REVERT.to_string(), - from, + from: sender.clone(), value: amount, }; - //TODO: rename to hub_token_address - let hub_btp_address = NetworkAddress::get_network_address(PROTOCOL, &hub_net, &hub_address); + + let hub_token_address = NetworkAddress::new(&hub_net.to_string(), hub_address.as_ref()); let call_message = XCallMsg::SendCallMessage { - to: hub_btp_address, + to: hub_token_address.to_string(), data: encode(&call_data).to_vec(), rollback: Some(encode(&rollback_data).to_vec()), }; let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { - contract_addr: X_CALL.load(deps.storage).unwrap(), + contract_addr: X_CALL.load(deps.storage).unwrap().to_string(), msg: to_binary(&call_message)?, funds, }); @@ -238,39 +241,47 @@ mod execute { execute_burn(deps, env, info, amount.into()).map_err(ContractError::Cw20BaseError)?; //TODO: emit a event log for cross transfer + let event = Event::new("CrossTransfer") + .add_attribute("from", from.to_string()) + .add_attribute("to", to.to_string()) + .add_attribute("value", amount.to_string()); + Ok(Response::new() .add_submessage(sub_message) - .add_attribute("method", "cross_transfer")) + .add_attribute("method", "cross_transfer") + .add_event(event)) } pub fn x_cross_transfer( deps: DepsMut, env: Env, info: MessageInfo, - from: String, + from: NetworkAddress, cross_transfer_data: CrossTransfer, ) -> Result { + debug_println!("this is {:?}", cross_transfer_data); let nid = NID.load(deps.storage)?; - let hub_net: String = HUB_NET.load(deps.storage)?; + let hub_net: NetId = DESTINATION_TOKEN_NET.load(deps.storage)?; - //TODO: rename hub address to DESTINATION_TOKEN_ADDRESS - let hub_address: String = HUB_ADDRESS.load(deps.storage)?; - let btp_address = NetworkAddress::get_network_address(PROTOCOL, &hub_net, &hub_address); + let destination_network_address: Addr = DESTINATION_TOKEN_ADDRESS.load(deps.storage)?; + let network_address = + NetworkAddress::new(&hub_net.to_string(), destination_network_address.as_ref()); - if from != btp_address { + debug_println!("this is {:?},{:?}", network_address, from); + if from != network_address { return Err(ContractError::WrongAddress {}); } //TODO: add a validation check for ICON address in network address library - let (net, account) = NetworkAddress::parse_network_address(&cross_transfer_data.to)?; + let (net, account) = NetworkAddress::parse_parts(&cross_transfer_data.to); debug_println!("this is {:?},{:?}", net, nid); if net != nid { return Err(ContractError::WrongNetwork); } deps.api - .addr_validate(account) + .addr_validate(account.as_ref()) .map_err(ContractError::Std)?; let res = execute_mint( @@ -282,49 +293,43 @@ mod execute { ) .expect("Fail to mint"); + let event = Event::new("XCrossTransfer") + .add_attribute("from", cross_transfer_data.from.to_string()) + .add_attribute("to", cross_transfer_data.to.to_string()) + .add_attribute("value", cross_transfer_data.value.to_string()); + //TODO: add event for cross transfer with relevant parameters - Ok(res.add_attribute("method", "x_cross_transfer")) + Ok(res + .add_attribute("method", "x_cross_transfer") + .add_event(event)) } pub fn x_cross_transfer_revert( deps: DepsMut, env: Env, info: MessageInfo, - from: String, + from: NetworkAddress, cross_transfer_revert_data: CrossTransferRevert, ) -> Result { - let nid = NID.load(deps.storage)?; - let x_call_btp_address = X_CALL_BTP_ADDRESS.load(deps.storage)?; - debug_println!("this is {:?}, {:?}", from, x_call_btp_address); - - if from != x_call_btp_address { - return Err(ContractError::OnlyCallService); - } - - //TODO: from can be a native archway address - let (net, account) = - NetworkAddress::parse_network_address(&cross_transfer_revert_data.from)?; - debug_println!("this is {:?},{:?}", net, nid); - - //TODO: this can be removed - if net != nid { - return Err(ContractError::InvalidBTPAddress); - } - + debug_println!("this is {:?},{:?}", cross_transfer_revert_data, from); deps.api - .addr_validate(account) + .addr_validate(cross_transfer_revert_data.from.as_ref()) .map_err(ContractError::Std)?; let res = execute_mint( deps, env, info, - account.to_string(), + cross_transfer_revert_data.from.to_string(), cross_transfer_revert_data.value.into(), ) .expect("Fail to mint"); - - Ok(res) + let event = Event::new("XCrossTransferRevert") + .add_attribute("from", cross_transfer_revert_data.from) + .add_attribute("value", cross_transfer_revert_data.value.to_string()); + Ok(res + .add_attribute("method", "x_cross_transfer_revert") + .add_event(event)) } } @@ -333,15 +338,17 @@ mod rlp_test { use std::str::from_utf8; use bytes::BytesMut; - use common::rlp::{decode, encode, Rlp}; - use cw_common::data_types::CrossTransfer; + use cw_common::{data_types::CrossTransfer, network_address::NetworkAddress}; + use rlp::{decode, encode, Rlp}; #[test] fn encodetest() { let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - to: "btp://btp/archway123fdth".to_string(), + from: NetworkAddress( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), + to: NetworkAddress("0x38.bsc/archway123fdth".to_string()), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, @@ -371,11 +378,11 @@ mod rlp_test { mod tests { use std::vec; - use common::rlp::encode; use cosmwasm_std::{ testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}, - to_binary, ContractResult, MemoryStorage, OwnedDeps, SystemResult, WasmQuery, + to_binary, Addr, ContractResult, MemoryStorage, OwnedDeps, SystemResult, WasmQuery, }; + use rlp::encode; use super::*; @@ -389,14 +396,16 @@ mod tests { let info = mock_info("archway123fdth", &[]); let msg = InstantiateMsg { x_call: "archway123fdth".to_owned(), - hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + hub_address: "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), }; let _res: Response = instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); let setup_message = ExecuteMsg::Setup { - x_call: "archway123fdth".to_owned(), - hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + x_call: Addr::unchecked("archway123fdth".to_owned()), + hub_address: NetworkAddress( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), }; deps.querier.update_wasm(|r| match r { @@ -404,7 +413,7 @@ mod tests { contract_addr: _, msg: _, } => SystemResult::Ok(ContractResult::Ok( - to_binary("btp://0x38.bsc/archway192kfvz2vrxv4hhaz3tjdk39maa69xs75n5cea8").unwrap(), + to_binary("0x38.bsc/archway192kfvz2vrxv4hhaz3tjdk39maa69xs75n5cea8").unwrap(), )), _ => todo!(), }); @@ -425,8 +434,10 @@ mod tests { let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - to: "btp://0x38.bsc/archway123fdth".to_string(), + from: NetworkAddress( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), + to: NetworkAddress("0x38.bsc/archway123fdth".to_string()), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, @@ -441,7 +452,9 @@ mod tests { env, info, ExecuteMsg::HandleCallMessage { - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + from: NetworkAddress( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), data, }, ) @@ -454,8 +467,10 @@ mod tests { let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - to: "btp://0x38.bsc/archway123fdth".to_string(), + from: NetworkAddress( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), + to: NetworkAddress("0x38.bsc/archway123fdth".to_string()), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, @@ -470,7 +485,9 @@ mod tests { env.clone(), info.clone(), ExecuteMsg::HandleCallMessage { - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + from: NetworkAddress( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), data, }, ) @@ -481,7 +498,9 @@ mod tests { env, info, ExecuteMsg::CrossTransfer { - to: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + to: NetworkAddress( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), amount: 1000, data: vec![1, 2, 3, 4, 5], }, @@ -495,7 +514,9 @@ mod tests { let call_data = CrossTransferRevert { method: "xCrossTransferRevert".to_string(), - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + from: Addr::unchecked( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), value: 1000, }; @@ -507,7 +528,9 @@ mod tests { env, info, ExecuteMsg::HandleCallMessage { - from: "btp://0x38.bsc/archway192kfvz2vrxv4hhaz3tjdk39maa69xs75n5cea8".to_owned(), + from: NetworkAddress( + "0x38.bsc/archway192kfvz2vrxv4hhaz3tjdk39maa69xs75n5cea8".to_owned(), + ), data, }, ) diff --git a/contracts/token-contracts/cw-hub-bnusd/src/error.rs b/contracts/token-contracts/cw-hub-bnusd/src/error.rs index 79217a3..c79bf75 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/error.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/error.rs @@ -11,7 +11,7 @@ pub enum ContractError { #[error("Wrong Address")] WrongAddress, #[error("Invalid BTP Address")] - InvalidBTPAddress, + InvalidNetworkAddress, #[error("Wrong Network")] WrongNetwork, #[error("Invalid to Address")] diff --git a/contracts/token-contracts/cw-hub-bnusd/src/state.rs b/contracts/token-contracts/cw-hub-bnusd/src/state.rs index 1a01145..5ad8ff8 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/state.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/state.rs @@ -1,4 +1,5 @@ use cosmwasm_std::Addr; +use cw_common::network_address::{NetId, NetworkAddress}; use cw_storage_plus::{Item, Map}; pub const CONNECTED_CHAINS: &str = "connected_chains"; @@ -9,8 +10,8 @@ pub const CROSSCHAINSUPPLY: Map<&String, u128> = Map::new(CROSS_CHAIN_SUPPLY); pub const CONNECTEDCHAINS: Item> = Item::new(CONNECTED_CHAINS); pub const OWNER: Item = Item::new("owner"); -pub const X_CALL: Item = Item::new("xCall"); -pub const X_CALL_BTP_ADDRESS: Item = Item::new("xCallBTPAddress"); -pub const NID: Item = Item::new("nid"); -pub const HUB_ADDRESS: Item = Item::new("hubAddress"); -pub const HUB_NET: Item = Item::new("hubNet"); +pub const X_CALL: Item = Item::new("xCall"); +pub const X_CALL_NETWORK_ADDRESS: Item = Item::new("xCallBTPAddress"); +pub const NID: Item = Item::new("nid"); +pub const DESTINATION_TOKEN_ADDRESS: Item = Item::new("hubAddress"); +pub const DESTINATION_TOKEN_NET: Item = Item::new("hubNet"); diff --git a/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs index 9dc0310..34f041e 100644 --- a/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs +++ b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs @@ -47,14 +47,15 @@ pub fn setup_context() -> TestContext { } mod instantiate_test { - use common::rlp::encode; use cosmwasm_std::{Addr, Empty}; use cw_common::{ data_types::{CrossTransfer, CrossTransferRevert}, hub_token_msg::{self, ExecuteMsg}, + network_address::NetworkAddress, x_call_msg::{self, XCallMsg}, }; use cw_multi_test::{Contract, ContractWrapper, Executor}; + use rlp::encode; use x_call_mock::contract::{execute, instantiate, query}; use super::*; @@ -96,7 +97,7 @@ mod instantiate_test { ctx.sender.clone(), &InstantiateMsg { x_call: Addr::unchecked(_x_call_address).into_string(), - hub_address: "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" + hub_address: "0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" .to_owned(), }, &[], @@ -115,9 +116,10 @@ mod instantiate_test { ctx.sender.clone(), ctx.get_hubtoken_app(), &ExecuteMsg::Setup { - x_call: Addr::unchecked(ctx.get_xcall_app()).into_string(), - hub_address: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" - .to_owned(), + x_call: Addr::unchecked(ctx.get_xcall_app()), + hub_address: NetworkAddress( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), }, &[], ) @@ -129,8 +131,10 @@ mod instantiate_test { fn handle_call_message(mut ctx: TestContext) -> TestContext { let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - to: "btp://0x1.icon/archway123fdth".to_string(), + from: NetworkAddress( + "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + ), + to: NetworkAddress("0x1.icon/archway123fdth".to_string()), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, @@ -146,8 +150,7 @@ mod instantiate_test { ctx.sender.clone(), ctx.get_xcall_app(), &XCallMsg::TestHandleCallMessage { - from: "btp://0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" - .to_owned(), + from: "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), data, hub_token: ctx.get_hubtoken_app().into_string(), }, @@ -157,7 +160,7 @@ mod instantiate_test { let call_data = CrossTransferRevert { method: "xCrossTransferRevert".to_string(), - from: "btp://0x1.icon/".to_owned() + ctx.sender.as_str(), + from: ctx.sender.clone(), value: 1000, }; @@ -170,8 +173,7 @@ mod instantiate_test { ctx.sender.clone(), ctx.get_xcall_app(), &XCallMsg::TestHandleCallMessage { - from: "btp://0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" - .to_owned(), + from: "0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), data, hub_token: ctx.get_hubtoken_app().into_string(), }, @@ -189,7 +191,9 @@ mod instantiate_test { ctx.sender.clone(), ctx.get_hubtoken_app(), &ExecuteMsg::CrossTransfer { - to: "archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + to: NetworkAddress( + "0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_string(), + ), amount: 100, data: vec![], }, diff --git a/libraries/rust/common/Cargo.toml b/libraries/rust/common/Cargo.toml deleted file mode 100644 index 6b706af..0000000 --- a/libraries/rust/common/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "common" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bytes = { version = "1.4.0", default-features = false } -rlp-derive = { version = "0.1.0", default-features = false } -cw20 = { version = "1.0.1", default-features = false } -rustc-hex = { version = "2.1.0", default-features = false } -serde = { version = "1.0.156", default-features = false,features = ["derive"] } -hex ={ version = "0.4.3", default-features = false } -cosmwasm-std = { version = "1.2.5", default-features = false } -rlp = { version = "0.5.2", default-features = false } diff --git a/libraries/rust/common/src/lib.rs b/libraries/rust/common/src/lib.rs deleted file mode 100644 index c465d3b..0000000 --- a/libraries/rust/common/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod rlp; diff --git a/libraries/rust/common/src/rlp/error.rs b/libraries/rust/common/src/rlp/error.rs deleted file mode 100644 index 558b274..0000000 --- a/libraries/rust/common/src/rlp/error.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2020 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::fmt; -#[cfg(feature = "std")] -use std::error::Error as StdError; - -#[derive(Debug, PartialEq, Eq, Clone)] -/// Error concerning the RLP decoder. -pub enum DecoderError { - /// Data has additional bytes at the end of the valid RLP fragment. - RlpIsTooBig, - /// Data has too few bytes for valid RLP. - RlpIsTooShort, - /// Expect an encoded list, RLP was something else. - RlpExpectedToBeList, - /// Expect encoded data, RLP was something else. - RlpExpectedToBeData, - /// Expected a different size list. - RlpIncorrectListLen, - /// Data length number has a prefixed zero byte, invalid for numbers. - RlpDataLenWithZeroPrefix, - /// List length number has a prefixed zero byte, invalid for numbers. - RlpListLenWithZeroPrefix, - /// Non-canonical (longer than necessary) representation used for data or list. - RlpInvalidIndirection, - /// Declared length is inconsistent with data specified after. - RlpInconsistentLengthAndData, - /// Declared length is invalid and results in overflow - RlpInvalidLength, - /// Custom rlp decoding error. - Custom(&'static str), -} - -#[cfg(feature = "std")] -impl StdError for DecoderError { - fn description(&self) -> &str { - "builder error" - } -} - -impl fmt::Display for DecoderError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self, f) - } -} diff --git a/libraries/rust/common/src/rlp/impls.rs b/libraries/rust/common/src/rlp/impls.rs deleted file mode 100644 index a01f526..0000000 --- a/libraries/rust/common/src/rlp/impls.rs +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2020 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use bytes::{Bytes, BytesMut}; -use core::{ - iter::{empty, once}, - mem, str, -}; - -use crate::rlp::{ - error::DecoderError, - rlpin::Rlp, - stream::RlpStream, - traits::{Decodable, Encodable}, -}; - -pub fn decode_usize(bytes: &[u8]) -> Result { - match bytes.len() { - l if l <= mem::size_of::() => { - if bytes[0] == 0 { - return Err(DecoderError::RlpInvalidIndirection); - } - let mut res = 0usize; - for (i, byte) in bytes.iter().enumerate().take(l) { - let shift = (l - 1 - i) * 8; - res += (*byte as usize) << shift; - } - Ok(res) - } - _ => Err(DecoderError::RlpIsTooBig), - } -} - -impl Encodable for Box { - fn rlp_append(&self, s: &mut RlpStream) { - Encodable::rlp_append(&**self, s) - } -} - -impl Decodable for Box { - fn decode(rlp: &Rlp) -> Result { - T::decode(rlp).map(Box::new) - } -} - -impl Encodable for bool { - fn rlp_append(&self, s: &mut RlpStream) { - let as_uint = u8::from(*self); - Encodable::rlp_append(&as_uint, s); - } -} - -impl Decodable for bool { - fn decode(rlp: &Rlp) -> Result { - let as_uint = ::decode(rlp)?; - match as_uint { - 0 => Ok(false), - 1 => Ok(true), - _ => Err(DecoderError::Custom("invalid boolean value")), - } - } -} - -impl<'a> Encodable for &'a [u8] { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(self); - } -} - -impl Encodable for Vec { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(self); - } -} - -impl Decodable for Vec { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder().decode_value(|bytes| Ok(bytes.to_vec())) - } -} - -impl Encodable for Bytes { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(self); - } -} - -impl Decodable for Bytes { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder() - .decode_value(|bytes| Ok(Bytes::copy_from_slice(bytes))) - } -} - -impl Encodable for BytesMut { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(self); - } -} - -impl Decodable for BytesMut { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder().decode_value(|bytes| Ok(bytes.into())) - } -} - -impl Encodable for Option -where - T: Encodable, -{ - fn rlp_append(&self, s: &mut RlpStream) { - match *self { - None => { - s.begin_list(0); - } - Some(ref value) => { - s.begin_list(1); - s.append(value); - } - } - } -} - -impl Decodable for Option -where - T: Decodable, -{ - fn decode(rlp: &Rlp) -> Result { - let items = rlp.item_count()?; - match items { - 1 => rlp.val_at(0).map(Some), - 0 => Ok(None), - _ => Err(DecoderError::RlpIncorrectListLen), - } - } -} - -impl Encodable for u8 { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_iter(once(*self)); - } -} - -impl Decodable for u8 { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder().decode_value(|bytes| match bytes.len() { - 1 => Ok(bytes[0]), - 0 => Ok(0), - _ => Err(DecoderError::RlpIsTooBig), - }) - } -} - -macro_rules! impl_encodable_for_u { - ($name: ident) => { - impl Encodable for $name { - fn rlp_append(&self, s: &mut RlpStream) { - let bytes = |mut v: $name| -> Vec { - if v == 0 { - vec![0] - } else { - let mut buffer: Vec = vec![0_u8; 16]; - for i in (0..=15).rev() { - let b: u8 = (v & 0xff) as u8; - buffer[i] = b; - v >>= 8; - if v == 0 && (b & 0x80) == 0 { - return buffer[i..].to_vec(); - } - } - buffer - } - }(*self); - s.encoder().encode_value(&bytes); - } - } - }; -} - -macro_rules! impl_decodable_for_u { - ($name: ident) => { - impl Decodable for $name { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder().decode_value(|bytes| match bytes.len() { - 0 | 1 => u8::decode(rlp).map(|v| v as $name), - l if l <= mem::size_of::<$name>() => { - let mut res = 0 as $name; - for (i, byte) in bytes.iter().enumerate().take(l) { - let shift = (l - 1 - i) * 8; - res += (*byte as $name) << shift; - } - Ok(res) - } - _ => Err(DecoderError::RlpIsTooBig), - }) - } - } - }; -} - -impl_encodable_for_u!(u16); -impl_encodable_for_u!(u32); -impl_encodable_for_u!(u64); -impl_encodable_for_u!(u128); - -impl_decodable_for_u!(u16); -impl_decodable_for_u!(u32); -impl_decodable_for_u!(u64); -impl_decodable_for_u!(u128); - -impl Encodable for usize { - fn rlp_append(&self, s: &mut RlpStream) { - (*self as u64).rlp_append(s); - } -} - -impl Decodable for usize { - fn decode(rlp: &Rlp) -> Result { - u64::decode(rlp).map(|value| value as usize) - } -} - -impl<'a> Encodable for &'a str { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(self.as_bytes()); - } -} - -impl Encodable for String { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(self.as_bytes()); - } -} - -impl Decodable for String { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder().decode_value(|bytes| { - match str::from_utf8(bytes) { - Ok(s) => Ok(s.to_owned()), - // consider better error type here - Err(_err) => Err(DecoderError::RlpExpectedToBeData), - } - }) - } -} - -impl Encodable for i8 { - fn rlp_append(&self, s: &mut RlpStream) { - Encodable::rlp_append(&(*self as u8), s); - } -} - -impl Decodable for i8 { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder() - .decode_value(|bytes| match bytes.len() as u32 { - len if len == u8::BITS / 8 => Ok(bytes[0] as i8), - _ => Err(DecoderError::RlpInvalidLength), - }) - } -} diff --git a/libraries/rust/common/src/rlp/mod.rs b/libraries/rust/common/src/rlp/mod.rs deleted file mode 100644 index d2d1b67..0000000 --- a/libraries/rust/common/src/rlp/mod.rs +++ /dev/null @@ -1,126 +0,0 @@ -#![allow(unused)] -// Copyright 2020 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Recursive Length Prefix serialization crate. -//! -//! Allows encoding, decoding, and view onto rlp-slice -//! -//! # What should you use when? -//! -//! ### Use `encode` function when: -//! * You want to encode something inline. -//! * You do not work on big set of data. -//! * You want to encode whole data structure at once. -//! -//! ### Use `decode` function when: -//! * You want to decode something inline. -//! * You do not work on big set of data. -//! * You want to decode whole rlp at once. -//! -//! ### Use `RlpStream` when: -//! * You want to encode something in portions. -//! * You encode a big set of data. -//! -//! ### Use `Rlp` when: -//! * You need to handle data corruption errors. -//! * You are working on input data. -//! * You want to get view onto rlp-slice. -//! * You don't want to decode whole rlp at once. - -mod error; -mod impls; -mod nullable; -mod rlpin; -mod stream; -mod traits; - -use bytes::BytesMut; -use core::borrow::Borrow; - -pub use self::{ - error::DecoderError, - rlpin::{PayloadInfo, Prototype, Rlp, RlpIterator}, - stream::RlpStream, - traits::{Decodable, Encodable}, -}; - -/// The RLP encoded empty data (used to mean "null value"). -pub const NULL_RLP: [u8; 2] = [0xf8, 0x00]; -/// The RLP encoded empty list. -pub const EMPTY_LIST_RLP: [u8; 1] = [0xC0; 1]; -pub use nullable::Nullable; -/// Shortcut function to decode trusted rlp -/// -/// ``` -/// use common::rlp::{self}; -/// let data = vec![0x83, b'c', b'a', b't']; -/// let animal: String = rlp::decode(&data).expect("could not decode"); -/// assert_eq!(animal, "cat".to_owned()); -/// ``` -pub fn decode(bytes: &[u8]) -> Result -where - T: Decodable, -{ - let rlp = Rlp::new(bytes); - rlp.as_val() -} - -pub fn decode_list(bytes: &[u8]) -> Vec -where - T: Decodable, -{ - let rlp = Rlp::new(bytes); - rlp.as_list().expect("trusted rlp should be valid") -} - -/// Shortcut function to encode structure into rlp. -/// -/// ``` -/// use common::rlp::{self}; -/// let animal = "cat"; -/// let out = rlp::encode(&animal); -/// assert_eq!(out, vec![0x83, b'c', b'a', b't']); -/// ``` -pub fn encode(object: &E) -> BytesMut -where - E: Encodable, -{ - let mut stream = RlpStream::new(); - stream.append(object); - stream.out() -} - -pub fn encode_list(object: &[K]) -> BytesMut -where - E: Encodable, - K: Borrow, -{ - let mut stream = RlpStream::new(); - stream.append_list(object); - stream.out() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_solidity_encoding() { - let expected = "00"; - let zero: u64 = 0; - let tag: u64 = 128; - let false_val = false; - let result = super::encode(&zero).to_vec(); - assert_eq!(expected, hex::encode(result)); - let result = super::encode(&false_val).to_vec(); - assert_eq!(expected, hex::encode(result)); - let result = super::encode(&tag).to_vec(); - assert_eq!("820080", hex::encode(result)); - } -} diff --git a/libraries/rust/common/src/rlp/nullable.rs b/libraries/rust/common/src/rlp/nullable.rs deleted file mode 100644 index b124232..0000000 --- a/libraries/rust/common/src/rlp/nullable.rs +++ /dev/null @@ -1,44 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use super::*; - -#[derive(Clone, Default, PartialEq, Eq, Debug, Serialize, Deserialize)] -pub struct Nullable(pub Option); - -impl Nullable { - pub fn new(item: Option) -> Self { - Self(item) - } - - pub fn is_some(&self) -> bool { - self.0.is_some() - } - - pub fn is_none(&self) -> bool { - self.0.is_none() - } - - pub fn get(&self) -> Result<&T, &'static str> { - self.0.as_ref().ok_or("object is null") - } -} - -impl Decodable for Nullable { - fn decode(rlp: &Rlp) -> Result { - if rlp.is_null() { - Ok(Self(None)) - } else { - Ok(Self(Some(rlp.as_val()?))) - } - } -} - -impl Encodable for Nullable { - fn rlp_append(&self, stream: &mut RlpStream) { - if self.is_none() { - stream.append_null_internal(); - } else { - stream.append_internal(self.get().unwrap()); - } - } -} diff --git a/libraries/rust/common/src/rlp/rlpin.rs b/libraries/rust/common/src/rlp/rlpin.rs deleted file mode 100644 index f8abba5..0000000 --- a/libraries/rust/common/src/rlp/rlpin.rs +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright 2020 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::{cell::Cell, fmt}; - -use rustc_hex::ToHex; - -use crate::rlp::{error::DecoderError, impls::decode_usize, traits::Decodable}; - -/// rlp offset -#[derive(Copy, Clone, Debug)] -struct OffsetCache { - index: usize, - offset: usize, -} - -impl OffsetCache { - const fn new(index: usize, offset: usize) -> OffsetCache { - OffsetCache { index, offset } - } -} - -#[derive(Debug)] -/// RLP prototype -pub enum Prototype { - /// Empty - Null, - /// Value - Data(usize), - /// List - List(usize), -} - -/// Stores basic information about item -#[derive(Debug)] -pub struct PayloadInfo { - /// Header length in bytes - pub header_len: usize, - /// Value length in bytes - pub value_len: usize, -} - -fn calculate_payload_info( - header_bytes: &[u8], - len_of_len: usize, -) -> Result { - let header_len = 1 + len_of_len; - match header_bytes.get(1) { - Some(&0) => return Err(DecoderError::RlpDataLenWithZeroPrefix), - None => return Err(DecoderError::RlpIsTooShort), - _ => (), - } - if header_bytes.len() < header_len { - return Err(DecoderError::RlpIsTooShort); - } - let value_len = decode_usize(&header_bytes[1..header_len])?; - if value_len <= 55 { - return Err(DecoderError::RlpInvalidIndirection); - } - Ok(PayloadInfo::new(header_len, value_len)) -} - -impl PayloadInfo { - const fn new(header_len: usize, value_len: usize) -> PayloadInfo { - PayloadInfo { - header_len, - value_len, - } - } - - /// Total size of the RLP. - pub fn total(&self) -> usize { - self.header_len + self.value_len - } - - /// Create a new object from the given bytes RLP. The bytes - pub fn from(header_bytes: &[u8]) -> Result { - let l = *header_bytes.first().ok_or(DecoderError::RlpIsTooShort)?; - - if l <= 0x7f { - Ok(PayloadInfo::new(0, 1)) - } else if l <= 0xb7 { - Ok(PayloadInfo::new(1, l as usize - 0x80)) - } else if l <= 0xbf { - let len_of_len = l as usize - 0xb7; - calculate_payload_info(header_bytes, len_of_len) - } else if l <= 0xf7 { - Ok(PayloadInfo::new(1, l as usize - 0xc0)) - } else if l == 0xf8 && header_bytes[1] == 0 { - Ok(PayloadInfo::new(1, 1)) - } else { - let len_of_len = l as usize - 0xf7; - calculate_payload_info(header_bytes, len_of_len) - } - } -} - -/// Data-oriented view onto rlp-slice. -/// -/// This is an immutable structure. No operations change it. -/// -/// Should be used in places where, error handling is required, -/// eg. on input -#[derive(Debug, Clone)] -pub struct Rlp<'a> { - bytes: &'a [u8], - offset_cache: Cell>, - count_cache: Cell>, -} - -impl<'a> fmt::Display for Rlp<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match self.prototype() { - Ok(Prototype::Null) => write!(f, "null"), - Ok(Prototype::Data(_)) => { - write!(f, "\"0x{}\"", self.data().unwrap().to_hex::()) - } - Ok(Prototype::List(len)) => { - write!(f, "[")?; - for i in 0..len - 1 { - write!(f, "{}, ", self.at(i).unwrap())?; - } - write!(f, "{}", self.at(len - 1).unwrap())?; - write!(f, "]") - } - Err(err) => write!(f, "{err:?}"), - } - } -} - -impl<'a> Rlp<'a> { - pub const fn new(bytes: &'a [u8]) -> Rlp<'a> { - Rlp { - bytes, - offset_cache: Cell::new(None), - count_cache: Cell::new(None), - } - } - - pub fn as_raw<'view>(&'view self) -> &'a [u8] - where - 'a: 'view, - { - self.bytes - } - - pub fn prototype(&self) -> Result { - // optimize? && return appropriate errors - if self.is_data() { - Ok(Prototype::Data(self.size())) - } else if self.is_list() { - self.item_count().map(Prototype::List) - } else { - Ok(Prototype::Null) - } - } - - pub fn payload_info(&self) -> Result { - BasicDecoder::payload_info(self.bytes) - } - - pub fn data<'view>(&'view self) -> Result<&'a [u8], DecoderError> - where - 'a: 'view, - { - let pi = BasicDecoder::payload_info(self.bytes)?; - Ok(&self.bytes[pi.header_len..(pi.header_len + pi.value_len)]) - } - - pub fn item_count(&self) -> Result { - if self.is_list() { - match self.count_cache.get() { - Some(c) => Ok(c), - None => { - let c = self.iter().count(); - self.count_cache.set(Some(c)); - Ok(c) - } - } - } else { - Err(DecoderError::RlpExpectedToBeList) - } - } - - pub fn size(&self) -> usize { - if self.is_data() { - // TODO: No panic on malformed data, but ideally would Err on no PayloadInfo. - BasicDecoder::payload_info(self.bytes) - .map(|b| b.value_len) - .unwrap_or(0) - } else { - 0 - } - } - - /// Returns an Rlp item in a list at the given index. - /// - /// Returns an error if this Rlp is not a list or if the index is out of range. - pub fn at<'view>(&'view self, index: usize) -> Result, DecoderError> - where - 'a: 'view, - { - let (rlp, _offset) = self.at_with_offset(index)?; - Ok(rlp) - } - - /// Returns an Rlp item in a list at the given index along with the byte offset into the - /// raw data slice. - /// - /// Returns an error if this Rlp is not a list or if the index is out of range. - pub fn at_with_offset<'view>( - &'view self, - index: usize, - ) -> Result<(Rlp<'a>, usize), DecoderError> - where - 'a: 'view, - { - if !self.is_list() { - return Err(DecoderError::RlpExpectedToBeList); - } - - // move to cached position if its index is less or equal to - // current search index, otherwise move to beginning of list - let cache = self.offset_cache.get(); - let (bytes, indexes_to_skip, bytes_consumed) = match cache { - Some(ref cache) if cache.index <= index => ( - Rlp::consume(self.bytes, cache.offset)?, - index - cache.index, - cache.offset, - ), - _ => { - let (bytes, consumed) = self.consume_list_payload()?; - (bytes, index, consumed) - } - }; - - // skip up to x items - let (bytes, consumed) = Rlp::consume_items(bytes, indexes_to_skip)?; - - // update the cache - let offset = bytes_consumed + consumed; - self.offset_cache.set(Some(OffsetCache::new(index, offset))); - - // construct new rlp - let found = BasicDecoder::payload_info(bytes)?; - Ok(( - Rlp::new(&bytes[0..found.header_len + found.value_len]), - offset, - )) - } - - pub fn is_null(&self) -> bool { - self.bytes.is_empty() || (self.bytes[0] == 0xf8 && self.bytes[1] == 0) - } - - pub fn is_empty(&self) -> bool { - !self.is_null() && (self.bytes[0] == 0xc0 || self.bytes[0] == 0x80) - } - - pub fn is_list(&self) -> bool { - !self.is_null() && self.bytes[0] >= 0xc0 - } - - pub fn is_data(&self) -> bool { - !self.is_null() && self.bytes[0] < 0xc0 - } - - pub fn is_int(&self) -> bool { - if self.is_null() { - return false; - } - - match self.bytes[0] { - 0..=0x80 => true, - 0x81..=0xb7 => self.bytes[1] != 0, - b @ 0xb8..=0xbf => { - let payload_idx = 1 + b as usize - 0xb7; - payload_idx < self.bytes.len() && self.bytes[payload_idx] != 0 - } - _ => false, - } - } - - pub fn iter<'view>(&'view self) -> RlpIterator<'a, 'view> - where - 'a: 'view, - { - self.into_iter() - } - - pub fn as_val(&self) -> Result - where - T: Decodable, - { - T::decode(self) - } - - pub fn as_list(&self) -> Result, DecoderError> - where - T: Decodable, - { - self.iter().map(|rlp| rlp.as_val()).collect() - } - - pub fn val_at(&self, index: usize) -> Result - where - T: Decodable, - { - self.at(index)?.as_val() - } - - pub fn list_at(&self, index: usize) -> Result, DecoderError> - where - T: Decodable, - { - self.at(index)?.as_list() - } - - pub fn decoder(&self) -> BasicDecoder { - BasicDecoder::new(self.bytes) - } - - /// consumes first found prefix - fn consume_list_payload(&self) -> Result<(&'a [u8], usize), DecoderError> { - let item = BasicDecoder::payload_info(self.bytes)?; - if self.bytes.len() < (item.header_len + item.value_len) { - return Err(DecoderError::RlpIsTooShort); - } - Ok(( - &self.bytes[item.header_len..item.header_len + item.value_len], - item.header_len, - )) - } - - /// consumes fixed number of items - fn consume_items(bytes: &'a [u8], items: usize) -> Result<(&'a [u8], usize), DecoderError> { - let mut result = bytes; - let mut consumed = 0; - for _ in 0..items { - let i = BasicDecoder::payload_info(result)?; - let to_consume = i.header_len + i.value_len; - result = Rlp::consume(result, to_consume)?; - consumed += to_consume; - } - Ok((result, consumed)) - } - - /// consumes slice prefix of length `len` - fn consume(bytes: &'a [u8], len: usize) -> Result<&'a [u8], DecoderError> { - if bytes.len() >= len { - Ok(&bytes[len..]) - } else { - Err(DecoderError::RlpIsTooShort) - } - } -} - -/// Iterator over rlp-slice list elements. -pub struct RlpIterator<'a, 'view> -where - 'a: 'view, -{ - rlp: &'view Rlp<'a>, - index: usize, -} - -impl<'a, 'view> IntoIterator for &'view Rlp<'a> -where - 'a: 'view, -{ - type Item = Rlp<'a>; - type IntoIter = RlpIterator<'a, 'view>; - - fn into_iter(self) -> Self::IntoIter { - RlpIterator { - rlp: self, - index: 0, - } - } -} - -impl<'a, 'view> Iterator for RlpIterator<'a, 'view> { - type Item = Rlp<'a>; - - fn next(&mut self) -> Option> { - let index = self.index; - let result = self.rlp.at(index).ok(); - self.index += 1; - result - } -} - -impl<'a, 'view> ExactSizeIterator for RlpIterator<'a, 'view> { - fn len(&self) -> usize { - self.rlp.item_count().unwrap_or(0) - } -} - -pub struct BasicDecoder<'a> { - rlp: &'a [u8], -} - -impl<'a> BasicDecoder<'a> { - pub const fn new(rlp: &'a [u8]) -> BasicDecoder<'a> { - BasicDecoder { rlp } - } - - /// Return first item info. - fn payload_info(bytes: &[u8]) -> Result { - let item = PayloadInfo::from(bytes)?; - match item.header_len.checked_add(item.value_len) { - Some(x) if x <= bytes.len() => Ok(item), - _ => Err(DecoderError::RlpIsTooShort), - } - } - - pub fn decode_value(&self, f: F) -> Result - where - F: Fn(&[u8]) -> Result, - { - let bytes = self.rlp; - - let l = *bytes.first().ok_or(DecoderError::RlpIsTooShort)?; - - if l <= 0x7f { - Ok(f(&[l])?) - } else if l <= 0xb7 { - let last_index_of = 1 + l as usize - 0x80; - if bytes.len() < last_index_of { - return Err(DecoderError::RlpInconsistentLengthAndData); - } - let d = &bytes[1..last_index_of]; - if l == 0x81 && d[0] < 0x80 { - return Err(DecoderError::RlpInvalidIndirection); - } - Ok(f(d)?) - } else if l <= 0xbf { - let len_of_len = l as usize - 0xb7; - let begin_of_value = 1_usize + len_of_len; - if bytes.len() < begin_of_value { - return Err(DecoderError::RlpInconsistentLengthAndData); - } - let len = decode_usize(&bytes[1..begin_of_value])?; - - let last_index_of_value = begin_of_value - .checked_add(len) - .ok_or(DecoderError::RlpInvalidLength)?; - if bytes.len() < last_index_of_value { - return Err(DecoderError::RlpInconsistentLengthAndData); - } - Ok(f(&bytes[begin_of_value..last_index_of_value])?) - } else { - Err(DecoderError::RlpExpectedToBeData) - } - } -} diff --git a/libraries/rust/common/src/rlp/stream.rs b/libraries/rust/common/src/rlp/stream.rs deleted file mode 100644 index b9c5a25..0000000 --- a/libraries/rust/common/src/rlp/stream.rs +++ /dev/null @@ -1,466 +0,0 @@ -// Copyright 2020 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use bytes::{BufMut, BytesMut}; -use core::borrow::Borrow; - -use crate::rlp::traits::Encodable; - -#[derive(Debug, Copy, Clone)] -struct ListInfo { - position: usize, - current: usize, - max: Option, -} - -impl ListInfo { - fn new(position: usize, max: Option) -> ListInfo { - ListInfo { - position, - current: 0, - max, - } - } -} - -/// Appendable rlp encoder. -pub struct RlpStream { - unfinished_lists: Vec, - start_pos: usize, - buffer: BytesMut, - finished_list: bool, -} - -impl Default for RlpStream { - fn default() -> Self { - RlpStream::new() - } -} - -impl RlpStream { - /// Initializes instance of empty `Stream`. - pub fn new() -> Self { - Self::new_with_buffer(BytesMut::with_capacity(1024)) - } - - /// Initializes the `Stream` as a list. - pub fn new_list(len: usize) -> Self { - Self::new_list_with_buffer(BytesMut::with_capacity(1024), len) - } - - /// Initializes instance of empty `Stream`. - pub fn new_with_buffer(buffer: BytesMut) -> Self { - RlpStream { - unfinished_lists: Vec::with_capacity(16), - start_pos: buffer.len(), - buffer, - finished_list: false, - } - } - - /// Initializes the `Stream` as a list. - pub fn new_list_with_buffer(buffer: BytesMut, len: usize) -> Self { - let mut stream = RlpStream::new_with_buffer(buffer); - stream.begin_list(len); - stream - } - - fn total_written(&self) -> usize { - self.buffer.len() - self.start_pos - } - - /// Apends null to the end of stream, chainable. - /// - /// ``` - /// use common::rlp::{RlpStream}; - /// let mut stream = RlpStream::new_list(2); - /// stream.append_null().append_null(); - /// let out = stream.out(); - /// println!("{:?}", b"\xc4\xf8\0\xf8\0"); - /// assert_eq!(out, vec![0xc4, 0xf8, 0, 0xf8, 0]); - /// ``` - pub fn append_null(&mut self) -> &mut Self { - // self push raw item - self.buffer.put_u8(0xf8); - self.buffer.put_u8(0x00); - // try to finish and prepend the length - self.note_appended(1); - - // return chainable self - self - } - - pub fn append_null_internal(&mut self) -> &mut Self { - // self push raw item - self.buffer.put_u8(0xf8); - self.buffer.put_u8(0x00); - // return chainable self - self - } - - /// Apends null to the end of stream, chainable. - /// - /// ``` - /// use common::rlp::RlpStream; - /// let mut stream = RlpStream::new_list(2); - /// stream.append_empty_data().append_empty_data(); - /// let out = stream.out(); - /// assert_eq!(out, vec![0xc2, 0x80, 0x80]); - /// ``` - pub fn append_empty_data(&mut self) -> &mut Self { - // self push raw item - self.buffer.put_u8(0x80); - - // try to finish and prepend the length - self.note_appended(1); - - // return chainable self - self - } - - /// Appends raw (pre-serialised) RLP data. Use with caution. Chainable. - pub fn append_raw(&mut self, bytes: &[u8], item_count: usize) -> &mut Self { - // push raw items - self.buffer.extend_from_slice(bytes); - - // try to finish and prepend the length - self.note_appended(item_count); - - // return chainable self - self - } - - /// Appends value to the end of stream, chainable. - /// - /// ``` - /// use common::rlp::{RlpStream}; - /// let mut stream = RlpStream::new_list(2); - /// stream.append(&"cat").append(&"dog"); - /// let out = stream.out(); - /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); - /// ``` - pub fn append(&mut self, value: &E) -> &mut Self - where - E: Encodable, - { - self.finished_list = false; - value.rlp_append(self); - if !self.finished_list { - self.note_appended(1); - } - self - } - - /// Appends iterator to the end of stream, chainable. - /// - /// ``` - /// use common::rlp::{RlpStream}; - /// let mut stream = RlpStream::new_list(2); - /// stream.append(&"cat").append_iter("dog".as_bytes().iter().cloned()); - /// let out = stream.out(); - /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); - /// ``` - pub fn append_iter(&mut self, value: I) -> &mut Self - where - I: IntoIterator, - { - self.finished_list = false; - self.encoder().encode_iter(value); - if !self.finished_list { - self.note_appended(1); - } - self - } - - /// Appends list of values to the end of stream, chainable. - pub fn append_list(&mut self, values: &[K]) -> &mut Self - where - E: Encodable, - K: Borrow, - { - self.begin_list(values.len()); - for value in values { - self.append(value.borrow()); - } - self - } - - /// Appends value to the end of stream, but do not count it as an appended item. - /// It's useful for wrapper types - pub fn append_internal(&mut self, value: &E) -> &mut Self - where - E: Encodable, - { - value.rlp_append(self); - self - } - - /// Declare appending the list of given size, chainable. - /// - /// ``` - /// use common::rlp::{RlpStream}; - /// let mut stream = RlpStream::new_list(2); - /// stream.begin_list(2).append(&"cat").append(&"dog"); - /// stream.append(&""); - /// let out = stream.out(); - /// assert_eq!(out, vec![0xca, 0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g', 0x80]); - /// ``` - pub fn begin_list(&mut self, len: usize) -> &mut RlpStream { - self.finished_list = false; - match len { - 0 => { - // we may finish, if the appended list len is equal 0 - self.buffer.put_u8(0xc0u8); - self.note_appended(1); - self.finished_list = true; - } - _ => { - // payload is longer than 1 byte only for lists > 55 bytes - // by pushing always this 1 byte we may avoid unnecessary shift of data - self.buffer.put_u8(0); - - let position = self.total_written(); - self.unfinished_lists - .push(ListInfo::new(position, Some(len))); - } - } - - // return chainable self - self - } - - /// Declare appending the list of unknown size, chainable. - pub fn begin_unbounded_list(&mut self) -> &mut RlpStream { - self.finished_list = false; - // payload is longer than 1 byte only for lists > 55 bytes - // by pushing always this 1 byte we may avoid unnecessary shift of data - self.buffer.put_u8(0); - let position = self.total_written(); - self.unfinished_lists.push(ListInfo::new(position, None)); - // return chainable self - self - } - - /// Appends raw (pre-serialised) RLP data. Checks for size overflow. - pub fn append_raw_checked(&mut self, bytes: &[u8], item_count: usize, max_size: usize) -> bool { - if self.estimate_size(bytes.len()) > max_size { - return false; - } - self.append_raw(bytes, item_count); - true - } - - /// Calculate total RLP size for appended payload. - pub fn estimate_size(&self, add: usize) -> usize { - let total_size = self.total_written() + add; - let mut base_size = total_size; - for list in &self.unfinished_lists[..] { - let len = total_size - list.position; - if len > 55 { - let leading_empty_bytes = (len as u64).leading_zeros() as usize / 8; - let size_bytes = 8 - leading_empty_bytes; - base_size += size_bytes; - } - } - base_size - } - - /// Returns current RLP size in bytes for the data pushed into the list. - pub fn len(&self) -> usize { - self.estimate_size(0) - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Clear the output stream so far. - /// - /// ``` - /// use common::rlp::{RlpStream}; - /// let mut stream = RlpStream::new_list(3); - /// stream.append(&"cat"); - /// stream.clear(); - /// stream.append(&"dog"); - /// let out = stream.out(); - /// assert_eq!(out, vec![0x83, b'd', b'o', b'g']); - /// ``` - pub fn clear(&mut self) { - // clear bytes - self.buffer.truncate(self.start_pos); - - // clear lists - self.unfinished_lists.clear(); - } - - /// Returns true if stream doesnt expect any more items. - /// - /// ``` - /// use common::rlp::{RlpStream}; - /// let mut stream = RlpStream::new_list(2); - /// stream.append(&"cat"); - /// assert_eq!(stream.is_finished(), false); - /// stream.append(&"dog"); - /// assert_eq!(stream.is_finished(), true); - /// let out = stream.out(); - /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); - /// ``` - pub fn is_finished(&self) -> bool { - self.unfinished_lists.is_empty() - } - - /// Get raw encoded bytes - pub fn as_raw(&self) -> &[u8] { - //&self.encoder.bytes - &self.buffer - } - - /// Streams out encoded bytes. - /// - /// panic! if stream is not finished. - pub fn out(self) -> BytesMut { - if self.is_finished() { - self.buffer - } else { - panic!() - } - } - - /// Try to finish lists - fn note_appended(&mut self, inserted_items: usize) { - if self.unfinished_lists.is_empty() { - return; - } - - let back = self.unfinished_lists.len() - 1; - let should_finish = match self.unfinished_lists.get_mut(back) { - None => false, - Some(ref mut x) => { - x.current += inserted_items; - match x.max { - Some(ref max) if x.current > *max => { - panic!("You cannot append more items than you expect!") - } - Some(ref max) => x.current == *max, - _ => false, - } - } - }; - if should_finish { - let x = self.unfinished_lists.pop().unwrap(); - let len = self.total_written() - x.position; - self.encoder().insert_list_payload(len, x.position); - self.note_appended(1); - } - self.finished_list = should_finish; - } - - pub fn encoder(&mut self) -> BasicEncoder { - BasicEncoder::new(self, self.start_pos) - } - - /// Finalize current unbounded list. Panics if no unbounded list has been opened. - pub fn finalize_unbounded_list(&mut self) { - let list = self.unfinished_lists.pop().expect("No open list."); - if list.max.is_some() { - panic!("List type mismatch."); - } - let len = self.total_written() - list.position; - self.encoder().insert_list_payload(len, list.position); - self.note_appended(1); - self.finished_list = true; - } -} - -pub struct BasicEncoder<'a> { - buffer: &'a mut BytesMut, - start_pos: usize, -} - -impl<'a> BasicEncoder<'a> { - fn new(stream: &'a mut RlpStream, start_pos: usize) -> Self { - BasicEncoder { - buffer: &mut stream.buffer, - start_pos, - } - } - - fn total_written(&self) -> usize { - self.buffer.len() - self.start_pos - } - - fn insert_size(&mut self, size: usize, position: usize) -> u8 { - let size = size as u32; - let leading_empty_bytes = size.leading_zeros() as usize / 8; - let size_bytes = 4 - leading_empty_bytes as u8; - let buffer: [u8; 4] = size.to_be_bytes(); - assert!(position <= self.total_written()); - - self.buffer - .extend_from_slice(&buffer[leading_empty_bytes..]); - self.buffer[self.start_pos + position..].rotate_right(size_bytes as usize); - size_bytes - } - - /// Inserts list prefix at given position - fn insert_list_payload(&mut self, len: usize, pos: usize) { - // 1 byte was already reserved for payload earlier - match len { - 0..=55 => { - self.buffer[self.start_pos + pos - 1] = 0xc0u8 + len as u8; - } - _ => { - let inserted_bytes = self.insert_size(len, pos); - self.buffer[self.start_pos + pos - 1] = 0xf7u8 + inserted_bytes; - } - }; - } - - pub fn encode_value(&mut self, value: &[u8]) { - self.encode_iter(value.iter().cloned()); - } - - /// Pushes encoded value to the end of buffer - pub fn encode_iter(&mut self, value: I) - where - I: IntoIterator, - { - let mut value = value.into_iter(); - let len = match value.size_hint() { - (lower, Some(upper)) if lower == upper => lower, - _ => { - let value = value.collect::>(); - return self.encode_iter(value); - } - }; - match len { - // just 0 - 0 => self.buffer.put_u8(0x80u8), - len @ 1..=55 => { - let first = value.next().expect("iterator length is higher than 1"); - if len == 1 && first < 0x80 { - // byte is its own encoding if < 0x80 - self.buffer.put_u8(first); - } else { - // (prefix + length), followed by the string - self.buffer.put_u8(0x80u8 + len as u8); - self.buffer.put_u8(first); - self.buffer.extend(value); - } - } - // (prefix + length of length), followed by the length, followd by the string - len => { - self.buffer.put_u8(0); - let position = self.total_written(); - let inserted_bytes = self.insert_size(len, position); - self.buffer[self.start_pos + position - 1] = 0xb7 + inserted_bytes; - self.buffer.extend(value); - } - } - } -} diff --git a/libraries/rust/common/src/rlp/traits.rs b/libraries/rust/common/src/rlp/traits.rs deleted file mode 100644 index 0e00b87..0000000 --- a/libraries/rust/common/src/rlp/traits.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Common RLP traits -use bytes::BytesMut; - -use crate::rlp::{error::DecoderError, rlpin::Rlp, stream::RlpStream}; - -/// RLP decodable trait -pub trait Decodable: Sized { - /// Decode a value from RLP bytes - fn decode(rlp: &Rlp) -> Result; -} - -/// Structure encodable to RLP -pub trait Encodable { - /// Append a value to the stream - fn rlp_append(&self, s: &mut RlpStream); - - /// Get rlp-encoded bytes for this instance - fn rlp_bytes(&self) -> BytesMut { - let mut s = RlpStream::new(); - self.rlp_append(&mut s); - s.out() - } -} From eb2b46af9d4f9904e1464e502b915905e053cd73 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Wed, 5 Jul 2023 15:14:52 +0545 Subject: [PATCH 33/37] chore: remove unused files from x-call-mock Signed-off-by: Night Owl --- .../x-call-mock/.circleci/config.yml | 61 ------ .../mock-contracts/x-call-mock/.editorconfig | 11 - .../x-call-mock/.github/workflows/Basic.yml | 75 ------- .../x-call-mock/.github/workflows/Release.yml | 35 --- .../mock-contracts/x-call-mock/.gitignore | 16 -- contracts/mock-contracts/x-call-mock/LICENSE | 202 ------------------ contracts/mock-contracts/x-call-mock/NOTICE | 13 -- 7 files changed, 413 deletions(-) delete mode 100644 contracts/mock-contracts/x-call-mock/.circleci/config.yml delete mode 100644 contracts/mock-contracts/x-call-mock/.editorconfig delete mode 100644 contracts/mock-contracts/x-call-mock/.github/workflows/Basic.yml delete mode 100644 contracts/mock-contracts/x-call-mock/.github/workflows/Release.yml delete mode 100644 contracts/mock-contracts/x-call-mock/.gitignore delete mode 100644 contracts/mock-contracts/x-call-mock/LICENSE delete mode 100644 contracts/mock-contracts/x-call-mock/NOTICE diff --git a/contracts/mock-contracts/x-call-mock/.circleci/config.yml b/contracts/mock-contracts/x-call-mock/.circleci/config.yml deleted file mode 100644 index 9b07669..0000000 --- a/contracts/mock-contracts/x-call-mock/.circleci/config.yml +++ /dev/null @@ -1,61 +0,0 @@ -version: 2.1 - -executors: - builder: - docker: - - image: buildpack-deps:trusty - -jobs: - docker-image: - executor: builder - steps: - - checkout - - setup_remote_docker - docker_layer_caching: true - - run: - name: Build Docker artifact - command: docker build --pull -t "cosmwasm/cw-gitpod-base:${CIRCLE_SHA1}" . - - run: - name: Push application Docker image to docker hub - command: | - if [ "${CIRCLE_BRANCH}" = "master" ]; then - docker tag "cosmwasm/cw-gitpod-base:${CIRCLE_SHA1}" cosmwasm/cw-gitpod-base:latest - docker login --password-stdin -u "$DOCKER_USER" \<<<"$DOCKER_PASS" - docker push cosmwasm/cw-gitpod-base:latest - docker logout - fi - - docker-tagged: - executor: builder - steps: - - checkout - - setup_remote_docker - docker_layer_caching: true - - run: - name: Push application Docker image to docker hub - command: | - docker tag "cosmwasm/cw-gitpod-base:${CIRCLE_SHA1}" "cosmwasm/cw-gitpod-base:${CIRCLE_TAG}" - docker login --password-stdin -u "$DOCKER_USER" \<<<"$DOCKER_PASS" - docker push - docker logout - -workflows: - version: 2 - test-suite: - jobs: - # this is now a slow process... let's only run on master - - docker-image: - filters: - branches: - only: - - master - - docker-tagged: - filters: - tags: - only: - - /^v.*/ - branches: - ignore: - - /.*/ - requires: - - docker-image diff --git a/contracts/mock-contracts/x-call-mock/.editorconfig b/contracts/mock-contracts/x-call-mock/.editorconfig deleted file mode 100644 index 3d36f20..0000000 --- a/contracts/mock-contracts/x-call-mock/.editorconfig +++ /dev/null @@ -1,11 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.rs] -indent_size = 4 diff --git a/contracts/mock-contracts/x-call-mock/.github/workflows/Basic.yml b/contracts/mock-contracts/x-call-mock/.github/workflows/Basic.yml deleted file mode 100644 index 3890a07..0000000 --- a/contracts/mock-contracts/x-call-mock/.github/workflows/Basic.yml +++ /dev/null @@ -1,75 +0,0 @@ -# Based on https://github.com/actions-rs/example/blob/master/.github/workflows/quickstart.yml - -on: [push, pull_request] - -name: Basic - -jobs: - - test: - name: Test Suite - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.60.0 - target: wasm32-unknown-unknown - override: true - - - name: Run unit tests - uses: actions-rs/cargo@v1 - with: - command: unit-test - args: --locked - env: - RUST_BACKTRACE: 1 - - - name: Compile WASM contract - uses: actions-rs/cargo@v1 - with: - command: wasm - args: --locked - env: - RUSTFLAGS: "-C link-arg=-s" - - lints: - name: Lints - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.60.0 - override: true - components: rustfmt, clippy - - - name: Run cargo fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - - name: Run cargo clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: -- -D warnings - - - name: Generate Schema - uses: actions-rs/cargo@v1 - with: - command: schema - args: --locked - - - name: Schema Changes - # fails if any changes not committed - run: git diff --exit-code schema diff --git a/contracts/mock-contracts/x-call-mock/.github/workflows/Release.yml b/contracts/mock-contracts/x-call-mock/.github/workflows/Release.yml deleted file mode 100644 index a9d5979..0000000 --- a/contracts/mock-contracts/x-call-mock/.github/workflows/Release.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: release wasm - -on: - release: - types: [created] - -jobs: - release: - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - name: Install cargo-run-script - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-run-script - - name: Run cargo optimize - uses: actions-rs/cargo@v1 - with: - command: run-script - args: optimize - - name: Get release ID - id: get_release - uses: bruceadams/get-release@v1.2.3 - env: - GITHUB_TOKEN: ${{ github.token }} - - name: Upload optimized wasm - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ./artifacts/*.wasm - tag: ${{ github.ref }} - overwrite: true - file_glob: true diff --git a/contracts/mock-contracts/x-call-mock/.gitignore b/contracts/mock-contracts/x-call-mock/.gitignore deleted file mode 100644 index 9095dea..0000000 --- a/contracts/mock-contracts/x-call-mock/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# Build results -/target -/schema - -# Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) -.cargo-ok - -# Text file backups -**/*.rs.bk - -# macOS -.DS_Store - -# IDEs -*.iml -.idea diff --git a/contracts/mock-contracts/x-call-mock/LICENSE b/contracts/mock-contracts/x-call-mock/LICENSE deleted file mode 100644 index d645695..0000000 --- a/contracts/mock-contracts/x-call-mock/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/contracts/mock-contracts/x-call-mock/NOTICE b/contracts/mock-contracts/x-call-mock/NOTICE deleted file mode 100644 index 489ac61..0000000 --- a/contracts/mock-contracts/x-call-mock/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2023 qwerty0789 - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. From a602c2288b0c72f77303abf414a9325d8d92b0f4 Mon Sep 17 00:00:00 2001 From: qwerty0789 <134275268+qwerty0789@users.noreply.github.com> Date: Tue, 11 Jul 2023 11:39:33 +0545 Subject: [PATCH 34/37] feat: add cw-hub-bnUSD contract (#28) * style: cargo fix and clippy * feat: changes after the review * fix: cargo clippy run * feat: change common/rlp to crate rlp * style: cargo fix and clippy * feat: changes after the review * fix: cargo clippy run * feat: change common/rlp to crate rlp * feat: network validation added, and tests fixed according to that * feat: changes after the review * feat: changes after the review * feat: changes after the review * feat: changes after the review * feat: changes after the review * style: run cargo fmt Signed-off-by: Night Owl * fix: validate icon address fixes --------- Signed-off-by: Night Owl Co-authored-by: Night Owl --- Cargo.lock | 146 +++++++---------- contracts/cw-common/Cargo.toml | 1 - contracts/cw-common/src/network_address.rs | 80 +++++++--- .../x-call-mock/src/contract.rs | 2 +- .../cw-hub-bnusd/src/contract.rs | 149 +++++++++++------- .../token-contracts/cw-hub-bnusd/src/error.rs | 2 +- .../token-contracts/cw-hub-bnusd/src/state.rs | 9 +- .../cw-hub-bnusd/tests/setup.rs | 64 ++++---- 8 files changed, 242 insertions(+), 211 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 442a4bb..a694067 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,15 +13,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - [[package]] name = "anyhow" version = "1.0.71" @@ -84,15 +75,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "6340df57935414636969091153f35f68d9f00bbc8fb4a9c6054706c213e6c6bc" [[package]] name = "cosmwasm-crypto" -version = "1.2.6" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c0e41be7e6c7d7ab3c61cdc32fcfaa14f948491a401cbc1c74bb33b6f4b851" +checksum = "bb64554a91d6a9231127f4355d351130a0b94e663d5d9dc8b3a54ca17d83de49" dependencies = [ "digest 0.10.7", "ed25519-zebra", @@ -103,18 +94,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.6" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a7ee2798c92c00dd17bebb4210f81d5f647e5e92d847959b7977e0fd29a3500" +checksum = "a0fb2ce09f41a3dae1a234d56a9988f9aff4c76441cd50ef1ee9a4f20415b028" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.6" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "407aca6f1671a08b60db8167f03bb7cb6b2378f0ddd9a030367b66ba33c2fd41" +checksum = "230e5d1cefae5331db8934763c81b9c871db6a2cd899056a5694fa71d292c815" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -125,9 +116,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.6" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d1e00b8fd27ff923c10303023626358e23a6f9079f8ebec23a8b4b0bfcd4b3" +checksum = "43dadf7c23406cb28079d69e6cb922c9c29b9157b0fe887e3b79c783b7d4bcb8" dependencies = [ "proc-macro2", "quote", @@ -136,9 +127,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.6" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d5fdfd112b070055f068fad079d490117c8e905a588b92a5a7c9276d029930" +checksum = "4337eef8dfaf8572fe6b6b415d6ec25f9308c7bb09f2da63789209fb131363be" dependencies = [ "base64", "cosmwasm-crypto", @@ -149,16 +140,16 @@ dependencies = [ "schemars", "serde", "serde-json-wasm", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "uint", ] [[package]] name = "cosmwasm-storage" -version = "1.2.6" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9e21c4f58986fd20184d7685e1c43c5732c9309337b09307d5952fd34dba6e" +checksum = "e8601d284db8776e39fe99b3416516c5636ca73cef14666b7bb9648ca32c4b89" dependencies = [ "cosmwasm-std", "serde", @@ -166,9 +157,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -224,7 +215,6 @@ dependencies = [ "cw-storage-plus", "cw20", "hex", - "regex", "rlp", "rlp-derive", "rustc-hex", @@ -256,9 +246,9 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.16.4" +version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a18afd2e201221c6d72a57f0886ef2a22151bbc9e6db7af276fde8a91081042" +checksum = "127c7bb95853b8e828bdab97065c81cb5ddc20f7339180b61b2300565aaa99d1" dependencies = [ "anyhow", "cosmwasm-std", @@ -301,22 +291,23 @@ dependencies = [ [[package]] name = "cw2" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" +checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", "serde", + "thiserror", ] [[package]] name = "cw20" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" +checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -327,9 +318,9 @@ dependencies = [ [[package]] name = "cw20-base" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcd279230b08ed8afd8be5828221622bd5b9ce25d0b01d58bad626c6ce0169c" +checksum = "0b3ad456059901a36cfa68b596d85d579c3df2b797dae9950dc34c27e14e995f" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -477,9 +468,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -532,9 +523,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "k256" @@ -545,20 +536,14 @@ dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] name = "libc" -version = "0.2.145" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "once_cell" @@ -584,9 +569,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] @@ -616,9 +601,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -638,23 +623,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "regex" -version = "1.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" - [[package]] name = "rfc6979" version = "0.3.1" @@ -695,9 +663,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] name = "schemars" @@ -745,9 +713,9 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" dependencies = [ "serde_derive", ] @@ -763,13 +731,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -785,9 +753,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" dependencies = [ "itoa", "ryu", @@ -809,9 +777,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -863,9 +831,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" dependencies = [ "proc-macro2", "quote", @@ -874,22 +842,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -912,9 +880,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "version_check" diff --git a/contracts/cw-common/Cargo.toml b/contracts/cw-common/Cargo.toml index 9aa609c..42e8339 100644 --- a/contracts/cw-common/Cargo.toml +++ b/contracts/cw-common/Cargo.toml @@ -16,6 +16,5 @@ cosmwasm-std = { version = "1.2.5", default-features = false } rlp = { version = "0.5.2", default-features = false } cosmwasm-schema = "1.2.6" cw-storage-plus = "1.1.0" -regex = "1.5.4" diff --git a/contracts/cw-common/src/network_address.rs b/contracts/cw-common/src/network_address.rs index 1bd5ec6..0a759ba 100644 --- a/contracts/cw-common/src/network_address.rs +++ b/contracts/cw-common/src/network_address.rs @@ -1,4 +1,3 @@ -use regex::Regex; use std::str::FromStr; use cosmwasm_schema::cw_serde; @@ -93,23 +92,13 @@ impl NetworkAddress { } pub fn validate(&self) -> bool { - let address = self.get_parts()[1]; - if !address.is_ascii() { - return false; + let parts = self.get_parts(); + let net_id = parts[0].to_string(); + let address = parts[1]; + match net_id { + s if s.contains("icon") => validate_icon_address(address), + _ => false, } - let lowercase_address = address.to_lowercase(); - if !(lowercase_address.starts_with("hx") || lowercase_address.starts_with("cx")) { - return false; - } - let address_without_prefix = &lowercase_address[2..]; - let address_length = address_without_prefix.len(); - - if address_length == 40 { - // Check if the address contains only valid characters [0-9, a-f] - let regex = Regex::new("^[0-9a-f]+$").unwrap(); - return regex.is_match(address_without_prefix); - } - false } } @@ -134,14 +123,61 @@ impl FromStr for NetworkAddress { } } +fn validate_icon_address(address: &str) -> bool { + let lowercase_address = address.to_lowercase(); + + if !lowercase_address.starts_with("hx") && !lowercase_address.starts_with("cx") { + return false; + } + + lowercase_address.len() == 42 && is_valid_character_set(&lowercase_address[2..]) +} + +fn is_valid_character_set(address: &str) -> bool { + address.chars().all(|c| matches!(c, '0'..='9' | 'a'..='f')) +} + #[test] fn test_parse_btp_address() { - let btp_address = - NetworkAddress("0x38.bsc/0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798".to_string()); - let (network, account) = btp_address.parse_parts(); - assert_eq!(network, NetId("0x38.bsc".to_string())); + let network_address = + NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()); + let (network, account) = network_address.parse_parts(); + assert_eq!(network, NetId("0x01.icon".to_string())); assert_eq!( account, - Addr::unchecked("0x034AaDE86BF402F023Aa17E5725fABC4ab9E9798") + Addr::unchecked("cx9876543210fedcba9876543210fedcba98765432") ); } + +#[test] +fn address_validation_test() { + let network_address = + NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()); + let res = network_address.validate(); + assert!(res); + + let network_address = + NetworkAddress("0x01.icon/hx9876543210fedcba9876543210fedcba98765432".to_string()); + let res = network_address.validate(); + assert!(res); + + let network_address = + NetworkAddress("0x01.bsc/cx9876543210fedcba9876543210fedcba98765432".to_string()); + let res = network_address.validate(); + assert!(!res); + + let network_address = + NetworkAddress("0x01.icon/wx9876543210fedcba9876543210fedcba98765432".to_string()); + let res = network_address.validate(); + assert!(!res); + + let network_address = + NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba9876542".to_string()); + let res = network_address.validate(); + assert!(!res); + + let network_address = + NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba9876543_".to_string()); + let res = network_address.validate(); + assert!(!res); +} diff --git a/contracts/mock-contracts/x-call-mock/src/contract.rs b/contracts/mock-contracts/x-call-mock/src/contract.rs index 7e9dd22..0ab8773 100644 --- a/contracts/mock-contracts/x-call-mock/src/contract.rs +++ b/contracts/mock-contracts/x-call-mock/src/contract.rs @@ -73,7 +73,7 @@ pub fn execute( pub fn query(_deps: Deps, _env: Env, _msg: XCallQuery) -> StdResult { match _msg { XCallQuery::GetNetworkAddress {} => Ok(to_binary( - "0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e", + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432", )?), } } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index 6211957..113c26a 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,8 +1,3 @@ -#[cfg(not(feature = "library"))] -use cosmwasm_std::entry_point; -use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult}; -use cw2::set_contract_version; -// use cw2::set_contract_version; use crate::constants::{ REPLY_MSG_SUCCESS, TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT, @@ -11,6 +6,10 @@ use crate::error::ContractError; use crate::state::{ DESTINATION_TOKEN_ADDRESS, DESTINATION_TOKEN_NET, NID, OWNER, X_CALL, X_CALL_NETWORK_ADDRESS, }; +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult}; +use cw2::set_contract_version; use cw_common::hub_token_msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use cw_common::x_call_msg::{XCallMsg, XCallQuery}; @@ -123,6 +122,10 @@ mod execute { .addr_validate(x_call.as_ref()) .map_err(ContractError::Std)?; + if !hub_network_address.validate() { + return Err(ContractError::InvalidNetworkAddress); + } + X_CALL .save(deps.storage, &x_call) .map_err(ContractError::Std)?; @@ -159,21 +162,26 @@ mod execute { from: NetworkAddress, data: Vec, ) -> Result { + if !from.validate() { + return Err(ContractError::InvalidNetworkAddress); + } + let xcall = X_CALL.load(deps.storage)?; if info.sender != xcall { return Err(ContractError::OnlyCallService); } + let rlp: Rlp = Rlp::new(&data); let data_list: Vec = rlp.as_list().unwrap(); if data_list.len() <= 2 { return Err(ContractError::InvalidData); } - debug_println!("this is {:?}", data_list); + debug_println!("datalist {:?}", data_list); let data_list = &data_list[0].to_vec(); let method = from_utf8(data_list).unwrap(); - debug_println!("this is {:?}", method); + debug_println!("method {:?}", method); match method { X_CROSS_TRANSFER => { let cross_transfer_data: CrossTransfer = decode(&data).unwrap(); @@ -199,6 +207,9 @@ mod execute { amount: u128, data: Vec, ) -> Result { + if !to.validate() { + return Err(ContractError::InvalidNetworkAddress); + } let funds = info.funds.clone(); let nid = NID.load(deps.storage)?; let hub_net: NetId = DESTINATION_TOKEN_NET.load(deps.storage)?; @@ -212,7 +223,7 @@ mod execute { from: from.clone(), to: to.clone(), value: amount, - data, + data: data.clone(), }; let rollback_data = CrossTransferRevert { method: X_CROSS_TRANSFER_REVERT.to_string(), @@ -237,16 +248,21 @@ mod execute { let sub_message = SubMsg::reply_always(wasm_execute_message, REPLY_MSG_SUCCESS); debug_println!("this is {:?}", info.sender); - let _result = + debug_println!("burn from {:?}", sub_message); + + let result = execute_burn(deps, env, info, amount.into()).map_err(ContractError::Cw20BaseError)?; + debug_println!("burn from {:?}", sub_message); + //TODO: emit a event log for cross transfer let event = Event::new("CrossTransfer") .add_attribute("from", from.to_string()) .add_attribute("to", to.to_string()) - .add_attribute("value", amount.to_string()); - - Ok(Response::new() + .add_attribute("value", amount.to_string()) + .add_attribute("data", hex_encode(data)); + debug_println!("this is {:?}", event); + Ok(result .add_submessage(sub_message) .add_attribute("method", "cross_transfer") .add_event(event)) @@ -259,7 +275,10 @@ mod execute { from: NetworkAddress, cross_transfer_data: CrossTransfer, ) -> Result { - debug_println!("this is {:?}", cross_transfer_data); + debug_println!("xcrosstransfer {:?}", cross_transfer_data); + if !cross_transfer_data.from.validate() || !cross_transfer_data.to.validate() { + return Err(ContractError::InvalidNetworkAddress); + } let nid = NID.load(deps.storage)?; let hub_net: NetId = DESTINATION_TOKEN_NET.load(deps.storage)?; @@ -268,14 +287,14 @@ mod execute { let network_address = NetworkAddress::new(&hub_net.to_string(), destination_network_address.as_ref()); - debug_println!("this is {:?},{:?}", network_address, from); + debug_println!("before network addr==from {:?},{:?}", network_address, from); if from != network_address { return Err(ContractError::WrongAddress {}); } //TODO: add a validation check for ICON address in network address library let (net, account) = NetworkAddress::parse_parts(&cross_transfer_data.to); - debug_println!("this is {:?},{:?}", net, nid); + debug_println!("net nid comparision {:?},{:?}", net, nid); if net != nid { return Err(ContractError::WrongNetwork); } @@ -283,7 +302,7 @@ mod execute { deps.api .addr_validate(account.as_ref()) .map_err(ContractError::Std)?; - + debug_println!("mint to {:?}", account); let res = execute_mint( deps, env, @@ -296,7 +315,8 @@ mod execute { let event = Event::new("XCrossTransfer") .add_attribute("from", cross_transfer_data.from.to_string()) .add_attribute("to", cross_transfer_data.to.to_string()) - .add_attribute("value", cross_transfer_data.value.to_string()); + .add_attribute("value", cross_transfer_data.value.to_string()) + .add_attribute("data", hex_encode(cross_transfer_data.data)); //TODO: add event for cross transfer with relevant parameters Ok(res @@ -312,6 +332,9 @@ mod execute { cross_transfer_revert_data: CrossTransferRevert, ) -> Result { debug_println!("this is {:?},{:?}", cross_transfer_revert_data, from); + if !from.validate() { + return Err(ContractError::InvalidNetworkAddress); + } deps.api .addr_validate(cross_transfer_revert_data.from.as_ref()) .map_err(ContractError::Std)?; @@ -331,6 +354,18 @@ mod execute { .add_attribute("method", "x_cross_transfer_revert") .add_event(event)) } + + fn hex_encode(data: Vec) -> String { + debug_println!("this is {:?}", data); + if data.is_empty() { + debug_println!("this is empty"); + "null".to_string() + } else { + let data = hex::encode(data); + debug_println!("this is not empty, {}", data); + data + } + } } #[cfg(test)] @@ -345,10 +380,8 @@ mod rlp_test { fn encodetest() { let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: NetworkAddress( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - ), - to: NetworkAddress("0x38.bsc/archway123fdth".to_string()), + from: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned()), + to: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, @@ -382,29 +415,33 @@ mod tests { testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}, to_binary, Addr, ContractResult, MemoryStorage, OwnedDeps, SystemResult, WasmQuery, }; + use debug_print::debug_println; use rlp::encode; use super::*; - fn setup() -> ( + fn setup( + sender: &str, + ) -> ( OwnedDeps, Env, MessageInfo, ) { let mut deps: OwnedDeps = mock_dependencies(); let env = mock_env(); - let info = mock_info("archway123fdth", &[]); + let info = mock_info(sender, &[]); let msg = InstantiateMsg { x_call: "archway123fdth".to_owned(), - hub_address: "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + hub_address: "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), }; - let _res: Response = instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + let res = instantiate(deps.as_mut(), env.clone(), info.clone(), msg); + assert!(res.is_ok()); let setup_message = ExecuteMsg::Setup { x_call: Addr::unchecked("archway123fdth".to_owned()), hub_address: NetworkAddress( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), ), }; @@ -413,31 +450,30 @@ mod tests { contract_addr: _, msg: _, } => SystemResult::Ok(ContractResult::Ok( - to_binary("0x38.bsc/archway192kfvz2vrxv4hhaz3tjdk39maa69xs75n5cea8").unwrap(), + to_binary("0x01.icon/cx9876543210fedcba9876543210fedcba98765432").unwrap(), )), _ => todo!(), }); - execute(deps.as_mut(), env.clone(), info.clone(), setup_message).unwrap(); + let res = execute(deps.as_mut(), env.clone(), info.clone(), setup_message); + assert!(res.is_ok()); (deps, env, info) } #[test] fn instantiate_test() { - setup(); + setup("archway123fdth"); } #[test] fn execute_handle_call_xcrosstransfer_test() { - let (mut deps, env, info) = setup(); + let (mut deps, env, info) = setup("archway123fdth"); let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: NetworkAddress( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - ), - to: NetworkAddress("0x38.bsc/archway123fdth".to_string()), + from: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned()), + to: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, @@ -447,30 +483,28 @@ mod tests { // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); - let _res: Response = execute( + let res = execute( deps.as_mut(), env, info, ExecuteMsg::HandleCallMessage { from: NetworkAddress( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), ), data, }, - ) - .unwrap(); + ); + assert!(res.is_ok()); } #[test] fn cross_transfer_test() { - let (mut deps, env, info) = setup(); + let (mut deps, env, info) = setup("archway123fdth"); let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: NetworkAddress( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - ), - to: NetworkAddress("0x38.bsc/archway123fdth".to_string()), + from: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned()), + to: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765452".to_string()), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, @@ -483,39 +517,44 @@ mod tests { let _res: Response = execute( deps.as_mut(), env.clone(), - info.clone(), + info, ExecuteMsg::HandleCallMessage { from: NetworkAddress( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), ), data, }, ) .unwrap(); - let _res: Response = execute( + let info = mock_info("cx9876543210fedcba9876543210fedcba98765452", &[]); + + // execute_mint(deps, env, info, info.sender.to_string(), 1000); + + let res = execute( deps.as_mut(), env, info, ExecuteMsg::CrossTransfer { to: NetworkAddress( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), ), amount: 1000, data: vec![1, 2, 3, 4, 5], }, - ) - .unwrap(); + ); + debug_println!("this is {:?}", _res); + assert!(res.is_ok()); } #[test] fn execute_handle_call_test_xcrossrevert() { - let (mut deps, env, info) = setup(); + let (mut deps, env, info) = setup("archway123fdth"); let call_data = CrossTransferRevert { method: "xCrossTransferRevert".to_string(), from: Addr::unchecked( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), ), value: 1000, }; @@ -523,17 +562,17 @@ mod tests { // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); - let _res: Response = execute( + let res = execute( deps.as_mut(), env, info, ExecuteMsg::HandleCallMessage { from: NetworkAddress( - "0x38.bsc/archway192kfvz2vrxv4hhaz3tjdk39maa69xs75n5cea8".to_owned(), + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), ), data, }, - ) - .unwrap(); + ); + assert!(res.is_ok()); } } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/error.rs b/contracts/token-contracts/cw-hub-bnusd/src/error.rs index c79bf75..7bb6eac 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/error.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/error.rs @@ -10,7 +10,7 @@ pub enum ContractError { Unauthorized {}, #[error("Wrong Address")] WrongAddress, - #[error("Invalid BTP Address")] + #[error("Invalid Network Address according to Network ID")] InvalidNetworkAddress, #[error("Wrong Network")] WrongNetwork, diff --git a/contracts/token-contracts/cw-hub-bnusd/src/state.rs b/contracts/token-contracts/cw-hub-bnusd/src/state.rs index 5ad8ff8..8058ee8 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/state.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/state.rs @@ -1,13 +1,6 @@ use cosmwasm_std::Addr; use cw_common::network_address::{NetId, NetworkAddress}; -use cw_storage_plus::{Item, Map}; - -pub const CONNECTED_CHAINS: &str = "connected_chains"; -pub const SPOKE_CONTRACTS: &str = "spoke_contract"; -pub const CROSS_CHAIN_SUPPLY: &str = "cross_chain_supply"; - -pub const CROSSCHAINSUPPLY: Map<&String, u128> = Map::new(CROSS_CHAIN_SUPPLY); -pub const CONNECTEDCHAINS: Item> = Item::new(CONNECTED_CHAINS); +use cw_storage_plus::Item; pub const OWNER: Item = Item::new("owner"); pub const X_CALL: Item = Item::new("xCall"); diff --git a/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs index 34f041e..b12e127 100644 --- a/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs +++ b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs @@ -97,8 +97,7 @@ mod instantiate_test { ctx.sender.clone(), &InstantiateMsg { x_call: Addr::unchecked(_x_call_address).into_string(), - hub_address: "0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e" - .to_owned(), + hub_address: "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), }, &[], "HubToken", @@ -118,7 +117,7 @@ mod instantiate_test { &ExecuteMsg::Setup { x_call: Addr::unchecked(ctx.get_xcall_app()), hub_address: NetworkAddress( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), + "0x01.icon/cx7866543210fedcba9876543210fedcba987654df".to_owned(), ), }, &[], @@ -131,10 +130,8 @@ mod instantiate_test { fn handle_call_message(mut ctx: TestContext) -> TestContext { let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: NetworkAddress( - "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - ), - to: NetworkAddress("0x1.icon/archway123fdth".to_string()), + from: NetworkAddress("0x01.icon/cx7866543210fedcba9876543210fedcba987654df".to_owned()), + to: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, @@ -144,19 +141,18 @@ mod instantiate_test { // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); - let _resp = ctx - .app - .execute_contract( - ctx.sender.clone(), - ctx.get_xcall_app(), - &XCallMsg::TestHandleCallMessage { - from: "0x38.bsc/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - data, - hub_token: ctx.get_hubtoken_app().into_string(), - }, - &[], - ) - .unwrap(); + let _resp = ctx.app.execute_contract( + ctx.sender.clone(), + ctx.get_xcall_app(), + &XCallMsg::TestHandleCallMessage { + from: "0x01.icon/cx7866543210fedcba9876543210fedcba987654df".to_owned(), + data, + hub_token: ctx.get_hubtoken_app().into_string(), + }, + &[], + ); + println!("Respose Please{:?}", _resp); + assert!(_resp.is_ok()); let call_data = CrossTransferRevert { method: "xCrossTransferRevert".to_string(), @@ -167,19 +163,18 @@ mod instantiate_test { // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); - let _resp = ctx - .app - .execute_contract( - ctx.sender.clone(), - ctx.get_xcall_app(), - &XCallMsg::TestHandleCallMessage { - from: "0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_owned(), - data, - hub_token: ctx.get_hubtoken_app().into_string(), - }, - &[], - ) - .unwrap(); + let _resp = ctx.app.execute_contract( + ctx.sender.clone(), + ctx.get_xcall_app(), + &XCallMsg::TestHandleCallMessage { + from: "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), + data, + hub_token: ctx.get_hubtoken_app().into_string(), + }, + &[], + ); + println!("{:?}", _resp); + assert!(_resp.is_ok()); ctx } @@ -192,7 +187,7 @@ mod instantiate_test { ctx.get_hubtoken_app(), &ExecuteMsg::CrossTransfer { to: NetworkAddress( - "0x1.icon/archway1qvqas572t6fx7af203mzygn7lgw5ywjt4y6q8e".to_string(), + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string(), ), amount: 100, data: vec![], @@ -200,6 +195,7 @@ mod instantiate_test { &[], ) .unwrap(); + println!("{:?}", _resp); ctx } From 5a670c5aac996b075acf2369aafb35199c32ca4c Mon Sep 17 00:00:00 2001 From: gcranju <134275268+gcranju@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:14:27 +0545 Subject: [PATCH 35/37] feat: Merge cw-hub-bnUSD to hubToken (#30) * style: cargo fix and clippy * feat: changes after the review * fix: cargo clippy run * feat: change common/rlp to crate rlp * style: cargo fix and clippy * feat: changes after the review * fix: cargo clippy run * feat: change common/rlp to crate rlp * feat: network validation added, and tests fixed according to that * feat: changes after the review * feat: changes after the review * feat: changes after the review * feat: changes after the review * feat: changes after the review * style: run cargo fmt Signed-off-by: Night Owl * fix: validate icon address fixes * feat: event functions created * feat: cw20base query and execute msgs added * feat: integration tests added * chore: run pre-commit script * chore: run precommit script * style: cargo clippy * fix: delete xcall-mock-contract * feat: cw20-flow tests added * feat: query tests added * fix: icon-check validation removed * fix: implementation of instantiate and setup changed * feat: contract migrate entry point added * fix: change handleMessage signature * feat: integration tests for cross transfer and xcrossTransfer * fix: changes in migration entry point * fix: network address, and xcall messages are used from xcall * fix: change in decodable implementation * chore: cargo lints fixes * fix: unnecessary clone and strng lenght comparition removed * fix: spelling error Signed-off-by: Night Owl * fix: make imports pub Signed-off-by: Night Owl * chore: run lint scripts Signed-off-by: Night Owl * fix: xcall imported as library * fix: cargo lock file updated * fix: cargo lock file updated * fix: exclude archway from member list in workspace Signed-off-by: Night Owl * fix: remove unnecessary use Signed-off-by: Night Owl * update: cargo packages Signed-off-by: Night Owl --------- Signed-off-by: Night Owl Co-authored-by: Night Owl Co-authored-by: qwerty0789 <134275268+qwerty0789@users.noreply.github.com> --- Cargo.lock | 1392 +++++++++++++++-- Cargo.toml | 10 +- contracts/cw-common/Cargo.toml | 4 +- contracts/cw-common/src/data_types.rs | 13 +- contracts/cw-common/src/hub_token_msg.rs | 59 +- contracts/cw-common/src/network_address.rs | 150 +- contracts/cw-common/src/x_call_msg.rs | 34 +- .../mock-contracts/x-call-mock/.cargo/config | 4 - .../mock-contracts/x-call-mock/Cargo.toml | 55 - .../mock-contracts/x-call-mock/README.md | 99 -- .../x-call-mock/src/bin/schema.rs | 11 - .../x-call-mock/src/contract.rs | 101 -- .../mock-contracts/x-call-mock/src/error.rs | 13 - .../mock-contracts/x-call-mock/src/helpers.rs | 27 - .../mock-contracts/x-call-mock/src/lib.rs | 6 - .../mock-contracts/x-call-mock/src/state.rs | 1 - .../token-contracts/cw-hub-bnusd/Cargo.toml | 9 +- .../cw-hub-bnusd/schema/cw-hub-bnusd.json | 984 +++++++++++- .../cw-hub-bnusd/schema/raw/execute.json | 287 ++++ .../cw-hub-bnusd/schema/raw/query.json | 210 ++- .../schema/raw/response_to_all_accounts.json | 16 + .../raw/response_to_all_allowances.json | 101 ++ .../response_to_all_spender_allowances.json | 101 ++ .../schema/raw/response_to_allowance.json | 82 + .../schema/raw/response_to_balance.json | 20 + .../schema/raw/response_to_download_logo.json | 25 + .../raw/response_to_marketing_info.json | 74 + .../schema/raw/response_to_minter.json | 31 + .../schema/raw/response_to_token_info.json | 34 + .../cw-hub-bnusd/src/contract.rs | 382 +++-- .../token-contracts/cw-hub-bnusd/src/error.rs | 4 +- .../cw-hub-bnusd/src/events.rs | 35 + .../token-contracts/cw-hub-bnusd/src/lib.rs | 1 + .../cw-hub-bnusd/tests/cross_transfer_test.rs | 43 + .../cw-hub-bnusd/tests/cw20_flow_test.rs | 585 +++++++ .../tests/handle_call_message_test.rs | 217 +++ .../cw-hub-bnusd/tests/setup.rs | 384 +++-- 37 files changed, 4684 insertions(+), 920 deletions(-) delete mode 100644 contracts/mock-contracts/x-call-mock/.cargo/config delete mode 100644 contracts/mock-contracts/x-call-mock/Cargo.toml delete mode 100644 contracts/mock-contracts/x-call-mock/README.md delete mode 100644 contracts/mock-contracts/x-call-mock/src/bin/schema.rs delete mode 100644 contracts/mock-contracts/x-call-mock/src/contract.rs delete mode 100644 contracts/mock-contracts/x-call-mock/src/error.rs delete mode 100644 contracts/mock-contracts/x-call-mock/src/helpers.rs delete mode 100644 contracts/mock-contracts/x-call-mock/src/lib.rs delete mode 100644 contracts/mock-contracts/x-call-mock/src/state.rs create mode 100644 contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_accounts.json create mode 100644 contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_allowances.json create mode 100644 contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_spender_allowances.json create mode 100644 contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_allowance.json create mode 100644 contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_balance.json create mode 100644 contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_download_logo.json create mode 100644 contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_marketing_info.json create mode 100644 contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_minter.json create mode 100644 contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_token_info.json create mode 100644 contracts/token-contracts/cw-hub-bnusd/src/events.rs create mode 100644 contracts/token-contracts/cw-hub-bnusd/tests/cross_transfer_test.rs create mode 100644 contracts/token-contracts/cw-hub-bnusd/tests/cw20_flow_test.rs create mode 100644 contracts/token-contracts/cw-hub-bnusd/tests/handle_call_message_test.rs diff --git a/Cargo.lock b/Cargo.lock index a694067..6305231 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,11 +13,26 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base16ct" @@ -25,18 +40,48 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" + [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "block-buffer" version = "0.9.0" @@ -55,6 +100,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128a44527fc0d6abf05f9eda748b9027536e12dff93f5acc8449f51583309350" + [[package]] name = "byteorder" version = "1.4.3" @@ -63,9 +114,21 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -73,39 +136,116 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" +dependencies = [ + "num-traits", +] + +[[package]] +name = "common" +version = "0.1.0" +source = "git+https://github.com/icon-project/IBC-Integration.git?branch=feat/mock-ibc-connection-balanced-test#3eedebf92d0460c9ce7e090d91955cf21382f985" +dependencies = [ + "bytes", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "debug_print", + "derive_more", + "displaydoc", + "dyn-clone", + "hex", + "hex-literal", + "ibc-proto", + "ics23", + "pbjson", + "pbjson-types", + "prost 0.11.9", + "prost-types", + "rlp-derive", + "rustc-hex", + "safe-regex", + "serde", + "serde-json-wasm", + "serde_json", + "sha2 0.10.7", + "sha3", + "subtle-encoding", + "tendermint", + "time", +] + +[[package]] +name = "common" +version = "0.1.0" +source = "git+https://github.com/icon-project/IBC-Integration.git?branch=main#da20a2a6b508a836a8723e69ad0d11c49f9b5463" +dependencies = [ + "bytes", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "debug_print", + "derive_more", + "displaydoc", + "dyn-clone", + "hex", + "hex-literal", + "ibc-proto", + "ics23", + "pbjson", + "pbjson-types", + "prost 0.11.9", + "prost-types", + "rlp-derive", + "rustc-hex", + "safe-regex", + "serde", + "serde-json-wasm", + "serde_json", + "sha2 0.10.7", + "sha3", + "subtle-encoding", + "tendermint", + "time", +] + [[package]] name = "const-oid" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6340df57935414636969091153f35f68d9f00bbc8fb4a9c6054706c213e6c6bc" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb64554a91d6a9231127f4355d351130a0b94e663d5d9dc8b3a54ca17d83de49" +checksum = "1ca101fbf2f76723711a30ea3771ef312ec3ec254ad021b237871ed802f9f175" dependencies = [ "digest 0.10.7", "ed25519-zebra", - "k256", + "k256 0.13.1", "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0fb2ce09f41a3dae1a234d56a9988f9aff4c76441cd50ef1ee9a4f20415b028" +checksum = "c73d2dd292f60e42849d2b07c03d809cf31e128a4299a805abd6d24553bcaaf5" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "230e5d1cefae5331db8934763c81b9c871db6a2cd899056a5694fa71d292c815" +checksum = "6ce34a08020433989af5cc470104f6bd22134320fe0221bd8aeb919fd5ec92d5" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -116,9 +256,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43dadf7c23406cb28079d69e6cb922c9c29b9157b0fe887e3b79c783b7d4bcb8" +checksum = "96694ec781a7dd6dea1f968a2529ade009c21ad999c88b5f53d6cc495b3b96f7" dependencies = [ "proc-macro2", "quote", @@ -127,11 +267,12 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4337eef8dfaf8572fe6b6b415d6ec25f9308c7bb09f2da63789209fb131363be" +checksum = "2a44d3f9c25b2f864737c6605a98f2e4675d53fd8bbc7cf4d7c02475661a793d" dependencies = [ - "base64", + "base64 0.21.3", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -142,14 +283,13 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.7", "thiserror", - "uint", ] [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8601d284db8776e39fe99b3416516c5636ca73cef14666b7bb9648ca32c4b89" +checksum = "ab544dfcad7c9e971933d522d99ec75cc8ddfa338854bb992b092e11bcd7e818" dependencies = [ "cosmwasm-std", "serde", @@ -157,24 +297,30 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] [[package]] -name = "crunchy" -version = "0.2.2" +name = "crypto-bigint" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] [[package]] name = "crypto-bigint" -version = "0.4.9" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -212,8 +358,11 @@ dependencies = [ "bytes", "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cw-xcall 0.1.0 (git+https://github.com/icon-project/xcall-multi.git?branch=main)", + "cw-xcall-lib 0.1.0 (git+https://github.com/icon-project/xcall-multi.git?branch=main)", "cw20", + "cw20-base", "hex", "rlp", "rlp-derive", @@ -221,27 +370,119 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-common" +version = "0.1.0" +source = "git+https://github.com/icon-project/IBC-Integration.git?branch=feat/mock-ibc-connection-balanced-test#3eedebf92d0460c9ce7e090d91955cf21382f985" +dependencies = [ + "bech32", + "bytes", + "common 0.1.0 (git+https://github.com/icon-project/IBC-Integration.git?branch=feat/mock-ibc-connection-balanced-test)", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "cw-xcall-lib 0.1.0 (git+https://github.com/icon-project/xCall.git?branch=main)", + "debug_print", + "hex", + "hex-buffer-serde", + "ibc-proto", + "prost 0.11.9", + "schemars", + "serde", + "serde-json-wasm", + "serde_json", +] + +[[package]] +name = "cw-common" +version = "0.1.0" +source = "git+https://github.com/icon-project/IBC-Integration.git?branch=main#da20a2a6b508a836a8723e69ad0d11c49f9b5463" +dependencies = [ + "bech32", + "bytes", + "common 0.1.0 (git+https://github.com/icon-project/IBC-Integration.git?branch=main)", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "cw-xcall-lib 0.1.0 (git+https://github.com/icon-project/xCall.git?branch=main)", + "debug_print", + "hex", + "hex-buffer-serde", + "ibc-proto", + "prost 0.11.9", + "schemars", + "serde", + "serde-json-wasm", + "serde_json", +] + [[package]] name = "cw-hub-bnusd" version = "0.1.0" dependencies = [ + "anyhow", "bytes", "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", - "cw-common", + "cw-common 0.1.0", + "cw-common 0.1.0 (git+https://github.com/icon-project/IBC-Integration.git?branch=main)", + "cw-mock-ibc-connection", + "cw-mock-ibc-core", "cw-multi-test", - "cw-storage-plus", + "cw-storage-plus 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cw-xcall 0.1.0 (git+https://github.com/icon-project/xcall-multi.git?branch=main)", + "cw-xcall-lib 0.1.0 (git+https://github.com/icon-project/xcall-multi.git?branch=main)", "cw2", "cw20", "cw20-base", "debug_print", + "getrandom", "hex", "rlp", "schemars", "serde", "thiserror", - "x-call-mock", +] + +[[package]] +name = "cw-mock-ibc-connection" +version = "0.1.0" +source = "git+https://github.com/icon-project/IBC-Integration.git?branch=feat/mock-ibc-connection-balanced-test#3eedebf92d0460c9ce7e090d91955cf21382f985" +dependencies = [ + "common 0.1.0 (git+https://github.com/icon-project/IBC-Integration.git?branch=feat/mock-ibc-connection-balanced-test)", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-common 0.1.0 (git+https://github.com/icon-project/IBC-Integration.git?branch=feat/mock-ibc-connection-balanced-test)", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "cw-xcall 0.1.0 (git+https://github.com/icon-project/xCall.git?branch=main)", + "cw-xcall-lib 0.1.0 (git+https://github.com/icon-project/xCall.git?branch=main)", + "cw2", + "debug_print", + "hex", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-mock-ibc-core" +version = "0.1.0" +source = "git+https://github.com/icon-project/IBC-Integration.git?branch=main#da20a2a6b508a836a8723e69ad0d11c49f9b5463" +dependencies = [ + "common 0.1.0 (git+https://github.com/icon-project/IBC-Integration.git?branch=main)", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-common 0.1.0 (git+https://github.com/icon-project/IBC-Integration.git?branch=main)", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "cw-xcall 0.1.0 (git+https://github.com/icon-project/xCall.git?branch=main)", + "cw2", + "hex", + "schemars", + "serde", + "thiserror", ] [[package]] @@ -252,12 +493,12 @@ checksum = "127c7bb95853b8e828bdab97065c81cb5ddc20f7339180b61b2300565aaa99d1" dependencies = [ "anyhow", "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cw-utils", "derivative", "itertools", - "k256", - "prost", + "k256 0.11.6", + "prost 0.9.0", "schemars", "serde", "thiserror", @@ -274,6 +515,16 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-storage-plus" +version = "1.1.0" +source = "git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw#238b520c3e193226143eedb717a3ed76187d11de" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "cw-utils" version = "1.0.1" @@ -289,6 +540,74 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw-xcall" +version = "0.1.0" +source = "git+https://github.com/icon-project/xCall.git?branch=main#c40931f9b0ab92952d9671a574e4ccbe071a86d8" +dependencies = [ + "common 0.1.0 (git+https://github.com/icon-project/IBC-Integration.git?branch=main)", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "cw-xcall-lib 0.1.0 (git+https://github.com/icon-project/xCall.git?branch=main)", + "cw2", + "debug_print", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-xcall" +version = "0.1.0" +source = "git+https://github.com/icon-project/xcall-multi.git?branch=main#c40931f9b0ab92952d9671a574e4ccbe071a86d8" +dependencies = [ + "common 0.1.0 (git+https://github.com/icon-project/IBC-Integration.git?branch=main)", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "cw-xcall-lib 0.1.0 (git+https://github.com/icon-project/xcall-multi.git?branch=main)", + "cw2", + "debug_print", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-xcall-lib" +version = "0.1.0" +source = "git+https://github.com/icon-project/xCall.git?branch=main#c40931f9b0ab92952d9671a574e4ccbe071a86d8" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "cw2", + "debug_print", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-xcall-lib" +version = "0.1.0" +source = "git+https://github.com/icon-project/xcall-multi.git?branch=main#c40931f9b0ab92952d9671a574e4ccbe071a86d8" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-storage-plus 1.1.0 (git+https://github.com/icon-project/cw-storage-plus.git?branch=fix-raw)", + "cw2", + "debug_print", + "schemars", + "serde", + "thiserror", +] + [[package]] name = "cw2" version = "1.1.0" @@ -297,7 +616,7 @@ checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "schemars", "serde", "thiserror", @@ -324,7 +643,7 @@ checksum = "0b3ad456059901a36cfa68b596d85d579c3df2b797dae9950dc34c27e14e995f" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cw-utils", "cw2", "cw20", @@ -350,6 +669,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derivative" version = "2.2.0" @@ -361,6 +690,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.9.0" @@ -377,15 +717,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" [[package]] name = "ecdsa" @@ -393,10 +745,33 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature 1.6.4", ] [[package]] @@ -406,7 +781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek", - "hashbrown", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "serde", @@ -416,9 +791,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -426,22 +801,74 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", "digest 0.10.7", - "ff", + "ff 0.12.1", "generic-array", - "group", - "pkcs8", + "group 0.12.1", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", "subtle", "zeroize", ] [[package]] -name = "ff" +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.3", + "digest 0.10.7", + "ff 0.13.0", + "generic-array", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "ff" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" @@ -450,12 +877,98 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flex-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" +dependencies = [ + "paste", +] + [[package]] name = "forward_ref" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -464,6 +977,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -483,7 +997,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -497,12 +1022,40 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-buffer-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e84645a601cf4a58f40673d51c111d1b5f847b711559c076ebcb779606a6d0" +dependencies = [ + "hex", + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + [[package]] name = "hmac" version = "0.12.1" @@ -512,6 +1065,55 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "ibc-proto" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9303a1308c886aea769ef0667c5caa422a78b01e9f8177fea8b91b08a4ff50c" +dependencies = [ + "base64 0.13.1", + "bytes", + "flex-error", + "prost 0.11.9", + "serde", + "subtle-encoding", + "tendermint-proto", +] + +[[package]] +name = "ics23" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca44b684ce1859cff746ff46f5765ab72e12e3c06f76a8356db8f9a2ecf43f17" +dependencies = [ + "anyhow", + "bytes", + "hex", + "prost 0.11.9", + "ripemd", + "sha2 0.10.7", + "sha3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -523,9 +1125,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "k256" @@ -534,17 +1136,90 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.7", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa 0.16.8", + "elliptic-curve 0.13.5", + "once_cell", "sha2 0.10.7", + "signature 2.1.0", ] +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -557,21 +1232,106 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pbjson" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048f9ac93c1eab514f9470c4bc8d97ca2a0a236b84f45cc19d69a59fc11467f6" +dependencies = [ + "base64 0.13.1", + "serde", +] + +[[package]] +name = "pbjson-build" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbb7b706f2afc610f3853550cdbbf6372fd324824a087806bd4480ea4996e24" +dependencies = [ + "heck", + "itertools", + "prost 0.11.9", + "prost-types", +] + +[[package]] +name = "pbjson-types" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a88c8d87f99a4ac14325e7a4c24af190fca261956e3b82dd7ed67e77e6c7043" +dependencies = [ + "bytes", + "chrono", + "pbjson", + "pbjson-build", + "prost 0.11.9", + "prost-build", + "serde", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkcs8" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki 0.7.2", +] + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", ] [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -583,7 +1343,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.9.0", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost 0.11.9", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", ] [[package]] @@ -599,11 +1391,33 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost 0.11.9", +] + [[package]] name = "quote" -version = "1.0.29" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -623,17 +1437,74 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + [[package]] name = "rfc6979" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "rlp" version = "0.5.2" @@ -661,17 +1532,77 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustix" +version = "0.38.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "safe-proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "814c536dcd27acf03296c618dab7ad62d28e70abd7ba41d3f34a2ce707a2c666" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "safe-quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e530f7831f3feafcd5f1aae406ac205dd998436b4007c8e80f03eca78a88f7" +dependencies = [ + "safe-proc-macro2", +] + +[[package]] +name = "safe-regex" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15289bf322e0673d52756a18194167f2378ec1a15fe884af6e2d2cb934822b0" +dependencies = [ + "safe-regex-macro", +] + +[[package]] +name = "safe-regex-compiler" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "fba76fae590a2aa665279deb1f57b5098cbace01a0c5e60e262fcf55f7c51542" +dependencies = [ + "safe-proc-macro2", + "safe-quote", +] + +[[package]] +name = "safe-regex-macro" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c2e96b5c03f158d1b16ba79af515137795f4ad4e8de3f790518aae91f1d127" +dependencies = [ + "safe-proc-macro2", + "safe-regex-compiler", +] [[package]] name = "schemars" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +checksum = "763f8cd0d4c71ed8389c90cb8100cba87e763bd01a8e614d4f0af97bcd50a161" dependencies = [ "dyn-clone", "schemars_derive", @@ -681,9 +1612,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" dependencies = [ "proc-macro2", "quote", @@ -697,25 +1628,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", + "base16ct 0.1.1", + "der 0.6.1", "generic-array", - "pkcs8", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.8", + "generic-array", + "pkcs8 0.10.2", "subtle", "zeroize", ] [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.166" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -729,15 +1674,24 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.166" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.31", ] [[package]] @@ -753,15 +1707,26 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + [[package]] name = "sha2" version = "0.9.9" @@ -786,6 +1751,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "signature" version = "1.6.4" @@ -796,6 +1771,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "spki" version = "0.6.0" @@ -803,14 +1788,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "spki" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.8", +] [[package]] name = "subtle" @@ -818,6 +1807,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + [[package]] name = "syn" version = "1.0.109" @@ -831,58 +1829,135 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.23" +version = "2.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "tendermint" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda53c85447577769cdfc94c10a56f34afef2c00e4108badb57fce6b1a0c75eb" +dependencies = [ + "bytes", + "digest 0.10.7", + "ed25519", + "flex-error", + "futures", + "num-traits", + "once_cell", + "prost 0.11.9", + "prost-types", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "signature 1.6.4", + "subtle", + "subtle-encoding", + "tendermint-proto", + "time", + "zeroize", +] + +[[package]] +name = "tendermint-proto" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c943f78c929cdf14553842f705f2c30324bc35b9179caaa5c9b80620f60652e6" +dependencies = [ + "bytes", + "flex-error", + "num-derive", + "num-traits", + "prost 0.11.9", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + [[package]] name = "thiserror" -version = "1.0.41" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.41" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.31", ] [[package]] -name = "typenum" -version = "1.16.0" +name = "time" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "time-core", + "time-macros", +] [[package]] -name = "uint" -version = "0.9.5" +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", + "time-core", ] +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-xid" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "version_check" @@ -897,24 +1972,99 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "x-call-mock" -version = "0.1.0" +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ - "anyhow", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-common", - "cw-multi-test", - "cw-storage-plus", - "cw2", - "schemars", - "serde", - "thiserror", + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] diff --git a/Cargo.toml b/Cargo.toml index dbdd22b..31b7176 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,6 @@ [workspace] -members = [ -# "contracts/core-contracts/*", - "contracts/token-contracts/cw-hub-bnusd", - "contracts/cw-common", - "contracts/mock-contracts/x-call-mock",] - +members = ["contracts/token-contracts/cw-hub-bnusd", "contracts/cw-common"] +exclude = ["contracts/archway"] [profile.release] opt-level = 'z' @@ -18,4 +14,4 @@ incremental = false overflow-checks = true [workspace.dependencies] -debug_print = "1.0.0" \ No newline at end of file +debug_print = "1.0.0" diff --git a/contracts/cw-common/Cargo.toml b/contracts/cw-common/Cargo.toml index 42e8339..9d77a73 100644 --- a/contracts/cw-common/Cargo.toml +++ b/contracts/cw-common/Cargo.toml @@ -16,5 +16,7 @@ cosmwasm-std = { version = "1.2.5", default-features = false } rlp = { version = "0.5.2", default-features = false } cosmwasm-schema = "1.2.6" cw-storage-plus = "1.1.0" - +cw20-base = { version = "1.0.1", features = ["library"] } +cw-xcall-multi = {package="cw-xcall", git="https://github.com/icon-project/xcall-multi.git", branch="main", features=["library"]} +cw-xcall-lib={package="cw-xcall-lib", git="https://github.com/icon-project/xcall-multi.git", branch="main", features = ["library"]} diff --git a/contracts/cw-common/src/data_types.rs b/contracts/cw-common/src/data_types.rs index 81135bf..a1ec526 100644 --- a/contracts/cw-common/src/data_types.rs +++ b/contracts/cw-common/src/data_types.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use cosmwasm_schema::cw_serde; use cosmwasm_std::Addr; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; @@ -33,11 +35,14 @@ impl Encodable for CrossTransfer { } impl Decodable for CrossTransfer { - fn decode(rlp: &Rlp<'_>) -> Result { + fn decode(rlp: &Rlp<'_>) -> Result { + let from: String = rlp.val_at(1)?; + let to: String = rlp.val_at(2)?; Ok(Self { method: rlp.val_at(0)?, - from: NetworkAddress(rlp.val_at(1)?), - to: NetworkAddress(rlp.val_at(2)?), + from: NetworkAddress::from_str(&from) + .map_err(|_e| rlp::DecoderError::RlpInvalidLength)?, + to: NetworkAddress::from_str(&to).map_err(|_e| rlp::DecoderError::RlpInvalidLength)?, value: rlp.val_at(3)?, data: rlp.val_at(4)?, }) @@ -55,7 +60,7 @@ impl Encodable for CrossTransferRevert { } impl Decodable for CrossTransferRevert { - fn decode(rlp: &Rlp<'_>) -> Result { + fn decode(rlp: &Rlp<'_>) -> Result { let from: String = rlp.val_at(1)?; Ok(Self { method: rlp.val_at(0)?, diff --git a/contracts/cw-common/src/hub_token_msg.rs b/contracts/cw-common/src/hub_token_msg.rs index e325cbe..7151be8 100644 --- a/contracts/cw-common/src/hub_token_msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -1,7 +1,9 @@ -use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::Addr; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Uint128}; +use cw20::Expiration; use crate::network_address::NetworkAddress; +pub use cw20_base::msg::{ExecuteMsg as Cw20ExecuteMsg, QueryMsg}; #[cw_serde] pub struct InstantiateMsg { @@ -9,7 +11,6 @@ pub struct InstantiateMsg { pub hub_address: String, } -//TODO: Add network address as a parameter for xcall network address #[cw_serde] pub enum ExecuteMsg { Setup { @@ -26,8 +27,56 @@ pub enum ExecuteMsg { amount: u128, data: Vec, }, + /// Transfer is a base message to move tokens to another account without triggering actions + Transfer { + recipient: String, + amount: Uint128, + }, + /// Burn is a base message to destroy tokens forever + Burn { + amount: Uint128, + }, + /// Only with "approval" extension. Allows spender to access an additional amount tokens + /// from the owner's (env.sender) account. If expires is Some(), overwrites current allowance + /// expiration with this one. + IncreaseAllowance { + spender: String, + amount: Uint128, + expires: Option, + }, + /// Only with "approval" extension. Lowers the spender's access of tokens + /// from the owner's (env.sender) account by amount. If expires is Some(), overwrites current + /// allowance expiration with this one. + DecreaseAllowance { + spender: String, + amount: Uint128, + expires: Option, + }, + /// Only with "approval" extension. Transfers amount tokens from owner -> recipient + /// if `env.sender` has sufficient pre-approval. + TransferFrom { + owner: String, + recipient: String, + amount: Uint128, + }, + /// Only with "approval" extension. Destroys tokens forever + BurnFrom { + owner: String, + amount: Uint128, + }, + /// Only with the "mintable" extension. If authorized, creates amount new tokens + /// and adds to the recipient balance. + Mint { + recipient: String, + amount: Uint128, + }, + /// Only with the "mintable" extension. The current minter may set + /// a new minter. Setting the minter to None will remove the + /// token's minter forever. + UpdateMinter { + new_minter: Option, + }, } #[cw_serde] -#[derive(QueryResponses)] -pub enum QueryMsg {} +pub struct MigrateMsg {} diff --git a/contracts/cw-common/src/network_address.rs b/contracts/cw-common/src/network_address.rs index 0a759ba..6759b02 100644 --- a/contracts/cw-common/src/network_address.rs +++ b/contracts/cw-common/src/network_address.rs @@ -1,97 +1,13 @@ -use std::str::FromStr; +pub use cosmwasm_std::Addr; +pub use cw_xcall_lib::network_address::{NetId, NetworkAddress}; +pub use std::str::FromStr; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, StdError}; -use cw_storage_plus::{Key, KeyDeserialize, PrimaryKey}; - -#[cw_serde] -#[derive(Eq)] -pub struct NetId(String); - -impl From for NetId { - fn from(value: String) -> Self { - Self(value) - } -} - -impl ToString for NetId { - fn to_string(&self) -> String { - self.0.to_string() - } +pub trait IconAddressValidation { + fn validate_foreign_addresses(&self) -> bool; } -impl NetId { - pub fn as_str(&self) -> &str { - &self.0 - } -} - -impl FromStr for NetId { - type Err = StdError; - - fn from_str(s: &str) -> Result { - Ok(Self(s.to_owned())) - } -} - -impl<'a> PrimaryKey<'a> for NetId { - type Prefix = (); - - type SubPrefix = (); - - type Suffix = Self; - - type SuperSuffix = Self; - - fn key(&self) -> Vec { - vec![Key::Ref(self.0.as_bytes())] - } -} - -impl KeyDeserialize for NetId { - type Output = NetId; - - fn from_vec(value: Vec) -> cosmwasm_std::StdResult { - let result = String::from_utf8(value) - .map_err(StdError::invalid_utf8) - .unwrap(); - let net_id = NetId::from_str(&result).unwrap(); - Ok(net_id) - } -} - -#[cw_serde] -#[derive(Eq)] -pub struct NetworkAddress(pub String); - -impl NetworkAddress { - pub fn new(nid: &str, address: &str) -> Self { - Self(format!("{}/{}", nid, address)) - } - - pub fn nid(&self) -> NetId { - NetId(self.get_parts()[0].to_string()) - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn account(&self) -> Addr { - Addr::unchecked(self.get_parts()[1]) - } - - pub fn get_parts(&self) -> Vec<&str> { - let parts = self.0.split('/').collect::>(); - parts - } - - pub fn parse_parts(&self) -> (NetId, Addr) { - let parts = self.0.split('/').collect::>(); - (NetId(parts[0].to_string()), Addr::unchecked(parts[1])) - } - - pub fn validate(&self) -> bool { +impl IconAddressValidation for NetworkAddress { + fn validate_foreign_addresses(&self) -> bool { let parts = self.get_parts(); let net_id = parts[0].to_string(); let address = parts[1]; @@ -102,27 +18,6 @@ impl NetworkAddress { } } -impl ToString for NetworkAddress { - fn to_string(&self) -> String { - self.0.to_string() - } -} - -impl FromStr for NetworkAddress { - type Err = StdError; - - fn from_str(s: &str) -> Result { - let parts = s.split('/').collect::>(); - if parts.len() != 2 { - return Err(StdError::GenericErr { - msg: "Invalid Network Address".to_owned(), - }); - } - let na = format!("{}/{}", parts[0], parts[1]); - Ok(Self(na)) - } -} - fn validate_icon_address(address: &str) -> bool { let lowercase_address = address.to_lowercase(); @@ -140,11 +35,10 @@ fn is_valid_character_set(address: &str) -> bool { #[test] fn test_parse_btp_address() { let network_address = - NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()); - let (network, account) = network_address.parse_parts(); - assert_eq!(network, NetId("0x01.icon".to_string())); + NetworkAddress::from_str("0x01.icon/cx9876543210fedcba9876543210fedcba98765432").unwrap(); + assert_eq!(network_address.nid(), NetId::from_str("0x01.icon").unwrap()); assert_eq!( - account, + network_address.account(), Addr::unchecked("cx9876543210fedcba9876543210fedcba98765432") ); } @@ -152,32 +46,32 @@ fn test_parse_btp_address() { #[test] fn address_validation_test() { let network_address = - NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()); - let res = network_address.validate(); + NetworkAddress::from_str("0x7.icon/cxd06f80e28e989a67e297799ab1fb501cdddc2b4d").unwrap(); + let res = network_address.validate_foreign_addresses(); assert!(res); let network_address = - NetworkAddress("0x01.icon/hx9876543210fedcba9876543210fedcba98765432".to_string()); - let res = network_address.validate(); + NetworkAddress::from_str("0x01.icon/hx9876543210fedcba9876543210fedcba98765432").unwrap(); + let res = network_address.validate_foreign_addresses(); assert!(res); let network_address = - NetworkAddress("0x01.bsc/cx9876543210fedcba9876543210fedcba98765432".to_string()); - let res = network_address.validate(); + NetworkAddress::from_str("0x01.bsc/cx9876543210fedcba9876543210fedcba98765432").unwrap(); + let res = network_address.validate_foreign_addresses(); assert!(!res); let network_address = - NetworkAddress("0x01.icon/wx9876543210fedcba9876543210fedcba98765432".to_string()); - let res = network_address.validate(); + NetworkAddress::from_str("0x01.icon/wx9876543210fedcba9876543210fedcba98765432").unwrap(); + let res = network_address.validate_foreign_addresses(); assert!(!res); let network_address = - NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba9876542".to_string()); - let res = network_address.validate(); + NetworkAddress::from_str("0x01.icon/cx9876543210fedcba9876543210fedcba9876542").unwrap(); + let res = network_address.validate_foreign_addresses(); assert!(!res); let network_address = - NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba9876543_".to_string()); - let res = network_address.validate(); + NetworkAddress::from_str("0x01.icon/cx9876543210fedcba9876543210fedcba9876543_").unwrap(); + let res = network_address.validate_foreign_addresses(); assert!(!res); } diff --git a/contracts/cw-common/src/x_call_msg.rs b/contracts/cw-common/src/x_call_msg.rs index fd32ed7..1609ef3 100644 --- a/contracts/cw-common/src/x_call_msg.rs +++ b/contracts/cw-common/src/x_call_msg.rs @@ -1,32 +1,2 @@ -use cosmwasm_schema::{cw_serde, QueryResponses}; - -#[cw_serde] -struct NetworkAddress { - address: String, -} - -#[cw_serde] -pub struct InstantiateMsg {} - -#[cw_serde] -#[derive(QueryResponses)] -pub enum XCallQuery { - #[returns(NetworkAddress)] - GetNetworkAddress {}, -} - -//TODO: Use the ibc-integration/xcallmsg and xcall contract from ibc -#[cw_serde] -pub enum XCallMsg { - SendCallMessage { - to: String, - data: Vec, - rollback: Option>, - }, - - TestHandleCallMessage { - from: String, - data: Vec, - hub_token: String, - }, -} +pub use cw_xcall_lib::xcall_msg::ExecuteMsg as XCallMsg; +pub use cw_xcall_multi::msg::QueryMsg::GetNetworkAddress; diff --git a/contracts/mock-contracts/x-call-mock/.cargo/config b/contracts/mock-contracts/x-call-mock/.cargo/config deleted file mode 100644 index af5698e..0000000 --- a/contracts/mock-contracts/x-call-mock/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[alias] -wasm = "build --release --lib --target wasm32-unknown-unknown" -unit-test = "test --lib" -schema = "run --bin schema" diff --git a/contracts/mock-contracts/x-call-mock/Cargo.toml b/contracts/mock-contracts/x-call-mock/Cargo.toml deleted file mode 100644 index c76b097..0000000 --- a/contracts/mock-contracts/x-call-mock/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "x-call-mock" -version = "0.1.0" -authors = ["qwerty0789 "] -edition = "2021" - -exclude = [ - # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. - "contract.wasm", - "hash.txt", -] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = ["cdylib", "rlib"] - -[profile.release] -opt-level = 3 -debug = false -rpath = false -lto = true -debug-assertions = false -codegen-units = 1 -panic = 'abort' -incremental = false -overflow-checks = true - -[features] -# for more explicit tests, cargo test --features=backtraces -backtraces = ["cosmwasm-std/backtraces"] -# use library feature to disable all instantiate/execute/query exports -library = [] - -[package.metadata.scripts] -optimize = """docker run --rm -v "$(pwd)":/code \ - --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ - --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.10 -""" - -[dependencies] -cosmwasm-schema = "1.1.3" -cosmwasm-std = "1.1.3" -cosmwasm-storage = "1.1.3" -cw-storage-plus = "1.0.1" -cw2 = "1.0.1" -schemars = "0.8.10" -serde = { version = "1.0.145", default-features = false, features = ["derive"] } -thiserror = { version = "1.0.31" } -cw-common = { path = "../../cw-common" } -anyhow = "1.0.44" - -[dev-dependencies] -cw-multi-test = "0.16.4" diff --git a/contracts/mock-contracts/x-call-mock/README.md b/contracts/mock-contracts/x-call-mock/README.md deleted file mode 100644 index 054ea48..0000000 --- a/contracts/mock-contracts/x-call-mock/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# CosmWasm Starter Pack - -This is a template to build smart contracts in Rust to run inside a -[Cosmos SDK](https://github.com/cosmos/cosmos-sdk) module on all chains that enable it. -To understand the framework better, please read the overview in the -[cosmwasm repo](https://github.com/CosmWasm/cosmwasm/blob/master/README.md), -and dig into the [cosmwasm docs](https://www.cosmwasm.com). -This assumes you understand the theory and just want to get coding. - -## Creating a new repo from template - -Assuming you have a recent version of Rust and Cargo installed -(via [rustup](https://rustup.rs/)), -then the following should get you a new repo to start a contract: - -Install [cargo-generate](https://github.com/ashleygwilliams/cargo-generate) and cargo-run-script. -Unless you did that before, run this line now: - -```sh -cargo install cargo-generate --features vendored-openssl -cargo install cargo-run-script -``` - -Now, use it to create your new contract. -Go to the folder in which you want to place it and run: - -**Latest** - -```sh -cargo generate --git https://github.com/CosmWasm/cw-template.git --name PROJECT_NAME -``` - -For cloning minimal code repo: - -```sh -cargo generate --git https://github.com/CosmWasm/cw-template.git --name PROJECT_NAME -d minimal=true -``` - -**Older Version** - -Pass version as branch flag: - -```sh -cargo generate --git https://github.com/CosmWasm/cw-template.git --branch --name PROJECT_NAME -``` - -Example: - -```sh -cargo generate --git https://github.com/CosmWasm/cw-template.git --branch 0.16 --name PROJECT_NAME -``` - -You will now have a new folder called `PROJECT_NAME` (I hope you changed that to something else) -containing a simple working contract and build system that you can customize. - -## Create a Repo - -After generating, you have a initialized local git repo, but no commits, and no remote. -Go to a server (eg. github) and create a new upstream repo (called `YOUR-GIT-URL` below). -Then run the following: - -```sh -# this is needed to create a valid Cargo.lock file (see below) -cargo check -git branch -M main -git add . -git commit -m 'Initial Commit' -git remote add origin YOUR-GIT-URL -git push -u origin main -``` - -## CI Support - -We have template configurations for both [GitHub Actions](.github/workflows/Basic.yml) -and [Circle CI](.circleci/config.yml) in the generated project, so you can -get up and running with CI right away. - -One note is that the CI runs all `cargo` commands -with `--locked` to ensure it uses the exact same versions as you have locally. This also means -you must have an up-to-date `Cargo.lock` file, which is not auto-generated. -The first time you set up the project (or after adding any dep), you should ensure the -`Cargo.lock` file is updated, so the CI will test properly. This can be done simply by -running `cargo check` or `cargo unit-test`. - -## Using your project - -Once you have your custom repo, you should check out [Developing](./Developing.md) to explain -more on how to run tests and develop code. Or go through the -[online tutorial](https://docs.cosmwasm.com/) to get a better feel -of how to develop. - -[Publishing](./Publishing.md) contains useful information on how to publish your contract -to the world, once you are ready to deploy it on a running blockchain. And -[Importing](./Importing.md) contains information about pulling in other contracts or crates -that have been published. - -Please replace this README file with information about your specific project. You can keep -the `Developing.md` and `Publishing.md` files as useful referenced, but please set some -proper description in the README. diff --git a/contracts/mock-contracts/x-call-mock/src/bin/schema.rs b/contracts/mock-contracts/x-call-mock/src/bin/schema.rs deleted file mode 100644 index b1569be..0000000 --- a/contracts/mock-contracts/x-call-mock/src/bin/schema.rs +++ /dev/null @@ -1,11 +0,0 @@ -use cosmwasm_schema::write_api; - -use cw_common::x_call_msg::{InstantiateMsg, XCallMsg, XCallQuery}; - -fn main() { - write_api! { - instantiate: InstantiateMsg, - execute: XCallMsg, - query: XCallQuery, - } -} diff --git a/contracts/mock-contracts/x-call-mock/src/contract.rs b/contracts/mock-contracts/x-call-mock/src/contract.rs deleted file mode 100644 index 0ab8773..0000000 --- a/contracts/mock-contracts/x-call-mock/src/contract.rs +++ /dev/null @@ -1,101 +0,0 @@ -#[cfg(not(feature = "library"))] -use cosmwasm_std::entry_point; -use cosmwasm_std::{ - to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Response, StdResult, SubMsg, -}; -// use cw2::set_contract_version; - -use crate::error::ContractError; -use cosmwasm_std::{Reply, StdError}; -use cw_common::{ - hub_token_msg::ExecuteMsg, - x_call_msg::{InstantiateMsg, XCallMsg, XCallQuery}, -}; - -/* -// version info for migration info -const CONTRACT_NAME: &str = "crates.io:x-call-mock"; -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -*/ - -const REPLY_MSG_SUCCESS: u64 = 1; - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn instantiate( - _deps: DepsMut, - _env: Env, - _info: MessageInfo, - _msg: InstantiateMsg, -) -> Result { - Ok(Response::default()) -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn execute( - _deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: XCallMsg, -) -> Result { - match msg { - XCallMsg::SendCallMessage { to, data, rollback } => { - print!("to: {}", to); - print!("data: {:?}", data); - print!("rollback: {:?}", rollback); - let _network_address = to; - Ok(Response::default()) - } - XCallMsg::TestHandleCallMessage { - from, - data, - hub_token, - } => { - let call_message = ExecuteMsg::HandleCallMessage { - from: cw_common::network_address::NetworkAddress(from), - data, - }; - - let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { - contract_addr: hub_token, - msg: to_binary(&call_message)?, - funds: vec![], - }); - let sub_message = SubMsg::reply_always(wasm_execute_message, REPLY_MSG_SUCCESS); - - Ok(Response::new() - .add_submessage(sub_message) - .add_attribute("method", "testhandlecallmessage")) - } - } -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(_deps: Deps, _env: Env, _msg: XCallQuery) -> StdResult { - match _msg { - XCallQuery::GetNetworkAddress {} => Ok(to_binary( - "0x01.icon/cx9876543210fedcba9876543210fedcba98765432", - )?), - } -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { - match msg.id { - REPLY_MSG_SUCCESS => reply_msg_success(deps, env, msg), - _ => Err(ContractError::Std(StdError::generic_err( - "reply id not found", - ))), - } -} - -pub fn reply_msg_success(_deps: DepsMut, _env: Env, msg: Reply) -> Result { - match msg.result { - cosmwasm_std::SubMsgResult::Ok(_) => Ok(Response::default()), - cosmwasm_std::SubMsgResult::Err(error) => { - Err(StdError::GenericErr { msg: error }).map_err(Into::::into)? - } - } -} - -#[cfg(test)] -mod tests {} diff --git a/contracts/mock-contracts/x-call-mock/src/error.rs b/contracts/mock-contracts/x-call-mock/src/error.rs deleted file mode 100644 index 4a69d8f..0000000 --- a/contracts/mock-contracts/x-call-mock/src/error.rs +++ /dev/null @@ -1,13 +0,0 @@ -use cosmwasm_std::StdError; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum ContractError { - #[error("{0}")] - Std(#[from] StdError), - - #[error("Unauthorized")] - Unauthorized {}, - // Add any other custom errors you like here. - // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. -} diff --git a/contracts/mock-contracts/x-call-mock/src/helpers.rs b/contracts/mock-contracts/x-call-mock/src/helpers.rs deleted file mode 100644 index 5da9242..0000000 --- a/contracts/mock-contracts/x-call-mock/src/helpers.rs +++ /dev/null @@ -1,27 +0,0 @@ -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; - -use cw_common::x_call_msg::XCallMsg; - -/// CwTemplateContract is a wrapper around Addr that provides a lot of helpers -/// for working with this. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub struct CwTemplateContract(pub Addr); - -impl CwTemplateContract { - pub fn addr(&self) -> Addr { - self.0.clone() - } - - pub fn call>(&self, msg: T) -> StdResult { - let msg = to_binary(&msg.into())?; - Ok(WasmMsg::Execute { - contract_addr: self.addr().into(), - msg, - funds: vec![], - } - .into()) - } -} diff --git a/contracts/mock-contracts/x-call-mock/src/lib.rs b/contracts/mock-contracts/x-call-mock/src/lib.rs deleted file mode 100644 index 596ab8b..0000000 --- a/contracts/mock-contracts/x-call-mock/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod contract; -mod error; -pub mod helpers; -pub mod state; - -pub use crate::error::ContractError; diff --git a/contracts/mock-contracts/x-call-mock/src/state.rs b/contracts/mock-contracts/x-call-mock/src/state.rs deleted file mode 100644 index 8b13789..0000000 --- a/contracts/mock-contracts/x-call-mock/src/state.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml index ad149af..514f0bc 100644 --- a/contracts/token-contracts/cw-hub-bnusd/Cargo.toml +++ b/contracts/token-contracts/cw-hub-bnusd/Cargo.toml @@ -49,7 +49,14 @@ rlp = "0.5.2" [dev-dependencies] cw-multi-test = "0.16.4" -x-call-mock = { path = "../../mock-contracts/x-call-mock" } +cw-xcall-lib={package="cw-xcall-lib", git="https://github.com/icon-project/xcall-multi.git", branch="main"} +cw-xcall-multi = {package="cw-xcall", git="https://github.com/icon-project/xcall-multi.git", branch="main"} +cw-common-ibc = {package="cw-common", git = "https://github.com/icon-project/IBC-Integration.git", branch="main" } +cw_xcall_ibc_connection = { git = "https://github.com/icon-project/IBC-Integration.git", package = "cw-mock-ibc-connection", branch="feat/mock-ibc-connection-balanced-test" } +cw_mock_ibc_core = { git = "https://github.com/icon-project/IBC-Integration.git", branch="main", package="cw-mock-ibc-core" } +anyhow = { version = "1.0", default-features = false } +getrandom = { version = "0.2", features = ["custom"] } + [profile.release] # Do not perform backtrace for panic on release builds. diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json b/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json index 6031b81..a03088f 100644 --- a/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json +++ b/contracts/token-contracts/cw-hub-bnusd/schema/cw-hub-bnusd.json @@ -114,6 +114,230 @@ } }, "additionalProperties": false + }, + { + "description": "Transfer is a base message to move tokens to another account without triggering actions", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "recipient" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "recipient": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Burn is a base message to destroy tokens forever", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"approval\" extension. Allows spender to access an additional amount tokens from the owner's (env.sender) account. If expires is Some(), overwrites current allowance expiration with this one.", + "type": "object", + "required": [ + "increase_allowance" + ], + "properties": { + "increase_allowance": { + "type": "object", + "required": [ + "amount", + "spender" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "spender": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"approval\" extension. Lowers the spender's access of tokens from the owner's (env.sender) account by amount. If expires is Some(), overwrites current allowance expiration with this one.", + "type": "object", + "required": [ + "decrease_allowance" + ], + "properties": { + "decrease_allowance": { + "type": "object", + "required": [ + "amount", + "spender" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "spender": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"approval\" extension. Transfers amount tokens from owner -> recipient if `env.sender` has sufficient pre-approval.", + "type": "object", + "required": [ + "transfer_from" + ], + "properties": { + "transfer_from": { + "type": "object", + "required": [ + "amount", + "owner", + "recipient" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "owner": { + "type": "string" + }, + "recipient": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"approval\" extension. Destroys tokens forever", + "type": "object", + "required": [ + "burn_from" + ], + "properties": { + "burn_from": { + "type": "object", + "required": [ + "amount", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with the \"mintable\" extension. If authorized, creates amount new tokens and adds to the recipient balance.", + "type": "object", + "required": [ + "mint" + ], + "properties": { + "mint": { + "type": "object", + "required": [ + "amount", + "recipient" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "recipient": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with the \"mintable\" extension. The current minter may set a new minter. Setting the minter to None will remove the token's minter forever.", + "type": "object", + "required": [ + "update_minter" + ], + "properties": { + "update_minter": { + "type": "object", + "properties": { + "new_minter": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } ], "definitions": { @@ -121,18 +345,772 @@ "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", "type": "string" }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, "NetworkAddress": { "type": "string" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" } } }, "query": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "QueryMsg", - "type": "string", - "enum": [] + "oneOf": [ + { + "description": "Returns the current balance of the given address, 0 if unset.", + "type": "object", + "required": [ + "balance" + ], + "properties": { + "balance": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns metadata on the contract - name, decimals, supply, etc.", + "type": "object", + "required": [ + "token_info" + ], + "properties": { + "token_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"mintable\" extension. Returns who can mint and the hard cap on maximum tokens after minting.", + "type": "object", + "required": [ + "minter" + ], + "properties": { + "minter": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"allowance\" extension. Returns how much spender can use from owner account, 0 if unset.", + "type": "object", + "required": [ + "allowance" + ], + "properties": { + "allowance": { + "type": "object", + "required": [ + "owner", + "spender" + ], + "properties": { + "owner": { + "type": "string" + }, + "spender": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"enumerable\" extension (and \"allowances\") Returns all allowances this owner has approved. Supports pagination.", + "type": "object", + "required": [ + "all_allowances" + ], + "properties": { + "all_allowances": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"enumerable\" extension (and \"allowances\") Returns all allowances this spender has been granted. Supports pagination.", + "type": "object", + "required": [ + "all_spender_allowances" + ], + "properties": { + "all_spender_allowances": { + "type": "object", + "required": [ + "spender" + ], + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "spender": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"enumerable\" extension Returns all accounts that have balances. Supports pagination.", + "type": "object", + "required": [ + "all_accounts" + ], + "properties": { + "all_accounts": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"marketing\" extension Returns more metadata on the contract to display in the client: - description, logo, project url, etc.", + "type": "object", + "required": [ + "marketing_info" + ], + "properties": { + "marketing_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"marketing\" extension Downloads the embedded logo data (if stored on chain). Errors if no logo data is stored for this contract.", + "type": "object", + "required": [ + "download_logo" + ], + "properties": { + "download_logo": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] }, "migrate": null, "sudo": null, - "responses": {} + "responses": { + "all_accounts": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllAccountsResponse", + "type": "object", + "required": [ + "accounts" + ], + "properties": { + "accounts": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "all_allowances": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllAllowancesResponse", + "type": "object", + "required": [ + "allowances" + ], + "properties": { + "allowances": { + "type": "array", + "items": { + "$ref": "#/definitions/AllowanceInfo" + } + } + }, + "definitions": { + "AllowanceInfo": { + "type": "object", + "required": [ + "allowance", + "expires", + "spender" + ], + "properties": { + "allowance": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "spender": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "all_spender_allowances": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllSpenderAllowancesResponse", + "type": "object", + "required": [ + "allowances" + ], + "properties": { + "allowances": { + "type": "array", + "items": { + "$ref": "#/definitions/SpenderAllowanceInfo" + } + } + }, + "definitions": { + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "SpenderAllowanceInfo": { + "type": "object", + "required": [ + "allowance", + "expires", + "owner" + ], + "properties": { + "allowance": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "allowance": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllowanceResponse", + "type": "object", + "required": [ + "allowance", + "expires" + ], + "properties": { + "allowance": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "$ref": "#/definitions/Expiration" + } + }, + "definitions": { + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "balance": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "BalanceResponse", + "type": "object", + "required": [ + "balance" + ], + "properties": { + "balance": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "download_logo": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DownloadLogoResponse", + "description": "When we download an embedded logo, we get this response type. We expect a SPA to be able to accept this info and display it.", + "type": "object", + "required": [ + "data", + "mime_type" + ], + "properties": { + "data": { + "$ref": "#/definitions/Binary" + }, + "mime_type": { + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + } + } + }, + "marketing_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MarketingInfoResponse", + "type": "object", + "properties": { + "description": { + "description": "A longer description of the token and it's utility. Designed for tooltips or such", + "type": [ + "string", + "null" + ] + }, + "logo": { + "description": "A link to the logo, or a comment there is an on-chain logo stored", + "anyOf": [ + { + "$ref": "#/definitions/LogoInfo" + }, + { + "type": "null" + } + ] + }, + "marketing": { + "description": "The address (if any) who can update this data structure", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "project": { + "description": "A URL pointing to the project behind this token.", + "type": [ + "string", + "null" + ] + } + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "LogoInfo": { + "description": "This is used to display logo info, provide a link or inform there is one that can be downloaded from the blockchain itself", + "oneOf": [ + { + "description": "A reference to an externally hosted logo. Must be a valid HTTP or HTTPS URL.", + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "There is an embedded logo on the chain, make another call to download it.", + "type": "string", + "enum": [ + "embedded" + ] + } + ] + } + } + }, + "minter": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MinterResponse", + "type": "object", + "required": [ + "minter" + ], + "properties": { + "cap": { + "description": "cap is a hard cap on total supply that can be achieved by minting. Note that this refers to total_supply. If None, there is unlimited cap.", + "anyOf": [ + { + "$ref": "#/definitions/Uint128" + }, + { + "type": "null" + } + ] + }, + "minter": { + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "token_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TokenInfoResponse", + "type": "object", + "required": [ + "decimals", + "name", + "symbol", + "total_supply" + ], + "properties": { + "decimals": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "total_supply": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + } + } } diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json index 899fe07..9a264c8 100644 --- a/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/execute.json @@ -92,6 +92,230 @@ } }, "additionalProperties": false + }, + { + "description": "Transfer is a base message to move tokens to another account without triggering actions", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "recipient" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "recipient": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Burn is a base message to destroy tokens forever", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"approval\" extension. Allows spender to access an additional amount tokens from the owner's (env.sender) account. If expires is Some(), overwrites current allowance expiration with this one.", + "type": "object", + "required": [ + "increase_allowance" + ], + "properties": { + "increase_allowance": { + "type": "object", + "required": [ + "amount", + "spender" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "spender": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"approval\" extension. Lowers the spender's access of tokens from the owner's (env.sender) account by amount. If expires is Some(), overwrites current allowance expiration with this one.", + "type": "object", + "required": [ + "decrease_allowance" + ], + "properties": { + "decrease_allowance": { + "type": "object", + "required": [ + "amount", + "spender" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "spender": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"approval\" extension. Transfers amount tokens from owner -> recipient if `env.sender` has sufficient pre-approval.", + "type": "object", + "required": [ + "transfer_from" + ], + "properties": { + "transfer_from": { + "type": "object", + "required": [ + "amount", + "owner", + "recipient" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "owner": { + "type": "string" + }, + "recipient": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"approval\" extension. Destroys tokens forever", + "type": "object", + "required": [ + "burn_from" + ], + "properties": { + "burn_from": { + "type": "object", + "required": [ + "amount", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with the \"mintable\" extension. If authorized, creates amount new tokens and adds to the recipient balance.", + "type": "object", + "required": [ + "mint" + ], + "properties": { + "mint": { + "type": "object", + "required": [ + "amount", + "recipient" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "recipient": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with the \"mintable\" extension. The current minter may set a new minter. Setting the minter to None will remove the token's minter forever.", + "type": "object", + "required": [ + "update_minter" + ], + "properties": { + "update_minter": { + "type": "object", + "properties": { + "new_minter": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } ], "definitions": { @@ -99,8 +323,71 @@ "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", "type": "string" }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, "NetworkAddress": { "type": "string" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" } } } diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/query.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/query.json index 0f592a1..8e4dcb3 100644 --- a/contracts/token-contracts/cw-hub-bnusd/schema/raw/query.json +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/query.json @@ -1,6 +1,212 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "QueryMsg", - "type": "string", - "enum": [] + "oneOf": [ + { + "description": "Returns the current balance of the given address, 0 if unset.", + "type": "object", + "required": [ + "balance" + ], + "properties": { + "balance": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns metadata on the contract - name, decimals, supply, etc.", + "type": "object", + "required": [ + "token_info" + ], + "properties": { + "token_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"mintable\" extension. Returns who can mint and the hard cap on maximum tokens after minting.", + "type": "object", + "required": [ + "minter" + ], + "properties": { + "minter": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"allowance\" extension. Returns how much spender can use from owner account, 0 if unset.", + "type": "object", + "required": [ + "allowance" + ], + "properties": { + "allowance": { + "type": "object", + "required": [ + "owner", + "spender" + ], + "properties": { + "owner": { + "type": "string" + }, + "spender": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"enumerable\" extension (and \"allowances\") Returns all allowances this owner has approved. Supports pagination.", + "type": "object", + "required": [ + "all_allowances" + ], + "properties": { + "all_allowances": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"enumerable\" extension (and \"allowances\") Returns all allowances this spender has been granted. Supports pagination.", + "type": "object", + "required": [ + "all_spender_allowances" + ], + "properties": { + "all_spender_allowances": { + "type": "object", + "required": [ + "spender" + ], + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "spender": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"enumerable\" extension Returns all accounts that have balances. Supports pagination.", + "type": "object", + "required": [ + "all_accounts" + ], + "properties": { + "all_accounts": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"marketing\" extension Returns more metadata on the contract to display in the client: - description, logo, project url, etc.", + "type": "object", + "required": [ + "marketing_info" + ], + "properties": { + "marketing_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only with \"marketing\" extension Downloads the embedded logo data (if stored on chain). Errors if no logo data is stored for this contract.", + "type": "object", + "required": [ + "download_logo" + ], + "properties": { + "download_logo": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] } diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_accounts.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_accounts.json new file mode 100644 index 0000000..cea50fb --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_accounts.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllAccountsResponse", + "type": "object", + "required": [ + "accounts" + ], + "properties": { + "accounts": { + "type": "array", + "items": { + "type": "string" + } + } + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_allowances.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_allowances.json new file mode 100644 index 0000000..0128722 --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_allowances.json @@ -0,0 +1,101 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllAllowancesResponse", + "type": "object", + "required": [ + "allowances" + ], + "properties": { + "allowances": { + "type": "array", + "items": { + "$ref": "#/definitions/AllowanceInfo" + } + } + }, + "definitions": { + "AllowanceInfo": { + "type": "object", + "required": [ + "allowance", + "expires", + "spender" + ], + "properties": { + "allowance": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "spender": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_spender_allowances.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_spender_allowances.json new file mode 100644 index 0000000..f682c7b --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_all_spender_allowances.json @@ -0,0 +1,101 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllSpenderAllowancesResponse", + "type": "object", + "required": [ + "allowances" + ], + "properties": { + "allowances": { + "type": "array", + "items": { + "$ref": "#/definitions/SpenderAllowanceInfo" + } + } + }, + "definitions": { + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "SpenderAllowanceInfo": { + "type": "object", + "required": [ + "allowance", + "expires", + "owner" + ], + "properties": { + "allowance": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_allowance.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_allowance.json new file mode 100644 index 0000000..dbaf97d --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_allowance.json @@ -0,0 +1,82 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllowanceResponse", + "type": "object", + "required": [ + "allowance", + "expires" + ], + "properties": { + "allowance": { + "$ref": "#/definitions/Uint128" + }, + "expires": { + "$ref": "#/definitions/Expiration" + } + }, + "definitions": { + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_balance.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_balance.json new file mode 100644 index 0000000..7dcf4d4 --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_balance.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "BalanceResponse", + "type": "object", + "required": [ + "balance" + ], + "properties": { + "balance": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_download_logo.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_download_logo.json new file mode 100644 index 0000000..c5aa32b --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_download_logo.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DownloadLogoResponse", + "description": "When we download an embedded logo, we get this response type. We expect a SPA to be able to accept this info and display it.", + "type": "object", + "required": [ + "data", + "mime_type" + ], + "properties": { + "data": { + "$ref": "#/definitions/Binary" + }, + "mime_type": { + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + } + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_marketing_info.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_marketing_info.json new file mode 100644 index 0000000..c36ee5f --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_marketing_info.json @@ -0,0 +1,74 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MarketingInfoResponse", + "type": "object", + "properties": { + "description": { + "description": "A longer description of the token and it's utility. Designed for tooltips or such", + "type": [ + "string", + "null" + ] + }, + "logo": { + "description": "A link to the logo, or a comment there is an on-chain logo stored", + "anyOf": [ + { + "$ref": "#/definitions/LogoInfo" + }, + { + "type": "null" + } + ] + }, + "marketing": { + "description": "The address (if any) who can update this data structure", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "project": { + "description": "A URL pointing to the project behind this token.", + "type": [ + "string", + "null" + ] + } + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "LogoInfo": { + "description": "This is used to display logo info, provide a link or inform there is one that can be downloaded from the blockchain itself", + "oneOf": [ + { + "description": "A reference to an externally hosted logo. Must be a valid HTTP or HTTPS URL.", + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "There is an embedded logo on the chain, make another call to download it.", + "type": "string", + "enum": [ + "embedded" + ] + } + ] + } + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_minter.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_minter.json new file mode 100644 index 0000000..eaf9718 --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_minter.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MinterResponse", + "type": "object", + "required": [ + "minter" + ], + "properties": { + "cap": { + "description": "cap is a hard cap on total supply that can be achieved by minting. Note that this refers to total_supply. If None, there is unlimited cap.", + "anyOf": [ + { + "$ref": "#/definitions/Uint128" + }, + { + "type": "null" + } + ] + }, + "minter": { + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_token_info.json b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_token_info.json new file mode 100644 index 0000000..0e84d12 --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/schema/raw/response_to_token_info.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TokenInfoResponse", + "type": "object", + "required": [ + "decimals", + "name", + "symbol", + "total_supply" + ], + "properties": { + "decimals": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "total_supply": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs index 113c26a..98ddbf9 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/contract.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/contract.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use crate::constants::{ REPLY_MSG_SUCCESS, TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, X_CROSS_TRANSFER, X_CROSS_TRANSFER_REVERT, @@ -6,55 +8,57 @@ use crate::error::ContractError; use crate::state::{ DESTINATION_TOKEN_ADDRESS, DESTINATION_TOKEN_NET, NID, OWNER, X_CALL, X_CALL_NETWORK_ADDRESS, }; +use cw_common::network_address::IconAddressValidation; + #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult}; +use cosmwasm_std::{ + to_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryRequest, Reply, Response, + StdError, StdResult, WasmQuery, +}; + use cw2::set_contract_version; -use cw_common::hub_token_msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use cw_common::x_call_msg::{XCallMsg, XCallQuery}; +use cw_common::hub_token_msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use cw_common::x_call_msg::{GetNetworkAddress, XCallMsg}; -use cw20_base::contract::{execute_burn, execute_mint}; +use cw20_base::allowances::{ + execute_burn_from, execute_decrease_allowance, execute_increase_allowance, + execute_transfer_from, query_allowance, +}; +use cw20_base::contract::{ + execute_burn, execute_mint, execute_transfer, execute_update_minter, query_download_logo, + query_marketing_info, +}; +use cw20_base::contract::{query_balance, query_minter, query_token_info}; +use cw20_base::enumerable::{query_all_accounts, query_owner_allowances, query_spender_allowances}; use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; use cw_common::network_address::NetworkAddress; +use debug_print::debug_println; use rlp::Rlp; use cw_common::data_types::{CrossTransfer, CrossTransferRevert}; - -// version info for migration info const CONTRACT_NAME: &str = "crates.io:cw-hub-bnusd"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, - _env: Env, - _info: MessageInfo, + env: Env, + info: MessageInfo, msg: InstantiateMsg, ) -> Result { - // create initial accounts - // store token info using cw20-base format set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION) .map_err(ContractError::Std)?; - + debug_println!("Instantiate cw-hub-bnusd contract...{:?}", msg); let x_call_addr = deps .api .addr_validate(&msg.x_call) .map_err(ContractError::Std)?; - let data = TokenInfo { - name: TOKEN_NAME.to_string(), - symbol: TOKEN_SYMBOL.to_string(), - decimals: TOKEN_DECIMALS, - total_supply: TOKEN_TOTAL_SUPPLY, - mint: Some(MinterData { - minter: x_call_addr, - cap: None, - }), - }; - TOKEN_INFO - .save(deps.storage, &data) - .map_err(ContractError::Std)?; - Ok(Response::default()) + OWNER.save(deps.storage, &info.sender)?; + let hub_network_address = + NetworkAddress::from_str(&msg.hub_address).map_err(ContractError::Std)?; + setup_function(deps, env, x_call_addr, hub_network_address) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -75,12 +79,73 @@ pub fn execute( ExecuteMsg::CrossTransfer { to, amount, data } => { execute::cross_transfer(deps, env, info, to, amount, data) } + ExecuteMsg::Transfer { recipient, amount } => { + execute_transfer(deps, env, info, recipient, amount) + .map_err(ContractError::Cw20BaseError) + } + ExecuteMsg::Burn { amount } => { + execute_burn(deps, env, info, amount).map_err(ContractError::Cw20BaseError) + } + ExecuteMsg::IncreaseAllowance { + spender, + amount, + expires, + } => execute_increase_allowance(deps, env, info, spender, amount, expires) + .map_err(ContractError::Cw20BaseError), + ExecuteMsg::DecreaseAllowance { + spender, + amount, + expires, + } => execute_decrease_allowance(deps, env, info, spender, amount, expires) + .map_err(ContractError::Cw20BaseError), + ExecuteMsg::TransferFrom { + owner, + recipient, + amount, + } => execute_transfer_from(deps, env, info, owner, recipient, amount) + .map_err(ContractError::Cw20BaseError), + ExecuteMsg::BurnFrom { owner, amount } => { + execute_burn_from(deps, env, info, owner, amount).map_err(ContractError::Cw20BaseError) + } + ExecuteMsg::Mint { recipient, amount } => { + execute_mint(deps, env, info, recipient, amount).map_err(ContractError::Cw20BaseError) + } + ExecuteMsg::UpdateMinter { new_minter } => { + execute_update_minter(deps, env, info, new_minter).map_err(ContractError::Cw20BaseError) + } } } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { - unimplemented!() +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Balance { address } => to_binary(&query_balance(deps, address)?), + QueryMsg::TokenInfo {} => to_binary(&query_token_info(deps)?), + QueryMsg::Minter {} => to_binary(&query_minter(deps)?), + QueryMsg::Allowance { owner, spender } => { + to_binary(&query_allowance(deps, owner, spender)?) + } + QueryMsg::AllAllowances { + owner, + start_after, + limit, + } => to_binary(&query_owner_allowances(deps, owner, start_after, limit)?), + QueryMsg::AllSpenderAllowances { + spender, + start_after, + limit, + } => to_binary(&query_spender_allowances( + deps, + spender, + start_after, + limit, + )?), + QueryMsg::AllAccounts { start_after, limit } => { + to_binary(&query_all_accounts(deps, start_after, limit)?) + } + QueryMsg::MarketingInfo {} => to_binary(&query_marketing_info(deps)?), + QueryMsg::DownloadLogo {} => to_binary(&query_download_logo(deps)?), + } } #[cfg_attr(not(feature = "library"), entry_point)] @@ -91,6 +156,14 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION) + .map_err(ContractError::Std)?; + + Ok(Response::default().add_attribute("migrate", "successful")) +} + pub fn reply_msg_success(_deps: DepsMut, _env: Env, msg: Reply) -> Result { match msg.result { cosmwasm_std::SubMsgResult::Ok(_) => Ok(Response::default()), @@ -104,55 +177,27 @@ mod execute { use std::str::from_utf8; use bytes::BytesMut; - use cosmwasm_std::{to_binary, Addr, CosmosMsg, Empty, Event, QueryRequest, SubMsg, WasmQuery}; + use cosmwasm_std::{to_binary, Addr, CosmosMsg, SubMsg}; use cw_common::network_address::NetId; use debug_print::debug_println; use rlp::{decode, encode}; + use crate::events::{emit_cross_transfer_event, emit_cross_transfer_revert_event}; + use super::*; pub fn setup( deps: DepsMut, - _env: Env, + env: Env, info: MessageInfo, x_call: Addr, hub_network_address: NetworkAddress, ) -> Result { - deps.api - .addr_validate(x_call.as_ref()) - .map_err(ContractError::Std)?; - - if !hub_network_address.validate() { - return Err(ContractError::InvalidNetworkAddress); - } - - X_CALL - .save(deps.storage, &x_call) - .map_err(ContractError::Std)?; - - let query_message = XCallQuery::GetNetworkAddress {}; - - let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: x_call.to_string(), - msg: to_binary(&query_message).map_err(ContractError::Std)?, - }); - - let x_call_network_address: NetworkAddress = - deps.querier.query(&query).map_err(ContractError::Std)?; - - if x_call_network_address.is_empty() { - return Err(ContractError::AddressNotFound); + let owner = OWNER.load(deps.storage)?; + if owner != info.sender { + return Err(ContractError::Unauthorized); } - let (nid, _) = x_call_network_address.parse_parts(); - let (hub_net, hub_address) = hub_network_address.parse_parts(); - debug_println!("setup {:?},{:?},{:?}", hub_net, hub_address, nid); - X_CALL_NETWORK_ADDRESS.save(deps.storage, &x_call_network_address)?; - NID.save(deps.storage, &nid)?; - DESTINATION_TOKEN_ADDRESS.save(deps.storage, &hub_address)?; - DESTINATION_TOKEN_NET.save(deps.storage, &hub_net)?; - OWNER.save(deps.storage, &info.sender)?; - - Ok(Response::default()) + setup_function(deps, env, x_call, hub_network_address) } pub fn handle_call_message( @@ -162,10 +207,9 @@ mod execute { from: NetworkAddress, data: Vec, ) -> Result { - if !from.validate() { + if !from.validate_foreign_addresses() { return Err(ContractError::InvalidNetworkAddress); } - let xcall = X_CALL.load(deps.storage)?; if info.sender != xcall { return Err(ContractError::OnlyCallService); @@ -196,7 +240,7 @@ mod execute { } } - Ok(Response::default()) + Ok(Response::new().add_attribute("action", "handle_call_message")) } pub fn cross_transfer( @@ -207,7 +251,7 @@ mod execute { amount: u128, data: Vec, ) -> Result { - if !to.validate() { + if !to.validate_foreign_addresses() { return Err(ContractError::InvalidNetworkAddress); } let funds = info.funds.clone(); @@ -234,9 +278,11 @@ mod execute { let hub_token_address = NetworkAddress::new(&hub_net.to_string(), hub_address.as_ref()); let call_message = XCallMsg::SendCallMessage { - to: hub_token_address.to_string(), + to: hub_token_address, data: encode(&call_data).to_vec(), rollback: Some(encode(&rollback_data).to_vec()), + sources: None, + destinations: None, }; let wasm_execute_message: CosmosMsg = CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute { @@ -252,16 +298,8 @@ mod execute { let result = execute_burn(deps, env, info, amount.into()).map_err(ContractError::Cw20BaseError)?; + let event = emit_cross_transfer_event("CrossTransfer".to_string(), from, to, amount, data); - debug_println!("burn from {:?}", sub_message); - - //TODO: emit a event log for cross transfer - let event = Event::new("CrossTransfer") - .add_attribute("from", from.to_string()) - .add_attribute("to", to.to_string()) - .add_attribute("value", amount.to_string()) - .add_attribute("data", hex_encode(data)); - debug_println!("this is {:?}", event); Ok(result .add_submessage(sub_message) .add_attribute("method", "cross_transfer") @@ -275,8 +313,7 @@ mod execute { from: NetworkAddress, cross_transfer_data: CrossTransfer, ) -> Result { - debug_println!("xcrosstransfer {:?}", cross_transfer_data); - if !cross_transfer_data.from.validate() || !cross_transfer_data.to.validate() { + if !cross_transfer_data.from.validate_foreign_addresses() { return Err(ContractError::InvalidNetworkAddress); } let nid = NID.load(deps.storage)?; @@ -291,10 +328,11 @@ mod execute { if from != network_address { return Err(ContractError::WrongAddress {}); } - - //TODO: add a validation check for ICON address in network address library - let (net, account) = NetworkAddress::parse_parts(&cross_transfer_data.to); - debug_println!("net nid comparision {:?},{:?}", net, nid); + let (net, account) = ( + cross_transfer_data.to.nid(), + cross_transfer_data.to.account(), + ); + debug_println!("net nid comparison {:?},{:?}", net, nid); if net != nid { return Err(ContractError::WrongNetwork); } @@ -312,13 +350,14 @@ mod execute { ) .expect("Fail to mint"); - let event = Event::new("XCrossTransfer") - .add_attribute("from", cross_transfer_data.from.to_string()) - .add_attribute("to", cross_transfer_data.to.to_string()) - .add_attribute("value", cross_transfer_data.value.to_string()) - .add_attribute("data", hex_encode(cross_transfer_data.data)); + let event = emit_cross_transfer_event( + "CrossTransfer".to_string(), + cross_transfer_data.from, + cross_transfer_data.to, + cross_transfer_data.value, + cross_transfer_data.data, + ); - //TODO: add event for cross transfer with relevant parameters Ok(res .add_attribute("method", "x_cross_transfer") .add_event(event)) @@ -332,9 +371,6 @@ mod execute { cross_transfer_revert_data: CrossTransferRevert, ) -> Result { debug_println!("this is {:?},{:?}", cross_transfer_revert_data, from); - if !from.validate() { - return Err(ContractError::InvalidNetworkAddress); - } deps.api .addr_validate(cross_transfer_revert_data.from.as_ref()) .map_err(ContractError::Std)?; @@ -347,52 +383,103 @@ mod execute { cross_transfer_revert_data.value.into(), ) .expect("Fail to mint"); - let event = Event::new("XCrossTransferRevert") - .add_attribute("from", cross_transfer_revert_data.from) - .add_attribute("value", cross_transfer_revert_data.value.to_string()); + let event = emit_cross_transfer_revert_event( + "CrossTransferRevert".to_string(), + cross_transfer_revert_data.from, + cross_transfer_revert_data.value, + ); Ok(res .add_attribute("method", "x_cross_transfer_revert") .add_event(event)) } +} - fn hex_encode(data: Vec) -> String { - debug_println!("this is {:?}", data); - if data.is_empty() { - debug_println!("this is empty"); - "null".to_string() - } else { - let data = hex::encode(data); - debug_println!("this is not empty, {}", data); - data - } +fn setup_function( + deps: DepsMut, + _env: Env, + x_call: Addr, + hub_network_address: NetworkAddress, +) -> Result { + debug_println!( + "Instantiate cw-hub-bnusd contract...{:?}", + hub_network_address + ); + + let x_call_addr = deps + .api + .addr_validate(x_call.as_ref()) + .map_err(ContractError::Std)?; + + let data = TokenInfo { + name: TOKEN_NAME.to_string(), + symbol: TOKEN_SYMBOL.to_string(), + decimals: TOKEN_DECIMALS, + total_supply: TOKEN_TOTAL_SUPPLY, + mint: Some(MinterData { + minter: x_call_addr, + cap: None, + }), + }; + + TOKEN_INFO + .save(deps.storage, &data) + .map_err(ContractError::Std)?; + + if !hub_network_address.validate_foreign_addresses() { + return Err(ContractError::InvalidNetworkAddress); + } + + X_CALL + .save(deps.storage, &x_call) + .map_err(ContractError::Std)?; + + let query_message = GetNetworkAddress {}; + + let query: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: x_call.to_string(), + msg: to_binary(&query_message).map_err(ContractError::Std)?, + }); + + let x_call_network_address: NetworkAddress = + deps.querier.query(&query).map_err(ContractError::Std)?; + + if x_call_network_address.to_string().is_empty() { + return Err(ContractError::AddressNotFound); } + let nid = x_call_network_address.nid(); + let (hub_net, hub_address) = (hub_network_address.nid(), hub_network_address.account()); + debug_println!("setup {:?},{:?},{:?}", hub_net, hub_address, nid); + X_CALL_NETWORK_ADDRESS.save(deps.storage, &x_call_network_address)?; + NID.save(deps.storage, &nid)?; + DESTINATION_TOKEN_ADDRESS.save(deps.storage, &hub_address)?; + DESTINATION_TOKEN_NET.save(deps.storage, &hub_net)?; + Ok(Response::default()) } #[cfg(test)] mod rlp_test { - use std::str::from_utf8; + use std::str::{from_utf8, FromStr}; use bytes::BytesMut; use cw_common::{data_types::CrossTransfer, network_address::NetworkAddress}; use rlp::{decode, encode, Rlp}; #[test] - fn encodetest() { + fn encode_test() { let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned()), - to: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()), + from: NetworkAddress::from_str("0x01.icon/cx9876543210fedcba9876543210fedcba98765432") + .unwrap(), + to: NetworkAddress::from_str("archway/archway1ryhtghkyx9kac8m9xl02ac839g4f9qhqkd9slk") + .unwrap(), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, ], }; - // let mut stream = RlpStream::new(); let encoded_bytes = encode(&call_data).to_vec(); - // let encoded_data: Vec = stream.out().to_vec(); - let data: CrossTransfer = decode(&encoded_bytes).unwrap(); print!("this is {:?}", data); @@ -403,7 +490,6 @@ mod rlp_test { let method = from_utf8(data).unwrap(); print!("this is {:?}", method) - // TODO: Add fixed values for tests from java tests for encoding and decoding } } @@ -435,14 +521,26 @@ mod tests { hub_address: "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), }; + deps.querier.update_wasm(|r| match r { + WasmQuery::Smart { + contract_addr: _, + msg: _, + } => SystemResult::Ok(ContractResult::Ok( + to_binary("0x01.icon/cx9876543210fedcba9876543210fedcba98765432").unwrap(), + )), + _ => todo!(), + }); + let res = instantiate(deps.as_mut(), env.clone(), info.clone(), msg); + debug_println!("res {:?}", res); assert!(res.is_ok()); let setup_message = ExecuteMsg::Setup { x_call: Addr::unchecked("archway123fdth".to_owned()), - hub_address: NetworkAddress( - "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), - ), + hub_address: NetworkAddress::from_str( + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432", + ) + .unwrap(), }; deps.querier.update_wasm(|r| match r { @@ -467,20 +565,20 @@ mod tests { } #[test] - fn execute_handle_call_xcrosstransfer_test() { + fn execute_handle_call_x_cross_transfer_test() { let (mut deps, env, info) = setup("archway123fdth"); let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned()), - to: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()), + from: NetworkAddress::from_str("0x01.icon/cx9876543210fedcba9876543210fedcba98765432") + .unwrap(), + to: NetworkAddress::from_str("0x01.icon/cx9876543210fedcba9876543210fedcba98765432") + .unwrap(), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, ], }; - - // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); let res = execute( @@ -488,9 +586,10 @@ mod tests { env, info, ExecuteMsg::HandleCallMessage { - from: NetworkAddress( - "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), - ), + from: NetworkAddress::from_str( + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432", + ) + .unwrap(), data, }, ); @@ -503,15 +602,15 @@ mod tests { let call_data = CrossTransfer { method: "xCrossTransfer".to_string(), - from: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned()), - to: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765452".to_string()), + from: NetworkAddress::from_str("0x01.icon/cx9876543210fedcba9876543210fedcba98765432") + .unwrap(), + to: NetworkAddress::from_str("0x01.icon/cx9876543210fedcba9876543210fedcba98765452") + .unwrap(), value: 1000, data: vec![ 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, ], }; - - // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); let _res: Response = execute( @@ -519,9 +618,10 @@ mod tests { env.clone(), info, ExecuteMsg::HandleCallMessage { - from: NetworkAddress( - "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), - ), + from: NetworkAddress::from_str( + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432", + ) + .unwrap(), data, }, ) @@ -529,16 +629,15 @@ mod tests { let info = mock_info("cx9876543210fedcba9876543210fedcba98765452", &[]); - // execute_mint(deps, env, info, info.sender.to_string(), 1000); - let res = execute( deps.as_mut(), env, info, ExecuteMsg::CrossTransfer { - to: NetworkAddress( - "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), - ), + to: NetworkAddress::from_str( + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432", + ) + .unwrap(), amount: 1000, data: vec![1, 2, 3, 4, 5], }, @@ -548,7 +647,7 @@ mod tests { } #[test] - fn execute_handle_call_test_xcrossrevert() { + fn execute_handle_call_test_xcross_revert() { let (mut deps, env, info) = setup("archway123fdth"); let call_data = CrossTransferRevert { @@ -558,8 +657,6 @@ mod tests { ), value: 1000, }; - - // let mut stream = RlpStream::new(); let data = encode(&call_data).to_vec(); let res = execute( @@ -567,9 +664,10 @@ mod tests { env, info, ExecuteMsg::HandleCallMessage { - from: NetworkAddress( - "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), - ), + from: NetworkAddress::from_str( + "0x01.icon/cx9876543210fedcba9876543210fedcba98765432", + ) + .unwrap(), data, }, ); diff --git a/contracts/token-contracts/cw-hub-bnusd/src/error.rs b/contracts/token-contracts/cw-hub-bnusd/src/error.rs index 7bb6eac..87498e0 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/error.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/error.rs @@ -7,7 +7,7 @@ pub enum ContractError { Std(#[from] StdError), #[error("Unauthorized")] - Unauthorized {}, + Unauthorized, #[error("Wrong Address")] WrongAddress, #[error("Invalid Network Address according to Network ID")] @@ -34,6 +34,8 @@ pub enum ContractError { AddressNotFound, #[error("{0}")] Cw20BaseError(#[from] cw20_base::ContractError), + #[error("Cannot Send to Self")] + CannotSendToSelf, // Add any other custom errors you like here. // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. } diff --git a/contracts/token-contracts/cw-hub-bnusd/src/events.rs b/contracts/token-contracts/cw-hub-bnusd/src/events.rs new file mode 100644 index 0000000..08c6d5c --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/src/events.rs @@ -0,0 +1,35 @@ +use cosmwasm_std::{Addr, Event}; +use cw_common::network_address::NetworkAddress; +use debug_print::debug_println; + +pub fn emit_cross_transfer_event( + name: String, + from: NetworkAddress, + to: NetworkAddress, + amount: u128, + data: Vec, +) -> Event { + Event::new(name) + .add_attribute("from", from.to_string()) + .add_attribute("to", to.to_string()) + .add_attribute("value", amount.to_string()) + .add_attribute("data", hex_encode(data)) +} + +pub fn emit_cross_transfer_revert_event(name: String, from: Addr, amount: u128) -> Event { + Event::new(name) + .add_attribute("from", from.to_string()) + .add_attribute("value", amount.to_string()) +} + +fn hex_encode(data: Vec) -> String { + debug_println!("this is {:?}", data); + if data.is_empty() { + debug_println!("this is empty"); + "null".to_string() + } else { + let data = hex::encode(data); + debug_println!("this is not empty, {}", data); + data + } +} diff --git a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs index 39e6d15..2243acd 100644 --- a/contracts/token-contracts/cw-hub-bnusd/src/lib.rs +++ b/contracts/token-contracts/cw-hub-bnusd/src/lib.rs @@ -1,6 +1,7 @@ pub mod constants; pub mod contract; mod error; +pub mod events; pub mod helpers; pub mod state; pub use crate::error::ContractError; diff --git a/contracts/token-contracts/cw-hub-bnusd/tests/cross_transfer_test.rs b/contracts/token-contracts/cw-hub-bnusd/tests/cross_transfer_test.rs new file mode 100644 index 0000000..8eb5e0f --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/tests/cross_transfer_test.rs @@ -0,0 +1,43 @@ +mod setup; +use std::str::FromStr; + +use cosmwasm_std::Uint128; +use cw_common::hub_token_msg::ExecuteMsg; +use cw_common::network_address::NetworkAddress; +use cw_multi_test::Executor; + +use crate::setup::{call_set_xcall_host, execute_setup, instantiate_contracts}; +use setup::{mint_token, set_default_connection, setup_context, TestContext}; + +pub fn cross_transfer(mut ctx: TestContext) -> TestContext { + let x_call_connection = ctx.get_xcall_connection(); + ctx = set_default_connection(ctx, x_call_connection); + call_set_xcall_host(&mut ctx); + let _resp = ctx + .app + .execute_contract( + ctx.sender.clone(), + ctx.get_hubtoken_app(), + &ExecuteMsg::CrossTransfer { + to: NetworkAddress::from_str("icon/cx9876543210fedcba9876543210fedcba98765432") + .unwrap(), + amount: 100, + data: vec![], + }, + &[], + ) + .unwrap(); + println!("{:?}", _resp); + + ctx +} + +#[test] +pub fn cross_transfer_test() { + let mut context: TestContext = setup_context(); + context = instantiate_contracts(context); + context = execute_setup(context); + let sender = context.sender.to_string(); + context = mint_token(context, sender, Uint128::from(u128::MIN + 1000)); + cross_transfer(context); +} diff --git a/contracts/token-contracts/cw-hub-bnusd/tests/cw20_flow_test.rs b/contracts/token-contracts/cw-hub-bnusd/tests/cw20_flow_test.rs new file mode 100644 index 0000000..21fa19d --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/tests/cw20_flow_test.rs @@ -0,0 +1,585 @@ +use cosmwasm_std::{Addr, Uint128}; +use cw20::{ + AllAccountsResponse, AllAllowancesResponse, AllSpenderAllowancesResponse, AllowanceResponse, + BalanceResponse, MarketingInfoResponse, MinterResponse, +}; +use cw20_base::state::TokenInfo; +use cw_multi_test::Executor; +use setup::{execute_setup, instantiate_contracts, setup_context, TestContext}; + +mod setup; + +#[test] +fn cw20_flow_test() { + let mut context: TestContext = setup_context(); + context = instantiate_contracts(context); + context = execute_setup(context); + + let alice = Addr::unchecked("alice".to_owned()); + let bob = Addr::unchecked("bob".to_owned()); + let carol = Addr::unchecked("carol".to_owned()); + let amount: u128 = 1000; + + //mint 1000 tokens to each account, and minting access is given to only xcall app + let resp = context.app.execute_contract( + context.get_hubtoken_app(), + context.get_xcall_app(), + &cw_common::hub_token_msg::ExecuteMsg::Mint { + recipient: alice.to_string(), + amount: Uint128::from(amount), + }, + &[], + ); + + assert!(resp.is_err()); //cannot mint tokens from hubtoken app + + //use loop to mint tokens from xcall app to alice, bob and carol + vec![alice.to_string(), bob.to_string(), carol.to_string()] + .iter() + .for_each(|recipient| { + let _resp = context + .app + .execute_contract( + context.get_xcall_app(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::Mint { + recipient: recipient.clone(), + amount: Uint128::from(amount), + }, + &[], + ) + .unwrap(); + }); + + //check balance of each account, and assert this to be 1000 + vec![ + (alice.to_string(), amount), + (bob.to_string(), amount), + (carol.to_string(), amount), + ] + .iter() + .for_each(|(account, balance)| { + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: account.to_string(), + }, + ) + .unwrap(); + println!("balance: {:?}", balance_response.balance.u128()); + assert_eq!(balance_response.balance.u128(), *balance); + }); + + let mut balances = [amount, amount, amount]; + + //transfer 100 tokens from alice to bob and check again balance + let transfer_amount: u128 = 100; + let _resp = context + .app + .execute_contract( + alice.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::Transfer { + recipient: bob.to_string(), + amount: Uint128::from(transfer_amount), + }, + &[], + ) + .unwrap(); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: alice.to_string(), + }, + ) + .unwrap(); + assert_eq!( + balance_response.balance.u128(), + balances[0] - transfer_amount + ); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: bob.to_string(), + }, + ) + .unwrap(); + assert_eq!( + balance_response.balance.u128(), + balances[1] + transfer_amount + ); + + balances = [ + balances[0] - transfer_amount, + balances[1] + transfer_amount, + balances[2], + ]; + + //transfer 100 tokens from bob to carol and check again balance + let _resp = context + .app + .execute_contract( + bob.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::Transfer { + recipient: carol.to_string(), + amount: Uint128::from(transfer_amount), + }, + &[], + ) + .unwrap(); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: bob.to_string(), + }, + ) + .unwrap(); + assert_eq!( + balance_response.balance.u128(), + balances[1] - transfer_amount + ); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: carol.to_string(), + }, + ) + .unwrap(); + assert_eq!( + balance_response.balance.u128(), + balances[2] + transfer_amount + ); + + balances = [ + balances[0], + balances[1] - transfer_amount, + balances[2] + transfer_amount, + ]; + + //check self transfer, and the after the transfer amount should not increase + let _resp = context + .app + .execute_contract( + alice.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::Transfer { + recipient: alice.to_string(), + amount: Uint128::from(transfer_amount), + }, + &[], + ) + .unwrap(); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: alice.to_string(), + }, + ) + .unwrap(); + assert_eq!(balance_response.balance.u128(), balances[0]); + + let allowances_amount: u128 = 100; + + //set allowance of 100 tokens from alice to bob and and transfer 50 tokens of alice from bob to carol + let _resp = context + .app + .execute_contract( + alice.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::IncreaseAllowance { + spender: bob.to_string(), + amount: Uint128::from(allowances_amount), + expires: None, + }, + &[], + ) + .unwrap(); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: alice.to_string(), + }, + ) + .unwrap(); + assert_eq!(balance_response.balance.u128(), balances[0]); + + let allowance_response: AllowanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Allowance { + owner: alice.to_string(), + spender: bob.to_string(), + }, + ) + .unwrap(); + assert_eq!(allowance_response.allowance.u128(), allowances_amount); + + let _resp = context + .app + .execute_contract( + bob.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::TransferFrom { + owner: alice.to_string(), + recipient: carol.to_string(), + amount: Uint128::from(transfer_amount / 2), + }, + &[], + ) + .unwrap(); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: alice.to_string(), + }, + ) + .unwrap(); + assert_eq!( + balance_response.balance.u128(), + balances[0] - transfer_amount / 2 + ); + + balances = [ + balances[0] - transfer_amount / 2, + balances[1], + balances[2] + transfer_amount / 2, + ]; + + //get allowance of alice to bob and assert it to be 50 + let allowance_response: AllowanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Allowance { + owner: alice.to_string(), + spender: bob.to_string(), + }, + ) + .unwrap(); + assert_eq!( + allowance_response.allowance.u128(), + allowances_amount - transfer_amount / 2 + ); + + //increase, decrease and check allowance + + let _resp = context + .app + .execute_contract( + alice.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::IncreaseAllowance { + spender: bob.to_string(), + amount: Uint128::from(transfer_amount), + expires: None, + }, + &[], + ) + .unwrap(); + + let allowance_response: AllowanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Allowance { + owner: alice.to_string(), + spender: bob.to_string(), + }, + ) + .unwrap(); + assert_eq!( + allowance_response.allowance.u128(), + transfer_amount + transfer_amount / 2 + ); + + let _resp = context + .app + .execute_contract( + alice.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::DecreaseAllowance { + spender: bob.to_string(), + amount: Uint128::from(transfer_amount), + expires: None, + }, + &[], + ) + .unwrap(); + + let allowance_response: AllowanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Allowance { + owner: alice.to_string(), + spender: bob.to_string(), + }, + ) + .unwrap(); + assert_eq!(allowance_response.allowance.u128(), transfer_amount / 2); + + //burn 100 tokens from alice and check balance + let _resp = context + .app + .execute_contract( + alice.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::Burn { + amount: Uint128::from(transfer_amount), + }, + &[], + ) + .unwrap(); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: alice.to_string(), + }, + ) + .unwrap(); + assert_eq!( + balance_response.balance.u128(), + balances[0] - transfer_amount + ); + + balances = [balances[0] - transfer_amount, balances[1], balances[2]]; + + println!("balances {:?}", balances); + //burn_from test and check balance + + let _resp = context + .app + .execute_contract( + bob.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::BurnFrom { + owner: alice.to_string(), + amount: Uint128::from(transfer_amount / 2), + }, + &[], + ) + .unwrap(); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: alice.to_string(), + }, + ) + .unwrap(); + assert_eq!( + balance_response.balance.u128(), + balances[0] - transfer_amount / 2 + ); + + balances = [balances[0] - transfer_amount / 2, balances[1], balances[2]]; + + //check allowance of bob to be 0 + let allowance_response: AllowanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Allowance { + owner: alice.to_string(), + spender: bob.to_string(), + }, + ) + .unwrap(); + assert_eq!(allowance_response.allowance.u128(), 0); + + //update minter and check xcall app cannot mint tokens + + let _resp = context + .app + .execute_contract( + context.get_xcall_app(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::UpdateMinter { + new_minter: Some(bob.to_string()), + }, + &[], + ) + .unwrap(); + + let resp: MinterResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Minter {}, + ) + .unwrap(); + println!("resp {:?}", resp); + assert_eq!(resp.minter, bob.to_string()); + + //try to mint by xcall which should fail + + let resp = context.app.execute_contract( + context.get_xcall_app(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::Mint { + recipient: alice.to_string(), + amount: Uint128::from(amount), + }, + &[], + ); + assert!(resp.is_err()); + + //try to mint by bob which should pass and check balance of alice + let _resp = context + .app + .execute_contract( + bob.clone(), + context.get_hubtoken_app(), + &cw_common::hub_token_msg::ExecuteMsg::Mint { + recipient: alice.to_string(), + amount: Uint128::from(amount), + }, + &[], + ) + .unwrap(); + + let balance_response: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::Balance { + address: alice.to_string(), + }, + ) + .unwrap(); + + assert_eq!(balance_response.balance.u128(), balances[0] + amount); + balances = [balances[0] + amount, balances[1], balances[2]]; + //all query tests + + let token_info: TokenInfo = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::TokenInfo {}, + ) + .unwrap(); + + println!("token_info {:?}", token_info); + let expected_info = TokenInfo { + name: "Balanced Dollar".to_owned(), + symbol: "bnUSD".to_owned(), + decimals: 18, + total_supply: Uint128::from(balances[0] + balances[1] + balances[2]), + mint: None, + }; + + assert_eq!(token_info, expected_info); + + //query all allowances and all spender allowances + + let all_allowances_response: AllAllowancesResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::AllAllowances { + owner: alice.to_string(), + start_after: None, + limit: None, + }, + ) + .unwrap(); + + println!("all_allowances_response {:?}", all_allowances_response); + + //all spenderallowancesz + + let all_spender_allowances_response: AllSpenderAllowancesResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::AllSpenderAllowances { + spender: bob.to_string(), + start_after: None, + limit: None, + }, + ) + .unwrap(); + + println!( + "all_spender_allowances_response {:?}", + all_spender_allowances_response + ); + + //query all accounts + + let all_accounts: AllAccountsResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::AllAccounts { + start_after: None, + limit: None, + }, + ) + .unwrap(); + + println!("all_accounts {:?}", all_accounts); + + //marketing info and download logo + let marketing_info: MarketingInfoResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &cw_common::hub_token_msg::QueryMsg::MarketingInfo {}, + ) + .unwrap(); + println!("marketing_info {:?}", marketing_info); +} diff --git a/contracts/token-contracts/cw-hub-bnusd/tests/handle_call_message_test.rs b/contracts/token-contracts/cw-hub-bnusd/tests/handle_call_message_test.rs new file mode 100644 index 0000000..92cbf5d --- /dev/null +++ b/contracts/token-contracts/cw-hub-bnusd/tests/handle_call_message_test.rs @@ -0,0 +1,217 @@ +mod setup; +use std::str::FromStr; + +use cw_common::{data_types::CrossTransferRevert, x_call_msg::XCallMsg as XCallExecuteMsg}; +use cw_multi_test::Executor; + +use cosmwasm_std::{Addr, Uint128}; +use cw_common::{data_types::CrossTransfer, network_address::NetworkAddress}; + +use crate::setup::{execute_setup, instantiate_contracts}; +use cw20::BalanceResponse; +use cw20_base::msg::QueryMsg; +use cw_common::network_address::NetId; +use rlp::{encode, RlpStream}; +use setup::{get_event, set_default_connection, setup_context, TestContext}; + +fn execute_and_handle_message(mut context: TestContext) -> TestContext { + let hub_token_addr = context.get_hubtoken_app().into_string(); + let call_data = CrossTransfer { + method: "xCrossTransfer".to_string(), + from: NetworkAddress::from_str("icon/cx7866543210fedcba9876543210fedcba987654df").unwrap(), + to: NetworkAddress::from_str("icon/cx9876543210fedcba9876543210fedcba98765432").unwrap(), + value: 1000, + data: vec![ + 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, + ], + }; + + let data = encode(&call_data).to_vec(); + + let network_address = + NetworkAddress::from_str("icon/cx7866543210fedcba9876543210fedcba987654df").unwrap(); + let sequence_no: u64 = 1234; + let message_type: u64 = 1; + + let mut stream = RlpStream::new(); + stream.begin_list(6); + stream.append(&network_address.to_string()); + stream.append(&hub_token_addr); + stream.append(&sequence_no); + stream.append(&false); + stream.append(&data); + stream.begin_list(0); + + let encoded_data: Vec = stream.out().to_vec(); + println!("Encoded Data {:?}", encoded_data); + + let mut stream = RlpStream::new(); + stream.begin_list(2); + stream.append(&message_type); + stream.append(&encoded_data); + + let data = stream.out().to_vec(); + + let relay = Addr::unchecked("relay"); + context = set_default_connection(context, relay.clone()); + + let response = context + .app + .execute_contract( + relay, + context.get_xcall_app(), + &XCallExecuteMsg::HandleMessage { + from: NetId::from("icon".to_owned()), + msg: data, + }, + &[], + ) + .unwrap(); + + let event = get_event(&response, "wasm-CallMessage").unwrap(); + let request_id = event.get("reqId").unwrap(); + println!("Request ID {:?}", request_id); + + let call_data = CrossTransfer { + method: "xCrossTransfer".to_string(), + from: NetworkAddress::from_str("icon/cx7866543210fedcba9876543210fedcba987654df").unwrap(), + to: NetworkAddress::from_str("icon/cx9876543210fedcba9876543210fedcba98765432").unwrap(), + value: 1000, + data: vec![ + 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, + ], + }; + + let data = encode(&call_data).to_vec(); + + let response = context + .app + .execute_contract( + context.get_hubtoken_app(), + context.get_xcall_app(), + &XCallExecuteMsg::ExecuteCall { + request_id: request_id.parse::().unwrap(), + data, + }, + &[], + ) + .unwrap(); + + let balance: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &QueryMsg::Balance { + address: call_data.to.account().to_string(), + }, + ) + .unwrap(); + let expected_balance = Uint128::from(u128::MIN + 1000); + assert_eq!(balance.balance, expected_balance); + + println!("Response {:?}", response); + + context +} +/// HandleCallMessage is called by XCALL contract. +/// +/// For testing purpose, we called HandleMessage of XCall from sender 'relay', which returns a request ID in +/// the response. This request ID is then used to ExecuteCall message, which in turn calls HandleCallMessage +/// of hubToken contract. + +#[test] +fn handle_call_message_test() { + let mut context: TestContext = setup_context(); + context = instantiate_contracts(context); + context = execute_setup(context); + execute_and_handle_message(context); +} + +#[test] +pub fn cross_transfer_revert_data_test() { + let mut context: TestContext = setup_context(); + context = instantiate_contracts(context); + context = execute_setup(context); + let hub_token_addr = context.get_hubtoken_app().into_string(); + + let call_data = CrossTransferRevert { + method: "xCrossTransferRevert".to_string(), + from: Addr::unchecked("cx7866543210fedcba9876543210fedcba987654df".to_owned()), + value: 1000, + }; + + let data = encode(&call_data).to_vec(); + + let network_address = + NetworkAddress::from_str("icon/cx7866543210fedcba9876543210fedcba987654df").unwrap(); + let sequence_no: u64 = 1234; + let message_type: u64 = 1; + + let mut stream = RlpStream::new(); + stream.begin_list(6); + stream.append(&network_address.to_string()); + stream.append(&hub_token_addr); + stream.append(&sequence_no); + stream.append(&false); + stream.append(&data); + stream.begin_list(0); + + let encoded_data: Vec = stream.out().to_vec(); + println!("Encoded Data {:?}", encoded_data); + + let mut stream = RlpStream::new(); + stream.begin_list(2); + stream.append(&message_type); + stream.append(&encoded_data); + + let data = stream.out().to_vec(); + + let relay = Addr::unchecked("relay"); + context = set_default_connection(context, relay.clone()); + + let response = context.app.execute_contract( + relay, + context.get_xcall_app(), + &XCallExecuteMsg::HandleMessage { + from: NetId::from("icon".to_owned()), + msg: data, + }, + &[], + ); + + println!("Response {:?}", response); + let event = get_event(&response.unwrap(), "wasm-CallMessage").unwrap(); + let request_id = event.get("reqId").unwrap(); + println!("Request ID {:?}", request_id); + + let data = encode(&call_data).to_vec(); + + let response = context + .app + .execute_contract( + context.get_hubtoken_app(), + context.get_xcall_app(), + &XCallExecuteMsg::ExecuteCall { + request_id: request_id.parse::().unwrap(), + data, + }, + &[], + ) + .unwrap(); + + let balance: BalanceResponse = context + .app + .wrap() + .query_wasm_smart( + context.get_hubtoken_app(), + &QueryMsg::Balance { + address: call_data.from.to_string(), + }, + ) + .unwrap(); + let expected_balance = Uint128::from(u128::MIN + 1000); + assert_eq!(balance.balance, expected_balance); + + println!("Response {:?}", response); +} diff --git a/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs index b12e127..eef9216 100644 --- a/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs +++ b/contracts/token-contracts/cw-hub-bnusd/tests/setup.rs @@ -1,13 +1,36 @@ use std::collections::HashMap; +use std::str::FromStr; -use cosmwasm_std::Addr; +use cw_common::x_call_msg::XCallMsg as XCallExecuteMsg; +use cw_hub_bnusd::contract::{execute, instantiate, query, reply}; use cw_multi_test::App; +use cw_multi_test::{Contract, ContractWrapper, Executor}; +use cw_xcall_ibc_connection::{ + execute as execute_conn, instantiate as instantiate_conn, query as query_conn, + reply as reply_conn, +}; +use cw_xcall_multi::msg::InstantiateMsg as XCallInstantiateMsg; +use cw_xcall_multi::{ + execute as execute_xcall, instantiate as instantiate_xcall, query as query_xcall, + reply as reply_xcall, +}; + +use hub_token_msg::InstantiateMsg; + +use cosmwasm_std::{Addr, Empty}; +use cw_common::{ + hub_token_msg::{self, ExecuteMsg}, + network_address::NetworkAddress, +}; #[derive(Debug, PartialEq, Eq, Hash)] pub enum TestApps { XCall, HubToken, + XcallConnection, + IbcCore, } + pub struct TestContext { pub app: App, pub contracts: HashMap, @@ -25,10 +48,28 @@ impl TestContext { self.contracts.insert(TestApps::HubToken, addr) } + pub fn set_xcall_connection(&mut self, addr: Addr) -> Option { + self.contracts.insert(TestApps::XcallConnection, addr) + } + pub fn set_ibc_core(&mut self, addr: Addr) -> Option { + self.contracts.insert(TestApps::IbcCore, addr) + } + pub fn get_ibc_core(&self) -> Addr { + return self.contracts.get(&TestApps::IbcCore).unwrap().clone(); + } + pub fn get_xcall_app(&self) -> Addr { return self.contracts.get(&TestApps::XCall).unwrap().clone(); } + pub fn get_xcall_connection(&self) -> Addr { + return self + .contracts + .get(&TestApps::XcallConnection) + .unwrap() + .clone(); + } + pub fn get_hubtoken_app(&self) -> Addr { return self.contracts.get(&TestApps::HubToken).unwrap().clone(); } @@ -46,178 +87,209 @@ pub fn setup_context() -> TestContext { } } -mod instantiate_test { - use cosmwasm_std::{Addr, Empty}; - use cw_common::{ - data_types::{CrossTransfer, CrossTransferRevert}, - hub_token_msg::{self, ExecuteMsg}, - network_address::NetworkAddress, - x_call_msg::{self, XCallMsg}, - }; - use cw_multi_test::{Contract, ContractWrapper, Executor}; - use rlp::encode; - use x_call_mock::contract::{execute, instantiate, query}; - - use super::*; - - fn init_x_call(mut ctx: TestContext) -> TestContext { - let code: Box> = Box::new( - ContractWrapper::new(execute, instantiate, query) - .with_reply(x_call_mock::contract::reply), - ); - let code_id = ctx.app.store_code(code); - - let _addr = ctx - .app - .instantiate_contract( - code_id, - ctx.sender.clone(), - &x_call_msg::InstantiateMsg {}, - &[], - "XCall", - None, - ) - .unwrap(); - ctx.set_xcall_app(_addr); - ctx - } +pub fn x_call_contract_setup() -> Box> { + Box::new( + ContractWrapper::new(execute_xcall, instantiate_xcall, query_xcall).with_reply(reply_xcall), + ) +} +pub fn ibc_mock_core_setup() -> Box> { + Box::new( + ContractWrapper::new( + cw_mock_ibc_core::contract::execute, + cw_mock_ibc_core::contract::instantiate, + cw_mock_ibc_core::contract::query, + ) + .with_reply(cw_mock_ibc_core::contract::reply), + ) +} +pub fn hub_token_contract_setup() -> Box> { + Box::new(ContractWrapper::new(execute, instantiate, query).with_reply(reply)) +} - fn init_token(mut ctx: TestContext, _x_call_address: String) -> TestContext { - use cw_hub_bnusd::contract::{execute, instantiate, query, reply}; - use hub_token_msg::InstantiateMsg; - - let code: Box> = - Box::new(ContractWrapper::new(execute, instantiate, query).with_reply(reply)); - let code_id = ctx.app.store_code(code); - - let _addr = ctx - .app - .instantiate_contract( - code_id, - ctx.sender.clone(), - &InstantiateMsg { - x_call: Addr::unchecked(_x_call_address).into_string(), - hub_address: "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), - }, - &[], - "HubToken", - None, - ) - .unwrap(); - ctx.set_hubtoken_app(_addr); - ctx - } +pub fn x_call_connection_setup() -> Box> { + Box::new( + ContractWrapper::new(execute_conn, instantiate_conn, query_conn).with_reply(reply_conn), + ) +} - fn execute_setup(mut ctx: TestContext) -> TestContext { - let _resp = ctx - .app - .execute_contract( - ctx.sender.clone(), - ctx.get_hubtoken_app(), - &ExecuteMsg::Setup { - x_call: Addr::unchecked(ctx.get_xcall_app()), - hub_address: NetworkAddress( - "0x01.icon/cx7866543210fedcba9876543210fedcba987654df".to_owned(), - ), - }, - &[], - ) - .unwrap(); - - ctx - } +use cosmwasm_std::{Attribute, Event, Uint128}; - fn handle_call_message(mut ctx: TestContext) -> TestContext { - let call_data = CrossTransfer { - method: "xCrossTransfer".to_string(), - from: NetworkAddress("0x01.icon/cx7866543210fedcba9876543210fedcba987654df".to_owned()), - to: NetworkAddress("0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string()), - value: 1000, - data: vec![ - 118, 101, 99, 33, 91, 49, 44, 32, 50, 44, 32, 51, 44, 32, 52, 44, 32, 53, 93, - ], - }; - - // let mut stream = RlpStream::new(); - let data = encode(&call_data).to_vec(); - - let _resp = ctx.app.execute_contract( +use cw_common::network_address::NetId; +use cw_multi_test::AppResponse; + +pub fn init_x_call(mut ctx: TestContext) -> TestContext { + let code: Box> = x_call_contract_setup(); + let code_id = ctx.app.store_code(code); + + let _addr = ctx + .app + .instantiate_contract( + code_id, ctx.sender.clone(), - ctx.get_xcall_app(), - &XCallMsg::TestHandleCallMessage { - from: "0x01.icon/cx7866543210fedcba9876543210fedcba987654df".to_owned(), - data, - hub_token: ctx.get_hubtoken_app().into_string(), + &XCallInstantiateMsg { + network_id: "icon".to_string(), + denom: "xcalToken".to_string(), }, &[], - ); - println!("Respose Please{:?}", _resp); - assert!(_resp.is_ok()); + "XCall", + None, + ) + .unwrap(); + ctx.set_xcall_app(_addr); + ctx +} - let call_data = CrossTransferRevert { - method: "xCrossTransferRevert".to_string(), - from: ctx.sender.clone(), - value: 1000, - }; +pub fn init_mock_ibc_core(mut ctx: TestContext) -> TestContext { + let code: Box> = ibc_mock_core_setup(); + let code_id = ctx.app.store_code(code); - // let mut stream = RlpStream::new(); - let data = encode(&call_data).to_vec(); + let _addr = ctx + .app + .instantiate_contract( + code_id, + ctx.sender.clone(), + &cw_mock_ibc_core::msg::InstantiateMsg {}, + &[], + "IbcCore", + None, + ) + .unwrap(); + ctx.set_ibc_core(_addr); + ctx +} - let _resp = ctx.app.execute_contract( +pub fn init_xcall_connection_contract(mut ctx: TestContext) -> TestContext { + let connection_contract_code_id = ctx.app.store_code(x_call_connection_setup()); + let connection_contract_addr = ctx + .app + .instantiate_contract( + connection_contract_code_id, ctx.sender.clone(), - ctx.get_xcall_app(), - &XCallMsg::TestHandleCallMessage { - from: "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), - data, - hub_token: ctx.get_hubtoken_app().into_string(), + &cw_xcall_ibc_connection::msg::InstantiateMsg { + ibc_host: ctx.get_ibc_core(), + denom: "uarch".to_string(), + port_id: "mock".to_string(), + xcall_address: ctx.get_xcall_app(), }, &[], - ); - println!("{:?}", _resp); - assert!(_resp.is_ok()); + "IBCConnection", + Some(ctx.sender.clone().to_string()), + ) + .unwrap(); + ctx.set_xcall_connection(connection_contract_addr); + ctx +} - ctx - } +pub fn init_token(mut ctx: TestContext, _x_call_address: String) -> TestContext { + let code: Box> = hub_token_contract_setup(); + let code_id = ctx.app.store_code(code); - fn cross_transfer(mut ctx: TestContext) -> TestContext { - let _resp = ctx - .app - .execute_contract( - ctx.sender.clone(), - ctx.get_hubtoken_app(), - &ExecuteMsg::CrossTransfer { - to: NetworkAddress( - "0x01.icon/cx9876543210fedcba9876543210fedcba98765432".to_string(), - ), - amount: 100, - data: vec![], - }, - &[], - ) - .unwrap(); - println!("{:?}", _resp); - - ctx - } + let _addr = ctx + .app + .instantiate_contract( + code_id, + ctx.sender.clone(), + &InstantiateMsg { + x_call: Addr::unchecked(_x_call_address).into_string(), + hub_address: "icon/cx9876543210fedcba9876543210fedcba98765432".to_owned(), + }, + &[], + "HubToken", + None, + ) + .unwrap(); + ctx.set_hubtoken_app(_addr); + ctx +} - fn setup_contracts(mut ctx: TestContext) -> TestContext { - ctx = init_x_call(ctx); - let x_call_address = ctx.get_xcall_app().into_string(); - ctx = init_token(ctx, x_call_address); - ctx - } +pub fn call_set_xcall_host(ctx: &mut TestContext) -> AppResponse { + ctx.app + .execute_contract( + ctx.sender.clone(), + ctx.get_xcall_connection(), + &cw_common_ibc::xcall_connection_msg::ExecuteMsg::SetXCallHost { + address: ctx.get_xcall_app().to_string(), + }, + &[], + ) + .unwrap() +} + +pub fn execute_setup(mut ctx: TestContext) -> TestContext { + let _resp = ctx + .app + .execute_contract( + ctx.sender.clone(), + ctx.get_hubtoken_app(), + &ExecuteMsg::Setup { + x_call: Addr::unchecked(ctx.get_xcall_app()), + hub_address: NetworkAddress::from_str( + "icon/cx7866543210fedcba9876543210fedcba987654df", + ) + .unwrap(), + }, + &[], + ) + .unwrap(); - fn setup_test() -> TestContext { - let mut context: TestContext = setup_context(); - context = setup_contracts(context); - context = execute_setup(context); - context = handle_call_message(context); - context = cross_transfer(context); - context + ctx +} + +pub fn instantiate_contracts(mut ctx: TestContext) -> TestContext { + ctx = init_x_call(ctx); + let x_call_address = ctx.get_xcall_app().into_string(); + ctx = init_token(ctx, x_call_address); + ctx = init_mock_ibc_core(ctx); + ctx = init_xcall_connection_contract(ctx); + ctx +} + +pub fn to_attribute_map(attrs: &Vec) -> HashMap { + let mut map = HashMap::new(); + for attr in attrs { + map.insert(attr.key.clone(), attr.value.clone()); } + map +} - #[test] - fn contract_test() { - setup_test(); +pub fn get_event(res: &AppResponse, event: &str) -> Option> { + let event = res + .events + .iter() + .filter(|e| e.ty == event) + .collect::>(); + if !event.is_empty() { + let map = to_attribute_map(&event[0].attributes); + return Some(map); } + None +} + +pub fn mint_token(mut context: TestContext, recipient: String, amount: Uint128) -> TestContext { + let _response = context + .app + .execute_contract( + context.get_xcall_app(), + context.get_hubtoken_app(), + &ExecuteMsg::Mint { recipient, amount }, + &[], + ) + .unwrap(); + context +} + +pub fn set_default_connection(mut context: TestContext, address: Addr) -> TestContext { + let _response = context + .app + .execute_contract( + context.sender.clone(), + context.get_xcall_app(), + &XCallExecuteMsg::SetDefaultConnection { + nid: NetId::from("icon".to_owned()), + address, + }, + &[], + ) + .unwrap(); + context } From c3a50b861781e54a296a2620e3da1b212768f6dd Mon Sep 17 00:00:00 2001 From: Night Owl Date: Tue, 11 Jul 2023 12:58:20 +0545 Subject: [PATCH 36/37] chore: remove completed TODOs Signed-off-by: Night Owl --- contracts/cw-common/src/hub_token_msg.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/cw-common/src/hub_token_msg.rs b/contracts/cw-common/src/hub_token_msg.rs index 7151be8..b31edf3 100644 --- a/contracts/cw-common/src/hub_token_msg.rs +++ b/contracts/cw-common/src/hub_token_msg.rs @@ -14,7 +14,6 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { Setup { - //TODO: x_call should be of addr type x_call: Addr, hub_address: NetworkAddress, }, From 3ddf01c9279a0172913274ff5445ace0d27dfa85 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Fri, 8 Sep 2023 16:26:58 +0545 Subject: [PATCH 37/37] fix: add --locked option for installation of cw check Signed-off-by: Night Owl --- .github/workflows/deploy-cw-contracts.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-cw-contracts.yml b/.github/workflows/deploy-cw-contracts.yml index b3c8c70..6417cc4 100644 --- a/.github/workflows/deploy-cw-contracts.yml +++ b/.github/workflows/deploy-cw-contracts.yml @@ -41,7 +41,7 @@ jobs: - name: Install cw-check run: | - cargo install cosmwasm-check + cargo install cosmwasm-check --locked - name: Compile WASM run: bash ./scripts/generate_wasm.sh