Skip to content

Commit

Permalink
add migration path from terraport-token
Browse files Browse the repository at this point in the history
  • Loading branch information
fragwuerdig committed Apr 15, 2024
1 parent 6ac54c5 commit a3ed695
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 10 deletions.
33 changes: 24 additions & 9 deletions contracts/cw20-taxed/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ use crate::enumerable::{query_all_accounts, query_owner_allowances, query_spende
use crate::error::ContractError;
use crate::msg::{Cw20TaxedExecuteMsg as ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
use crate::state::{
MinterData, TokenInfo, ALLOWANCES, ALLOWANCES_SPENDER, BALANCES, LOGO, MARKETING_INFO, TAX_INFO, TOKEN_INFO
self, MinterData, TokenInfo, ALLOWANCES, ALLOWANCES_SPENDER, BALANCES, LOGO, MARKETING_INFO, TAX_INFO, TOKEN_INFO
};

use crate::tax::{self, TaxMap};

// version info for migration info
const CONTRACT_NAME: &str = "crates.io:cw20-base";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const CONTRACT_NAME: &str = "crates.io:cw20-base";
pub const CONTRACT_NAME_TERRAPORT: &str = "crates.io:terraport-token";
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

const LOGO_SIZE_CAP: usize = 5 * 1024;

Expand Down Expand Up @@ -722,6 +723,12 @@ pub fn query_download_logo(deps: Deps) -> StdResult<DownloadLogoResponse> {

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {

// merge upgrade paths
// crates.io:terraport-token 0.0.0 -> crates.io:cw20-base 1.1.0
// crates.io:cw20-base 1.1.0 -> crates.io:cw20-base 1.1.0
state::migrate_v1::ensure_known_upgrade_path(deps.storage)?;

let original_version =
ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

Expand All @@ -736,12 +743,20 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result<Response, Co
}

if original_version < "1.1.0+taxed001".parse::<semver::Version>().unwrap() {
// Add tax map
let tax_map = match msg.tax_map {
Some(x) => x,
None => TaxMap::default(),
};
TAX_INFO.save(deps.storage, &tax_map)?;
match TAX_INFO.load(deps.storage) {
// there seems to be an existing tax map, so we don't need to do anything
Ok(_) => {},

// no tax map, so we need to add one
Err(_) => {
// Add tax map
let tax_map = match msg.tax_map {
Some(x) => x,
None => TaxMap::default(),
};
TAX_INFO.save(deps.storage, &tax_map)?;
}
}
}
Ok(Response::default())
}
Expand Down
104 changes: 103 additions & 1 deletion contracts/cw20-taxed/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Decimal, Uint128};
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
use cw_storage_plus::{Item, Map};

use cw20::{AllowanceResponse, Logo, MarketingInfoResponse};
Expand Down Expand Up @@ -39,3 +39,105 @@ pub const ALLOWANCES_SPENDER: Map<(&Addr, &Addr), AllowanceResponse> =

// specific for TAXED token
pub const TAX_INFO: Item<TaxMap> = Item::new("tax_info");

// specific only for migration from Terraport Tokens
pub mod migrate_v1 {
use std::{backtrace::Backtrace, str::FromStr};

use cosmwasm_std::{to_json_binary, Addr, CosmosMsg, Order, StdError, StdResult, Storage, Uint128};
use cw2::{get_contract_version, set_contract_version};
use cw_storage_plus::{Map, SnapshotMap, Strategy};
use semver::Version;

use crate::contract::{CONTRACT_NAME, CONTRACT_NAME_TERRAPORT};

pub const BALANCES: SnapshotMap<&Addr, Uint128> = SnapshotMap::new(
"balance",
"balance__checkpoints",
"balance__changelog",
Strategy::EveryBlock,
);

pub const TOTAL_SUPPLY_HISTORY: Map<u64, Uint128> = Map::new("total_supply_history");

pub fn is_terraport_token_v0(store: &dyn Storage) -> StdResult<bool> {
let version = get_contract_version(store)?;
Ok(version.contract == CONTRACT_NAME_TERRAPORT && version.version == "0.0.0")
}

pub fn is_cw20_taxed_v0(store: &dyn Storage) -> StdResult<bool> {
let version = get_contract_version(store)?;
let this_version = Version::from_str(
version.version.as_str(),
).map_err(|_| StdError::generic_err("no valid version in store"))?;
let expect_v0 = Version::from_str("1.1.0")
.map_err(|_| StdError::generic_err("could not parse version 1.0.0"))?;
Ok(version.contract == CONTRACT_NAME && expect_v0 <= this_version)
}

pub fn ensure_known_upgrade_path(store: &mut dyn Storage) -> StdResult<()> {

if is_terraport_token_v0(store)? {
set_contract_version(store, "crates.io:cw20-base", "1.1.0")?;
return Ok(());
} else if is_cw20_taxed_v0(store)? {
return Ok(());
} else {
return Err(StdError::generic_err("This is not a knowledable migration path"));
}
}

#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::MockStorage;
use cw2::set_contract_version;

// setup a terraport style balances store
fn setup(balances: Vec<(Addr, Uint128, u64)>) -> MockStorage {
let mut store = MockStorage::new();
set_contract_version(&mut store, "crates.io:terraport-token", "0.0.0").unwrap();
for (addr, balance, height) in balances {
BALANCES.save(&mut store, &addr, &balance, height).unwrap();
}
store
}

#[test]
fn test_is_terraport_token_v0() {
let mut store = MockStorage::new();

set_contract_version(&mut store, "crates.io:cw20-base", "1.0.6").unwrap();
assert_eq!(is_terraport_token_v0(&store).unwrap(), false);

set_contract_version(&mut store, "crates.io:cw20-base", "0.0.0").unwrap();
assert_eq!(is_terraport_token_v0(&store).unwrap(), false);

set_contract_version(&mut store, "crates.io:terraport-token", "0.0.0").unwrap();
assert_eq!(is_terraport_token_v0(&store).unwrap(), true);

set_contract_version(&mut store, "crates.io:terraport-token", "1.0.0").unwrap();
assert_eq!(is_terraport_token_v0(&store).unwrap(), false);
}

#[test]
fn test_terraport_snapshot_map_is_compatible_with_map() {
let mut store = setup(vec![
// initial balances
(Addr::unchecked("addr1"), Uint128::new(1234), 123),
(Addr::unchecked("addr2"), Uint128::new(1234), 123),
(Addr::unchecked("addr3"), Uint128::new(4455), 123),

// mock a transfer at later height
(Addr::unchecked("addr1"), Uint128::new(1233), 456),
(Addr::unchecked("addr2"), Uint128::new(1235), 456),
]);

// ensure the new data is compatible
assert_eq!(super::BALANCES.load(&store, &Addr::unchecked("addr1")).unwrap(), Uint128::new(1233));
assert_eq!(super::BALANCES.load(&store, &Addr::unchecked("addr2")).unwrap(), Uint128::new(1235));
assert_eq!(super::BALANCES.load(&store, &Addr::unchecked("addr3")).unwrap(), Uint128::new(4455));
}
}

}

0 comments on commit a3ed695

Please sign in to comment.