Skip to content

Commit 1efd337

Browse files
committed
introduce IERC20Metadata to pallet-assets-precompiles
1 parent 21df44e commit 1efd337

File tree

4 files changed

+203
-38
lines changed

4 files changed

+203
-38
lines changed

substrate/frame/assets/precompiles/src/lib.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ where
118118
IERC20Calls::allowance(call) => Self::allowance(asset_id, call, env),
119119
IERC20Calls::approve(call) => Self::approve(asset_id, call, env),
120120
IERC20Calls::transferFrom(call) => Self::transfer_from(asset_id, call, env),
121+
IERC20Calls::name(_) => Self::name(asset_id, env),
122+
IERC20Calls::symbol(_) => Self::symbol(asset_id, env),
123+
IERC20Calls::decimals(_) => Self::decimals(asset_id, env),
121124
}
122125
}
123126
}
@@ -322,4 +325,49 @@ where
322325

323326
return Ok(IERC20::transferFromCall::abi_encode_returns(&true));
324327
}
328+
329+
/// Execute the name call.
330+
fn name(
331+
asset_id: <Runtime as Config<Instance>>::AssetId,
332+
env: &mut impl Ext<T = Runtime>,
333+
) -> Result<Vec<u8>, Error> {
334+
env.charge(<Runtime as Config<Instance>>::WeightInfo::get_name())?;
335+
336+
let metadata = pallet_assets::Pallet::<Runtime, Instance>::get_metadata(asset_id)
337+
.ok_or(Error::Revert(Revert { reason: "Metadata not found".into() }))?;
338+
339+
let name = alloc::string::String::from_utf8(metadata.name.to_vec())
340+
.map_err(|_| Error::Revert(Revert { reason: "Invalid UTF-8 in name".into() }))?;
341+
342+
return Ok(IERC20::nameCall::abi_encode_returns(&name));
343+
}
344+
345+
/// Execute the symbol call.
346+
fn symbol(
347+
asset_id: <Runtime as Config<Instance>>::AssetId,
348+
env: &mut impl Ext<T = Runtime>,
349+
) -> Result<Vec<u8>, Error> {
350+
env.charge(<Runtime as Config<Instance>>::WeightInfo::get_symbol())?;
351+
352+
let metadata = pallet_assets::Pallet::<Runtime, Instance>::get_metadata(asset_id)
353+
.ok_or(Error::Revert(Revert { reason: "Metadata not found".into() }))?;
354+
355+
let symbol = alloc::string::String::from_utf8(metadata.symbol.to_vec())
356+
.map_err(|_| Error::Revert(Revert { reason: "Invalid UTF-8 in symbol".into() }))?;
357+
358+
return Ok(IERC20::symbolCall::abi_encode_returns(&symbol));
359+
}
360+
361+
/// Execute the decimals call.
362+
fn decimals(
363+
asset_id: <Runtime as Config<Instance>>::AssetId,
364+
env: &mut impl Ext<T = Runtime>,
365+
) -> Result<Vec<u8>, Error> {
366+
env.charge(<Runtime as Config<Instance>>::WeightInfo::get_decimals())?;
367+
368+
let metadata = pallet_assets::Pallet::<Runtime, Instance>::get_metadata(asset_id)
369+
.ok_or(Error::Revert(Revert { reason: "Metadata not found".into() }))?;
370+
371+
return Ok(IERC20::decimalsCall::abi_encode_returns(&metadata.decimals));
372+
}
325373
}

substrate/frame/assets/src/benchmarking.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,5 +641,50 @@ benchmarks_instance_pallet! {
641641
assert_eq!(Reserves::<T, I>::get(id)[0], reserve);
642642
}
643643

644+
get_name {
645+
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
646+
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
647+
let name_bytes = vec![0u8; T::StringLimit::get() as usize];
648+
let symbol_bytes = vec![0u8; T::StringLimit::get() as usize];
649+
let origin = SystemOrigin::Signed(caller).into();
650+
Assets::<T, I>::set_metadata(origin, asset_id.clone(), name_bytes.clone(), symbol_bytes, 12)?;
651+
}: {
652+
let _ = Pallet::<T, I>::get_metadata(asset_id.clone().into());
653+
} verify {
654+
let metadata = Pallet::<T, I>::get_metadata(asset_id.into());
655+
assert!(metadata.is_some());
656+
assert_eq!(metadata.unwrap().name.to_vec(), name_bytes);
657+
}
658+
659+
get_symbol {
660+
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
661+
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
662+
let name_bytes = vec![0u8; T::StringLimit::get() as usize];
663+
let symbol_bytes = vec![0u8; T::StringLimit::get() as usize];
664+
let origin = SystemOrigin::Signed(caller).into();
665+
Assets::<T, I>::set_metadata(origin, asset_id.clone(), name_bytes, symbol_bytes.clone(), 12)?;
666+
}: {
667+
let _ = Pallet::<T, I>::get_metadata(asset_id.clone().into());
668+
} verify {
669+
let metadata = Pallet::<T, I>::get_metadata(asset_id.into());
670+
assert!(metadata.is_some());
671+
assert_eq!(metadata.unwrap().symbol.to_vec(), symbol_bytes);
672+
}
673+
674+
get_decimals {
675+
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
676+
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
677+
let name_bytes = vec![0u8; T::StringLimit::get() as usize];
678+
let symbol_bytes = vec![0u8; T::StringLimit::get() as usize];
679+
let origin = SystemOrigin::Signed(caller).into();
680+
Assets::<T, I>::set_metadata(origin, asset_id.clone(), name_bytes, symbol_bytes, 12)?;
681+
}: {
682+
let _ = Pallet::<T, I>::get_metadata(asset_id.clone().into());
683+
} verify {
684+
let metadata = Pallet::<T, I>::get_metadata(asset_id.into());
685+
assert!(metadata.is_some());
686+
assert_eq!(metadata.unwrap().decimals, 12);
687+
}
688+
644689
impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test)
645690
}

substrate/frame/assets/src/weights.rs

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

substrate/primitives/ethereum-standards/src/IERC20.sol

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,66 @@ pragma solidity ^0.8.20;
77
/// @dev Interface of the ERC-20 standard as defined in the ERC.
88
///
99
interface IERC20 {
10-
/// @dev Emitted when `value` tokens are moved from one account (`from`) to
11-
/// another (`to`).
12-
///
13-
/// Note that `value` may be zero.
10+
/// @dev Emitted when `value` tokens are moved from one account (`from`) to
11+
/// another (`to`).
12+
///
13+
/// Note that `value` may be zero.
1414
event Transfer(address indexed from, address indexed to, uint256 value);
1515

16-
/// @dev Emitted when the allowance of a `spender` for an `owner` is set by
17-
/// a call to {approve}. `value` is the new allowance.
16+
/// @dev Emitted when the allowance of a `spender` for an `owner` is set by
17+
/// a call to {approve}. `value` is the new allowance.
1818
event Approval(address indexed owner, address indexed spender, uint256 value);
1919

20-
/// @dev Returns the value of tokens in existence.
20+
/// @dev Returns the name of the token.
21+
function name() external view returns (string memory);
22+
23+
/// @dev Returns the symbol of the token.
24+
function symbol() external view returns (string memory);
25+
26+
/// @dev Returns the decimals places of the token.
27+
function decimals() external view returns (uint8);
28+
29+
/// @dev Returns the value of tokens in existence.
2130
function totalSupply() external view returns (uint256);
2231

23-
/// @dev Returns the value of tokens owned by `account`.
32+
/// @dev Returns the value of tokens owned by `account`.
2433
function balanceOf(address account) external view returns (uint256);
2534

26-
/// @dev Moves a `value` amount of tokens from the caller's account to `to`.
27-
///
28-
/// Returns a boolean value indicating whether the operation succeeded.
29-
///
30-
/// Emits a {Transfer} event.
35+
/// @dev Moves a `value` amount of tokens from the caller's account to `to`.
36+
///
37+
/// Returns a boolean value indicating whether the operation succeeded.
38+
///
39+
/// Emits a {Transfer} event.
3140
function transfer(address to, uint256 value) external returns (bool);
3241

33-
/// @dev Returns the remaining number of tokens that `spender` will be
34-
/// allowed to spend on behalf of `owner` through {transferFrom}. This is
35-
/// zero by default.
36-
///
37-
/// This value changes when {approve} or {transferFrom} are called.
42+
/// @dev Returns the remaining number of tokens that `spender` will be
43+
/// allowed to spend on behalf of `owner` through {transferFrom}. This is
44+
/// zero by default.
45+
///
46+
/// This value changes when {approve} or {transferFrom} are called.
3847
function allowance(address owner, address spender) external view returns (uint256);
3948

40-
/// @dev Sets a `value` amount of tokens as the allowance of `spender` over the
41-
/// caller's tokens.
42-
///
43-
/// Returns a boolean value indicating whether the operation succeeded.
44-
///
45-
/// IMPORTANT: Beware that changing an allowance with this method brings the risk
46-
/// that someone may use both the old and the new allowance by unfortunate
47-
/// transaction ordering. One possible solution to mitigate this race
48-
/// condition is to first reduce the spender's allowance to 0 and set the
49-
/// desired value afterwards:
50-
/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
51-
///
52-
/// Emits an {Approval} event.
49+
/// @dev Sets a `value` amount of tokens as the allowance of `spender` over the
50+
/// caller's tokens.
51+
///
52+
/// Returns a boolean value indicating whether the operation succeeded.
53+
///
54+
/// IMPORTANT: Beware that changing an allowance with this method brings the risk
55+
/// that someone may use both the old and the new allowance by unfortunate
56+
/// transaction ordering. One possible solution to mitigate this race
57+
/// condition is to first reduce the spender's allowance to 0 and set the
58+
/// desired value afterwards:
59+
/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
60+
///
61+
/// Emits an {Approval} event.
5362
function approve(address spender, uint256 value) external returns (bool);
5463

55-
/// @dev Moves a `value` amount of tokens from `from` to `to` using the
56-
/// allowance mechanism. `value` is then deducted from the caller's
57-
/// allowance.
58-
///
59-
/// Returns a boolean value indicating whether the operation succeeded.
60-
///
61-
/// Emits a {Transfer} event.
64+
/// @dev Moves a `value` amount of tokens from `from` to `to` using the
65+
/// allowance mechanism. `value` is then deducted from the caller's
66+
/// allowance.
67+
///
68+
/// Returns a boolean value indicating whether the operation succeeded.
69+
///
70+
/// Emits a {Transfer} event.
6271
function transferFrom(address from, address to, uint256 value) external returns (bool);
6372
}

0 commit comments

Comments
 (0)