Skip to content

Commit 4de08e5

Browse files
authored
revert: "feat: Turn All Call::new_in into Call::new (i.e. Stop Supporting Reentrancy) (#440)" (#464)
Towards #355 Reentrancy is necessary for Erc20FlashMint. refs: da0249b
1 parent 5fce6db commit 4de08e5

File tree

12 files changed

+68
-27
lines changed

12 files changed

+68
-27
lines changed

CHANGELOG.md

+2-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [v0.2.0-alpha.2] - 2024-12-17
8+
## [v0.2.0-alpha.2] - 2024-12-18
99

1010
### Added
1111

@@ -19,20 +19,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919

2020
### Changed
2121

22-
- Update "magic values" to explicit calculations in `Erc721Metadata::supports_interface`, and
23-
`Erc721::_check_on_erc721_received`. #442
22+
- Update "magic values" to explicit calculations in `Erc721Metadata::supports_interface`, and `Erc721::_check_on_erc721_received`. #442
2423
- Implement `AddAssignUnchecked` and `SubAssignUnchecked` for `StorageUint`. #418
2524
- Implement `MethodError` for `safe_erc20::Error`. #402
2625
- Use `function_selector!` to calculate transfer type selector in `Erc1155`. #417
2726

2827
### Changed (Breaking)
2928

3029
- Update internal functions of `Erc721` and `Erc721Consecutive` to accept a reference to `Bytes`. #437
31-
- Stop supporting reentrancy, and borrow `self` immutably in `IErc721::_check_on_erc721_received`. #440
32-
- Remove `&mut self` parameter from `IErc1155::_check_on_erc1155_received` and make it an associated function. #440
33-
- Remove `storage: &mut impl TopLevelStorage` parameter from `ecdsa::recover`. #440
34-
- Remove `TopLevelStorage` trait implementation from `VestingWallet`, `Erc1155`, `Erc20Permit`, `SafeErc20`,
35-
`Erc721Consecutive`, and `Erc721`. #440
3630

3731
### Fixed
3832

contracts/src/finance/vesting_wallet.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use stylus_sdk::{
3333
call::{self, call, Call},
3434
contract, evm, function_selector,
3535
prelude::storage,
36-
storage::{StorageMap, StorageU256, StorageU64},
36+
storage::{StorageMap, StorageU256, StorageU64, TopLevelStorage},
3737
stylus_proc::{public, SolidityError},
3838
};
3939

@@ -118,6 +118,11 @@ pub struct VestingWallet {
118118
pub safe_erc20: SafeErc20,
119119
}
120120

121+
/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when
122+
/// calling other contracts and not `&mut (impl TopLevelStorage +
123+
/// BorrowMut<Self>)`. Should be fixed in the future by the Stylus team.
124+
unsafe impl TopLevelStorage for VestingWallet {}
125+
121126
/// Required interface of a [`VestingWallet`] compliant contract.
122127
#[interface_id]
123128
pub trait IVestingWallet {
@@ -421,7 +426,7 @@ impl IVestingWallet for VestingWallet {
421426

422427
let owner = self.ownable.owner();
423428

424-
call(Call::new().value(amount), owner, &[])?;
429+
call(Call::new_in(self).value(amount), owner, &[])?;
425430

426431
evm::log(EtherReleased { amount });
427432

contracts/src/token/erc1155/extensions/supply.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ impl Erc1155Supply {
279279
self._update(from, to, ids.clone(), values.clone())?;
280280

281281
if !to.is_zero() {
282-
Erc1155::_check_on_erc1155_received(
282+
self.erc1155._check_on_erc1155_received(
283283
msg::sender(),
284284
from,
285285
to,

contracts/src/token/erc1155/extensions/uri_storage.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ mod tests {
9494
use stylus_sdk::prelude::storage;
9595

9696
use super::Erc1155UriStorage;
97-
use crate::token::erc1155::extensions::Erc1155MetadataUri;
97+
use crate::token::erc1155::{extensions::Erc1155MetadataUri, Erc1155};
9898

9999
fn random_token_id() -> U256 {
100100
let num: u32 = rand::random();

contracts/src/token/erc1155/mod.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use stylus_sdk::{
88
call::{self, Call, MethodError},
99
evm, function_selector, msg,
1010
prelude::{public, storage, AddressVM, SolidityError},
11-
storage::{StorageBool, StorageMap, StorageU256},
11+
storage::{StorageBool, StorageMap, StorageU256, TopLevelStorage},
1212
};
1313

1414
use crate::utils::{
@@ -194,6 +194,11 @@ pub struct Erc1155 {
194194
StorageMap<Address, StorageMap<Address, StorageBool>>,
195195
}
196196

197+
/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when
198+
/// calling other contracts and not `&mut (impl TopLevelStorage +
199+
/// BorrowMut<Self>)`. Should be fixed in the future by the Stylus team.
200+
unsafe impl TopLevelStorage for Erc1155 {}
201+
197202
/// Required interface of an [`Erc1155`] compliant contract.
198203
#[interface_id]
199204
pub trait IErc1155 {
@@ -557,7 +562,7 @@ impl Erc1155 {
557562
self._update(from, to, ids.clone(), values.clone())?;
558563

559564
if !to.is_zero() {
560-
Erc1155::_check_on_erc1155_received(
565+
self._check_on_erc1155_received(
561566
msg::sender(),
562567
from,
563568
to,
@@ -767,6 +772,7 @@ impl Erc1155 {
767772
///
768773
/// # Arguments
769774
///
775+
/// * `&mut self` - Write access to the contract's state.
770776
/// * `operator` - Generally the address that initiated the token transfer
771777
/// (e.g. `msg::sender()`).
772778
/// * `from` - Account of the sender.
@@ -785,6 +791,7 @@ impl Erc1155 {
785791
/// interface id or returned with error, then the error
786792
/// [`Error::InvalidReceiver`] is returned.
787793
fn _check_on_erc1155_received(
794+
&mut self,
788795
operator: Address,
789796
from: Address,
790797
to: Address,
@@ -796,7 +803,7 @@ impl Erc1155 {
796803
}
797804

798805
let receiver = IERC1155Receiver::new(to);
799-
let call = Call::new();
806+
let call = Call::new_in(self);
800807
let result = match details.transfer {
801808
Transfer::Single { id, value } => receiver
802809
.on_erc_1155_received(call, operator, from, id, value, data),

contracts/src/token/erc20/extensions/permit.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use alloy_sol_types::SolType;
1717
use stylus_sdk::{
1818
block,
1919
prelude::{storage, StorageType},
20+
storage::TopLevelStorage,
2021
stylus_proc::{public, SolidityError},
2122
};
2223

@@ -81,6 +82,11 @@ pub struct Erc20Permit<T: IEip712 + StorageType> {
8182
pub eip712: T,
8283
}
8384

85+
/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when
86+
/// calling other contracts and not `&mut (impl TopLevelStorage +
87+
/// BorrowMut<Self>)`. Should be fixed in the future by the Stylus team.
88+
unsafe impl<T: IEip712 + StorageType> TopLevelStorage for Erc20Permit<T> {}
89+
8490
#[public]
8591
impl<T: IEip712 + StorageType> Erc20Permit<T> {
8692
/// Returns the current nonce for `owner`.
@@ -171,7 +177,7 @@ impl<T: IEip712 + StorageType> Erc20Permit<T> {
171177

172178
let hash: B256 = self.eip712.hash_typed_data_v4(struct_hash);
173179

174-
let signer: Address = ecdsa::recover(hash, v, r, s)?;
180+
let signer: Address = ecdsa::recover(self, hash, v, r, s)?;
175181

176182
if signer != owner {
177183
return Err(ERC2612InvalidSigner { signer, owner }.into());

contracts/src/token/erc20/utils/safe_erc20.rs

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use stylus_sdk::{
1818
evm::gas_left,
1919
function_selector,
2020
prelude::storage,
21+
storage::TopLevelStorage,
2122
stylus_proc::{public, SolidityError},
2223
types::AddressVM,
2324
};
@@ -87,6 +88,11 @@ mod token {
8788
#[storage]
8889
pub struct SafeErc20 {}
8990

91+
/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when
92+
/// calling other contracts and not `&mut (impl TopLevelStorage +
93+
/// BorrowMut<Self>)`. Should be fixed in the future by the Stylus team.
94+
unsafe impl TopLevelStorage for SafeErc20 {}
95+
9096
/// Required interface of a [`SafeErc20`] utility contract.
9197
pub trait ISafeErc20 {
9298
/// The error type associated to this trait implementation.

contracts/src/token/erc721/extensions/consecutive.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use alloy_primitives::{uint, Address, U256};
3030
use stylus_sdk::{
3131
abi::Bytes,
3232
evm, msg,
33-
prelude::storage,
33+
prelude::{storage, TopLevelStorage},
3434
stylus_proc::{public, SolidityError},
3535
};
3636

@@ -140,6 +140,8 @@ pub enum Error {
140140
ForbiddenBatchBurn(ERC721ForbiddenBatchBurn),
141141
}
142142

143+
unsafe impl TopLevelStorage for Erc721Consecutive {}
144+
143145
// ************** ERC-721 External **************
144146

145147
#[public]

contracts/src/token/erc721/mod.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@ pub struct Erc721 {
207207
StorageMap<Address, StorageMap<Address, StorageBool>>,
208208
}
209209

210+
/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when
211+
/// calling other contracts and not `&mut (impl TopLevelStorage +
212+
/// BorrowMut<Self>)`. Should be fixed in the future by the Stylus team.
213+
unsafe impl TopLevelStorage for Erc721 {}
214+
210215
/// Required interface of an [`Erc721`] compliant contract.
211216
#[interface_id]
212217
pub trait IErc721 {
@@ -1099,7 +1104,7 @@ impl Erc721 {
10991104
///
11001105
/// # Arguments
11011106
///
1102-
/// * `&self` - Read access to the contract's state.
1107+
/// * `&mut self` - Write access to the contract's state.
11031108
/// * `operator` - Account to add to the set of authorized operators.
11041109
/// * `from` - Account of the sender.
11051110
/// * `to` - Account of the recipient.
@@ -1113,7 +1118,7 @@ impl Erc721 {
11131118
/// interface id or returned with error, then the error
11141119
/// [`Error::InvalidReceiver`] is returned.
11151120
pub fn _check_on_erc721_received(
1116-
&self,
1121+
&mut self,
11171122
operator: Address,
11181123
from: Address,
11191124
to: Address,
@@ -1125,7 +1130,7 @@ impl Erc721 {
11251130
}
11261131

11271132
let receiver = IERC721Receiver::new(to);
1128-
let call = Call::new();
1133+
let call = Call::new_in(self);
11291134
let result = receiver.on_erc_721_received(
11301135
call,
11311136
operator,

contracts/src/utils/cryptography/ecdsa.rs

+21-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use alloy_primitives::{address, uint, Address, B256, U256};
88
use alloy_sol_types::SolType;
99
use stylus_sdk::{
1010
call::{self, Call, MethodError},
11+
storage::TopLevelStorage,
1112
stylus_proc::SolidityError,
1213
};
1314

@@ -76,6 +77,7 @@ impl MethodError for ecdsa::Error {
7677
///
7778
/// # Arguments
7879
///
80+
/// * `storage` - Write access to storage.
7981
/// * `hash` - Hash of the message.
8082
/// * `v` - `v` value from the signature.
8183
/// * `r` - `r` value from the signature.
@@ -91,10 +93,16 @@ impl MethodError for ecdsa::Error {
9193
/// # Panics
9294
///
9395
/// * If the `ecrecover` precompile fails to execute.
94-
pub fn recover(hash: B256, v: u8, r: B256, s: B256) -> Result<Address, Error> {
96+
pub fn recover(
97+
storage: &mut impl TopLevelStorage,
98+
hash: B256,
99+
v: u8,
100+
r: B256,
101+
s: B256,
102+
) -> Result<Address, Error> {
95103
check_if_malleable(&s)?;
96104
// If the signature is valid (and not malleable), return the signer address.
97-
_recover(hash, v, r, s)
105+
_recover(storage, hash, v, r, s)
98106
}
99107

100108
/// Calls `ecrecover` EVM precompile.
@@ -104,6 +112,7 @@ pub fn recover(hash: B256, v: u8, r: B256, s: B256) -> Result<Address, Error> {
104112
///
105113
/// # Arguments
106114
///
115+
/// * `storage` - Write access to storage.
107116
/// * `hash` - Hash of the message.
108117
/// * `v` - `v` value from the signature.
109118
/// * `r` - `r` value from the signature.
@@ -119,7 +128,13 @@ pub fn recover(hash: B256, v: u8, r: B256, s: B256) -> Result<Address, Error> {
119128
/// # Panics
120129
///
121130
/// * If the `ecrecover` precompile fails to execute.
122-
fn _recover(hash: B256, v: u8, r: B256, s: B256) -> Result<Address, Error> {
131+
fn _recover(
132+
storage: &mut impl TopLevelStorage,
133+
hash: B256,
134+
v: u8,
135+
r: B256,
136+
s: B256,
137+
) -> Result<Address, Error> {
123138
let calldata = encode_calldata(hash, v, r, s);
124139

125140
if v == 0 || v == 1 {
@@ -130,8 +145,9 @@ fn _recover(hash: B256, v: u8, r: B256, s: B256) -> Result<Address, Error> {
130145
return Err(ECDSAInvalidSignature {}.into());
131146
}
132147

133-
let recovered = call::static_call(Call::new(), ECRECOVER_ADDR, &calldata)
134-
.expect("should call `ecrecover` precompile");
148+
let recovered =
149+
call::static_call(Call::new_in(storage), ECRECOVER_ADDR, &calldata)
150+
.expect("should call `ecrecover` precompile");
135151

136152
let recovered = Address::from_slice(&recovered[12..]);
137153

examples/ecdsa/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ impl ECDSAExample {
2020
r: B256,
2121
s: B256,
2222
) -> Result<Address, Vec<u8>> {
23-
let signer = ecdsa::recover(hash, v, r, s)?;
23+
let signer = ecdsa::recover(self, hash, v, r, s)?;
2424
Ok(signer)
2525
}
2626
}

lib/motsu/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Annotate tests with `#[motsu::test]` instead of `#[test]` to get access to VM
1313
affordances.
1414

1515
Note that we require contracts to implement `stylus_sdk::prelude::StorageType`.
16-
This trait is typically implemented by default with `stylus_proc::sol_storage`
16+
This trait is typically implemented by default with `stylus_proc::sol_storage`
1717
or `stylus_proc::storage` macros.
1818

1919
```rust

0 commit comments

Comments
 (0)