Skip to content

Commit 9c510ee

Browse files
committed
introduce revive.instantiateWithCodeAs
1 parent 21df44e commit 9c510ee

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed

substrate/frame/revive/src/lib.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,72 @@ pub mod pallet {
12301230
)
12311231
}
12321232

1233+
/// Instantiate a new contract with code, using a specified account id.
1234+
///
1235+
/// This is similar to [`Self::instantiate_with_code`] but allows root to specify
1236+
/// which account should be used for the instantiation. This is useful for
1237+
/// administrative operations where the actual instantiator should be different
1238+
/// from the caller.
1239+
///
1240+
/// # Parameters
1241+
///
1242+
/// * `origin`: Must be root.
1243+
/// * `value`: The balance to transfer from the `account` to the new contract.
1244+
/// * `weight_limit`: The gas limit enforced when executing the constructor.
1245+
/// * `storage_deposit_limit`: The maximum amount of balance that can be charged
1246+
/// from the caller to pay for the storage consumed.
1247+
/// * `code`: The contract code to deploy in raw bytes.
1248+
/// * `data`: The input data to pass to the contract constructor.
1249+
/// * `salt`: Used for the address derivation. See [`Pallet::contract_address`].
1250+
/// * `account`: The account to use for instantiation.
1251+
///
1252+
/// Requires root origin. The `account` will be used as the deployer account.
1253+
#[pallet::call_index(13)]
1254+
#[pallet::weight(
1255+
<T as Config>::WeightInfo::instantiate_with_code(
1256+
code.len() as u32,
1257+
data.len() as u32,
1258+
)
1259+
)]
1260+
pub fn instantiate_with_code_as(
1261+
origin: OriginFor<T>,
1262+
#[pallet::compact] value: BalanceOf<T>,
1263+
weight_limit: Weight,
1264+
#[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1265+
code: Vec<u8>,
1266+
data: Vec<u8>,
1267+
salt: Option<[u8; 32]>,
1268+
account: T::AccountId,
1269+
) -> DispatchResultWithPostInfo {
1270+
ensure_root(origin)?;
1271+
let instantiate_origin: OriginFor<T> = RawOrigin::Signed(account).into();
1272+
Self::ensure_non_contract_if_signed(&instantiate_origin)?;
1273+
let code_len = code.len() as u32;
1274+
let data_len = data.len() as u32;
1275+
let mut output = Self::bare_instantiate(
1276+
instantiate_origin,
1277+
Pallet::<T>::convert_native_to_evm(value),
1278+
TransactionLimits::WeightAndDeposit {
1279+
weight_limit,
1280+
deposit_limit: storage_deposit_limit,
1281+
},
1282+
Code::Upload(code),
1283+
data,
1284+
salt,
1285+
ExecConfig::new_substrate_tx(),
1286+
);
1287+
if let Ok(retval) = &output.result {
1288+
if retval.result.did_revert() {
1289+
output.result = Err(<Error<T>>::ContractReverted.into());
1290+
}
1291+
}
1292+
dispatch_result(
1293+
output.result.map(|result| result.result),
1294+
output.weight_consumed,
1295+
<T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1296+
)
1297+
}
1298+
12331299
/// Same as [`Self::instantiate_with_code`], but intended to be dispatched **only**
12341300
/// by an EVM transaction through the EVM compatibility layer.
12351301
///

substrate/frame/revive/src/test_utils/builder.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,33 @@ builder!(
125125
}
126126
);
127127

128+
builder!(
129+
instantiate_with_code_as(
130+
origin: OriginFor<T>,
131+
value: BalanceOf<T>,
132+
weight_limit: Weight,
133+
storage_deposit_limit: BalanceOf<T>,
134+
code: Vec<u8>,
135+
data: Vec<u8>,
136+
salt: Option<[u8; 32]>,
137+
account: T::AccountId,
138+
) -> DispatchResultWithPostInfo;
139+
140+
/// Create an [`InstantiateWithCodeAsBuilder`] with default values.
141+
pub fn instantiate_with_code_as(origin: OriginFor<T>, code: Vec<u8>, account: T::AccountId) -> Self {
142+
Self {
143+
origin,
144+
value: 0u32.into(),
145+
weight_limit: WEIGHT_LIMIT,
146+
storage_deposit_limit: deposit_limit::<T>(),
147+
code,
148+
data: vec![],
149+
salt: Some([0; 32]),
150+
account,
151+
}
152+
}
153+
);
154+
128155
builder!(
129156
bare_instantiate(
130157
origin: OriginFor<T>,

substrate/frame/revive/src/tests.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ pub(crate) mod builder {
241241
InstantiateBuilder::<Test>::instantiate(RuntimeOrigin::signed(ALICE), code_hash)
242242
}
243243

244+
pub fn instantiate_with_code_as(code: Vec<u8>, account: sp_runtime::AccountId32) -> InstantiateWithCodeAsBuilder<Test> {
245+
InstantiateWithCodeAsBuilder::<Test>::instantiate_with_code_as(
246+
RuntimeOrigin::signed(ALICE),
247+
code,
248+
account,
249+
)
250+
}
251+
244252
pub fn call(dest: H160) -> CallBuilder<Test> {
245253
CallBuilder::<Test>::call(RuntimeOrigin::signed(ALICE), dest)
246254
}

substrate/frame/revive/src/tests/pvm.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,6 +2934,30 @@ fn signed_cannot_set_code() {
29342934
});
29352935
}
29362936

2937+
#[test]
2938+
fn signed_cannot_instantiate_with_code_as_origin() {
2939+
let (binary, _) = compile_module("dummy").unwrap();
2940+
2941+
ExtBuilder::default().build().execute_with(|| {
2942+
assert_err_ignore_postinfo!(
2943+
builder::instantiate_with_code_as(binary, BOB).build(),
2944+
DispatchError::BadOrigin
2945+
);
2946+
});
2947+
}
2948+
2949+
#[test]
2950+
fn root_can_instantiate_with_code_as_origin() {
2951+
let (binary, _) = compile_module("dummy").unwrap();
2952+
2953+
ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
2954+
let _ = <Test as Config>::Currency::set_balance(&BOB, 1_000_000);
2955+
2956+
// Root can instantiate on behalf of BOB
2957+
assert_ok!(builder::instantiate_with_code_as(binary, BOB).origin(RuntimeOrigin::root()).build());
2958+
});
2959+
}
2960+
29372961
#[test]
29382962
fn none_cannot_call_code() {
29392963
ExtBuilder::default().build().execute_with(|| {

0 commit comments

Comments
 (0)