Skip to content

Commit b1cc5ad

Browse files
committed
feat: disallow re-using token names and symbols within a pool
1 parent 7e481bb commit b1cc5ad

File tree

3 files changed

+165
-7
lines changed

3 files changed

+165
-7
lines changed

pallets/pallet-bonded-coins/src/benchmarking.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,11 @@ mod benchmarks {
233233

234234
fn generate_token_metadata<T: Config>(c: u32) -> BoundedVec<TokenMetaOf<T>, T::MaxCurrenciesPerPool> {
235235
let mut token_meta = Vec::new();
236-
for _ in 1..=c {
236+
for i in 1..=c {
237237
token_meta.push(TokenMetaOf::<T> {
238238
min_balance: 1u128.saturated_into(),
239-
name: BoundedVec::try_from(b"BTC".to_vec()).expect("Failed to create BoundedVec"),
240-
symbol: BoundedVec::try_from(b"BTC".to_vec()).expect("Failed to create BoundedVec"),
239+
name: BoundedVec::try_from(format!("Coin_{}", &i).into_bytes()).expect("Failed to create BoundedVec"),
240+
symbol: BoundedVec::try_from(format!("BTC_{}", &i).into_bytes()).expect("Failed to create BoundedVec"),
241241
})
242242
}
243243
BoundedVec::try_from(token_meta).expect("creating bounded Vec should not fail")

pallets/pallet-bonded-coins/src/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub mod pallet {
7070
Hashable, Parameter,
7171
};
7272
use frame_system::pallet_prelude::*;
73+
use parity_scale_codec::alloc::collections::HashSet;
7374
use sp_arithmetic::ArithmeticError;
7475
use sp_core::U256;
7576
use sp_runtime::{
@@ -407,6 +408,9 @@ pub mod pallet {
407408
// currency to it. This should also verify that the currency actually exists.
408409
T::Collaterals::touch(collateral_id.clone(), pool_account, &who)?;
409410

411+
let mut names_seen = HashSet::<StringInputOf<T>>::with_capacity(currencies.len());
412+
let mut symbols_seen = HashSet::<StringInputOf<T>>::with_capacity(currencies.len());
413+
410414
currencies.into_iter().zip(currency_ids.iter()).try_for_each(
411415
|(token_metadata, asset_id)| -> DispatchResult {
412416
let TokenMeta {
@@ -415,6 +419,13 @@ pub mod pallet {
415419
symbol,
416420
} = token_metadata;
417421

422+
let name_ok = name.is_empty() || names_seen.insert(name.clone());
423+
let symbol_ok = symbol.is_empty() || symbols_seen.insert(symbol.clone());
424+
425+
if !name_ok || !symbol_ok {
426+
return Err(Error::<T>::InvalidInput.into());
427+
};
428+
418429
T::Fungibles::create(asset_id.clone(), pool_account.to_owned(), false, min_balance)?;
419430

420431
// set metadata for new asset class

pallets/pallet-bonded-coins/src/tests/transactions/create_pool.rs

+151-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use sp_std::ops::Sub;
2929
use crate::{
3030
mock::{runtime::*, *},
3131
types::{Locks, PoolStatus},
32-
AccountIdOf, Event as BondingPalletEvents, Pools, TokenMetaOf,
32+
AccountIdOf, Error, Event as BondingPalletEvents, Pools, TokenMetaOf,
3333
};
3434

3535
#[test]
@@ -104,6 +104,68 @@ fn single_currency() {
104104

105105
#[test]
106106
fn multi_currency() {
107+
let initial_balance = ONE_HUNDRED_KILT;
108+
ExtBuilder::default()
109+
.with_native_balances(vec![(ACCOUNT_00, initial_balance)])
110+
.with_collaterals(vec![DEFAULT_COLLATERAL_CURRENCY_ID])
111+
.build_and_execute_with_sanity_tests(|| {
112+
let origin = RawOrigin::Signed(ACCOUNT_00).into();
113+
let curve = get_linear_bonding_curve_input();
114+
115+
let bonded_tokens = bounded_vec![
116+
TokenMetaOf::<Test> {
117+
name: BoundedVec::truncate_from(b"Bitcoin".to_vec()),
118+
symbol: BoundedVec::truncate_from(b"BTC".to_vec()),
119+
min_balance: 1,
120+
},
121+
TokenMetaOf::<Test> {
122+
name: BoundedVec::truncate_from(b"Ether".to_vec()),
123+
symbol: BoundedVec::truncate_from(b"ETH".to_vec()),
124+
min_balance: 1,
125+
},
126+
TokenMetaOf::<Test> {
127+
name: BoundedVec::truncate_from(b"Dogecoin".to_vec()),
128+
symbol: BoundedVec::truncate_from(b"DOGE".to_vec()),
129+
min_balance: 1,
130+
}
131+
];
132+
133+
let next_asset_id = NextAssetId::<BondingPallet>::get();
134+
135+
assert_ok!(BondingPallet::create_pool(
136+
origin,
137+
curve,
138+
DEFAULT_COLLATERAL_CURRENCY_ID,
139+
bonded_tokens,
140+
DEFAULT_BONDED_DENOMINATION,
141+
true,
142+
1
143+
));
144+
145+
assert_eq!(NextAssetId::<BondingPallet>::get(), next_asset_id + 3);
146+
147+
let new_assets = Vec::from_iter(next_asset_id..next_asset_id + 3);
148+
let pool_id: AccountIdOf<Test> = calculate_pool_id(&new_assets);
149+
150+
let details = Pools::<Test>::get(pool_id.clone()).unwrap();
151+
152+
assert_eq!(BondingPallet::get_currencies_number(&details), 3);
153+
assert_eq!(details.bonded_currencies, new_assets);
154+
155+
assert_eq!(
156+
Balances::free_balance(ACCOUNT_00),
157+
initial_balance.sub(BondingPallet::calculate_pool_deposit(3))
158+
);
159+
160+
for new_asset_id in new_assets {
161+
assert!(Assets::asset_exists(new_asset_id));
162+
assert_eq!(Assets::owner(new_asset_id), Some(pool_id.clone()));
163+
}
164+
});
165+
}
166+
167+
#[test]
168+
fn multi_currency_with_empty_metadata() {
107169
let initial_balance = ONE_HUNDRED_KILT;
108170
ExtBuilder::default()
109171
.with_native_balances(vec![(ACCOUNT_00, initial_balance)])
@@ -113,13 +175,12 @@ fn multi_currency() {
113175
let curve = get_linear_bonding_curve_input();
114176

115177
let bonded_token = TokenMetaOf::<Test> {
116-
name: BoundedVec::truncate_from(b"Bitcoin".to_vec()),
117-
symbol: BoundedVec::truncate_from(b"btc".to_vec()),
178+
name: BoundedVec::new(),
179+
symbol: BoundedVec::new(),
118180
min_balance: 1,
119181
};
120182

121183
let bonded_tokens = bounded_vec![bonded_token; 3];
122-
123184
let next_asset_id = NextAssetId::<BondingPallet>::get();
124185

125186
assert_ok!(BondingPallet::create_pool(
@@ -207,6 +268,92 @@ fn can_create_identical_pools() {
207268
});
208269
}
209270

271+
#[test]
272+
fn cannot_reuse_names() {
273+
let initial_balance = ONE_HUNDRED_KILT;
274+
ExtBuilder::default()
275+
.with_native_balances(vec![(ACCOUNT_00, initial_balance)])
276+
.with_collaterals(vec![DEFAULT_COLLATERAL_CURRENCY_ID])
277+
.build_and_execute_with_sanity_tests(|| {
278+
let origin = RawOrigin::Signed(ACCOUNT_00).into();
279+
let curve = get_linear_bonding_curve_input();
280+
281+
let bonded_tokens = bounded_vec![
282+
TokenMetaOf::<Test> {
283+
name: BoundedVec::truncate_from(b"Bitcoin".to_vec()),
284+
symbol: BoundedVec::truncate_from(b"BTC".to_vec()),
285+
min_balance: 1,
286+
},
287+
TokenMetaOf::<Test> {
288+
name: BoundedVec::truncate_from(b"Ether".to_vec()),
289+
symbol: BoundedVec::truncate_from(b"ETH".to_vec()),
290+
min_balance: 1,
291+
},
292+
TokenMetaOf::<Test> {
293+
name: BoundedVec::truncate_from(b"Bitcoin".to_vec()),
294+
symbol: BoundedVec::truncate_from(b"DOGE".to_vec()),
295+
min_balance: 1,
296+
}
297+
];
298+
299+
assert_err!(
300+
BondingPallet::create_pool(
301+
origin,
302+
curve,
303+
DEFAULT_COLLATERAL_CURRENCY_ID,
304+
bonded_tokens,
305+
DEFAULT_BONDED_DENOMINATION,
306+
true,
307+
1
308+
),
309+
Error::<Test>::InvalidInput
310+
);
311+
});
312+
}
313+
314+
#[test]
315+
fn cannot_reuse_symbols() {
316+
let initial_balance = ONE_HUNDRED_KILT;
317+
ExtBuilder::default()
318+
.with_native_balances(vec![(ACCOUNT_00, initial_balance)])
319+
.with_collaterals(vec![DEFAULT_COLLATERAL_CURRENCY_ID])
320+
.build_and_execute_with_sanity_tests(|| {
321+
let origin = RawOrigin::Signed(ACCOUNT_00).into();
322+
let curve = get_linear_bonding_curve_input();
323+
324+
let bonded_tokens = bounded_vec![
325+
TokenMetaOf::<Test> {
326+
name: BoundedVec::truncate_from(b"Bitcoin".to_vec()),
327+
symbol: BoundedVec::truncate_from(b"BTC".to_vec()),
328+
min_balance: 1,
329+
},
330+
TokenMetaOf::<Test> {
331+
name: BoundedVec::truncate_from(b"Ether".to_vec()),
332+
symbol: BoundedVec::truncate_from(b"ETH".to_vec()),
333+
min_balance: 1,
334+
},
335+
TokenMetaOf::<Test> {
336+
name: BoundedVec::truncate_from(b"Dogecoin".to_vec()),
337+
symbol: BoundedVec::truncate_from(b"BTC".to_vec()),
338+
min_balance: 1,
339+
}
340+
];
341+
342+
assert_err!(
343+
BondingPallet::create_pool(
344+
origin,
345+
curve,
346+
DEFAULT_COLLATERAL_CURRENCY_ID,
347+
bonded_tokens,
348+
DEFAULT_BONDED_DENOMINATION,
349+
true,
350+
1
351+
),
352+
Error::<Test>::InvalidInput
353+
);
354+
});
355+
}
356+
210357
#[test]
211358
fn fails_if_collateral_not_exists() {
212359
ExtBuilder::default()

0 commit comments

Comments
 (0)