Skip to content

Commit bc8c514

Browse files
committed
add subnet precompile contract
1 parent fbc26eb commit bc8c514

File tree

5 files changed

+345
-85
lines changed

5 files changed

+345
-85
lines changed

runtime/src/precompiles/mod.rs

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,35 @@ use sp_core::{hashing::keccak_256, H160};
33
use sp_runtime::AccountId32;
44

55
use pallet_evm::{
6-
ExitError, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle,
6+
AddressMapping, BalanceConverter, ExitError, ExitSucceed, HashedAddressMapping,
7+
IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput,
78
PrecompileResult, PrecompileSet,
89
};
910
use pallet_evm_precompile_modexp::Modexp;
1011
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
1112
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
1213

14+
use frame_system::RawOrigin;
15+
16+
use sp_core::crypto::Ss58Codec;
17+
use sp_core::U256;
18+
use sp_runtime::traits::Dispatchable;
19+
use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto};
20+
21+
use sp_std::vec;
22+
23+
use crate::{Runtime, RuntimeCall};
24+
1325
// Include custom precompiles
1426
mod balance_transfer;
1527
mod ed25519;
1628
mod staking;
29+
mod subnet;
1730

1831
use balance_transfer::*;
1932
use ed25519::*;
2033
use staking::*;
34+
use subnet::*;
2135

2236
pub struct FrontierPrecompiles<R>(PhantomData<R>);
2337

@@ -37,7 +51,7 @@ where
3751
pub fn new() -> Self {
3852
Self(Default::default())
3953
}
40-
pub fn used_addresses() -> [H160; 10] {
54+
pub fn used_addresses() -> [H160; 11] {
4155
[
4256
hash(1),
4357
hash(2),
@@ -49,6 +63,7 @@ where
4963
hash(EDVERIFY_PRECOMPILE_INDEX),
5064
hash(BALANCE_TRANSFER_INDEX),
5165
hash(STAKING_PRECOMPILE_INDEX),
66+
hash(SUBNET_PRECOMPILE_INDEX),
5267
]
5368
}
5469
}
@@ -73,6 +88,7 @@ where
7388
Some(BalanceTransferPrecompile::execute(handle))
7489
}
7590
a if a == hash(STAKING_PRECOMPILE_INDEX) => Some(StakingPrecompile::execute(handle)),
91+
a if a == hash(SUBNET_PRECOMPILE_INDEX) => Some(SubnetPrecompile::execute(handle)),
7692
_ => None,
7793
}
7894
}
@@ -118,8 +134,86 @@ pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], Precompil
118134
if let Some(slice) = maybe_slice {
119135
Ok(slice)
120136
} else {
137+
log::error!(
138+
"fail to get slice from data, {:?}, from {}, to {}",
139+
&data,
140+
from,
141+
to
142+
);
121143
Err(PrecompileFailure::Error {
122144
exit_status: ExitError::InvalidRange,
123145
})
124146
}
125147
}
148+
149+
fn transfer_back_to_caller(
150+
account_id: &AccountId32,
151+
amount: U256,
152+
) -> Result<(), PrecompileFailure> {
153+
// this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address
154+
let smart_contract_account_id =
155+
match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") {
156+
Ok(addr) => addr,
157+
Err(_) => {
158+
return Err(PrecompileFailure::Error {
159+
exit_status: ExitError::Other("Invalid SS58 address".into()),
160+
});
161+
}
162+
};
163+
let amount_sub =
164+
<Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount)
165+
.ok_or(ExitError::OutOfFund)?;
166+
167+
// Create a transfer call from the smart contract to the caller
168+
let transfer_call =
169+
RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death {
170+
dest: account_id.clone().into(),
171+
value: amount_sub.unique_saturated_into(),
172+
});
173+
174+
// Execute the transfer
175+
let transfer_result =
176+
transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into());
177+
178+
if let Err(dispatch_error) = transfer_result {
179+
log::error!(
180+
"Transfer back to caller failed. Error: {:?}",
181+
dispatch_error
182+
);
183+
return Err(PrecompileFailure::Error {
184+
exit_status: ExitError::Other("Transfer back to caller failed".into()),
185+
});
186+
}
187+
188+
Ok(())
189+
}
190+
191+
fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult {
192+
let account_id =
193+
<HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id(
194+
handle.context().caller,
195+
);
196+
197+
// Transfer the amount back to the caller before executing the staking operation
198+
// let caller = handle.context().caller;
199+
let amount = handle.context().apparent_value;
200+
201+
if !amount.is_zero() {
202+
transfer_back_to_caller(&account_id, amount)?;
203+
}
204+
205+
let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into());
206+
match &result {
207+
Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info),
208+
Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error),
209+
}
210+
match result {
211+
Ok(_) => Ok(PrecompileOutput {
212+
exit_status: ExitSucceed::Returned,
213+
output: vec![],
214+
}),
215+
Err(_) => Err(PrecompileFailure::Error {
216+
exit_status: ExitError::Other("Subtensor call failed".into()),
217+
}),
218+
}
219+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[
2+
{
3+
"inputs": [
4+
{
5+
"internalType": "bytes",
6+
"name": "subnetName",
7+
"type": "bytes"
8+
},
9+
{
10+
"internalType": "bytes",
11+
"name": "githubRepo",
12+
"type": "bytes"
13+
},
14+
{
15+
"internalType": "bytes",
16+
"name": "subnetContact",
17+
"type": "bytes"
18+
}
19+
],
20+
"name": "registerNetwork",
21+
"outputs": [],
22+
"stateMutability": "payable",
23+
"type": "function"
24+
},
25+
{
26+
"inputs": [],
27+
"name": "registerNetwork",
28+
"outputs": [],
29+
"stateMutability": "payable",
30+
"type": "function"
31+
}
32+
]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pragma solidity ^0.8.0;
2+
3+
address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000803;
4+
5+
interface ISubnet {
6+
/// Registers a new network without specifying details.
7+
function registerNetwork() external payable;
8+
/// Registers a new network with specified subnet name, GitHub repository, and contact information.
9+
function registerNetwork(bytes subnetName, bytes githubRepo, bytes subnetContact) external payable;
10+
}

runtime/src/precompiles/staking.rs

Lines changed: 77 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,12 @@
2525
// - Precompile checks the result of do_remove_stake and, in case of a failure, reverts the transaction.
2626
//
2727

28-
use frame_system::RawOrigin;
29-
use pallet_evm::{AddressMapping, BalanceConverter, HashedAddressMapping};
30-
use pallet_evm::{
31-
ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult,
32-
};
33-
use sp_core::crypto::Ss58Codec;
28+
use pallet_evm::BalanceConverter;
29+
use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult};
3430
use sp_core::U256;
35-
use sp_runtime::traits::Dispatchable;
36-
use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto};
37-
use sp_runtime::AccountId32;
31+
use sp_runtime::traits::UniqueSaturatedInto;
3832

39-
use crate::precompiles::{get_method_id, get_slice};
33+
use crate::precompiles::{dispatch, get_method_id, get_slice};
4034
use sp_std::vec;
4135

4236
use crate::{Runtime, RuntimeCall};
@@ -78,7 +72,7 @@ impl StakingPrecompile {
7872
amount_staked: amount_sub.unique_saturated_into(),
7973
});
8074
// Dispatch the add_stake call
81-
Self::dispatch(handle, call)
75+
dispatch(handle, call)
8276
}
8377
fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult {
8478
let hotkey = Self::parse_hotkey(data)?.into();
@@ -98,7 +92,7 @@ impl StakingPrecompile {
9892
hotkey,
9993
amount_unstaked: amount_sub.unique_saturated_into(),
10094
});
101-
Self::dispatch(handle, call)
95+
dispatch(handle, call)
10296
}
10397

10498
fn parse_hotkey(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> {
@@ -112,75 +106,75 @@ impl StakingPrecompile {
112106
Ok(hotkey)
113107
}
114108

115-
fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult {
116-
let account_id =
117-
<HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id(
118-
handle.context().caller,
119-
);
120-
121-
// Transfer the amount back to the caller before executing the staking operation
122-
// let caller = handle.context().caller;
123-
let amount = handle.context().apparent_value;
124-
125-
if !amount.is_zero() {
126-
Self::transfer_back_to_caller(&account_id, amount)?;
127-
}
128-
129-
let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into());
130-
match &result {
131-
Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info),
132-
Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error),
133-
}
134-
match result {
135-
Ok(_) => Ok(PrecompileOutput {
136-
exit_status: ExitSucceed::Returned,
137-
output: vec![],
138-
}),
139-
Err(_) => Err(PrecompileFailure::Error {
140-
exit_status: ExitError::Other("Subtensor call failed".into()),
141-
}),
142-
}
143-
}
144-
145-
fn transfer_back_to_caller(
146-
account_id: &AccountId32,
147-
amount: U256,
148-
) -> Result<(), PrecompileFailure> {
149-
// this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address
150-
let smart_contract_account_id =
151-
match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") {
152-
Ok(addr) => addr,
153-
Err(_) => {
154-
return Err(PrecompileFailure::Error {
155-
exit_status: ExitError::Other("Invalid SS58 address".into()),
156-
});
157-
}
158-
};
159-
let amount_sub =
160-
<Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount)
161-
.ok_or(ExitError::OutOfFund)?;
162-
163-
// Create a transfer call from the smart contract to the caller
164-
let transfer_call =
165-
RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death {
166-
dest: account_id.clone().into(),
167-
value: amount_sub.unique_saturated_into(),
168-
});
169-
170-
// Execute the transfer
171-
let transfer_result =
172-
transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into());
173-
174-
if let Err(dispatch_error) = transfer_result {
175-
log::error!(
176-
"Transfer back to caller failed. Error: {:?}",
177-
dispatch_error
178-
);
179-
return Err(PrecompileFailure::Error {
180-
exit_status: ExitError::Other("Transfer back to caller failed".into()),
181-
});
182-
}
183-
184-
Ok(())
185-
}
109+
// fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult {
110+
// let account_id =
111+
// <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id(
112+
// handle.context().caller,
113+
// );
114+
115+
// // Transfer the amount back to the caller before executing the staking operation
116+
// // let caller = handle.context().caller;
117+
// let amount = handle.context().apparent_value;
118+
119+
// if !amount.is_zero() {
120+
// Self::transfer_back_to_caller(&account_id, amount)?;
121+
// }
122+
123+
// let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into());
124+
// match &result {
125+
// Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info),
126+
// Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error),
127+
// }
128+
// match result {
129+
// Ok(_) => Ok(PrecompileOutput {
130+
// exit_status: ExitSucceed::Returned,
131+
// output: vec![],
132+
// }),
133+
// Err(_) => Err(PrecompileFailure::Error {
134+
// exit_status: ExitError::Other("Subtensor call failed".into()),
135+
// }),
136+
// }
137+
// }
138+
139+
// fn transfer_back_to_caller(
140+
// account_id: &AccountId32,
141+
// amount: U256,
142+
// ) -> Result<(), PrecompileFailure> {
143+
// // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address
144+
// let smart_contract_account_id =
145+
// match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") {
146+
// Ok(addr) => addr,
147+
// Err(_) => {
148+
// return Err(PrecompileFailure::Error {
149+
// exit_status: ExitError::Other("Invalid SS58 address".into()),
150+
// });
151+
// }
152+
// };
153+
// let amount_sub =
154+
// <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount)
155+
// .ok_or(ExitError::OutOfFund)?;
156+
157+
// // Create a transfer call from the smart contract to the caller
158+
// let transfer_call =
159+
// RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death {
160+
// dest: account_id.clone().into(),
161+
// value: amount_sub.unique_saturated_into(),
162+
// });
163+
164+
// // Execute the transfer
165+
// let transfer_result =
166+
// transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into());
167+
168+
// if let Err(dispatch_error) = transfer_result {
169+
// log::error!(
170+
// "Transfer back to caller failed. Error: {:?}",
171+
// dispatch_error
172+
// );
173+
// return Err(PrecompileFailure::Error {
174+
// exit_status: ExitError::Other("Transfer back to caller failed".into()),
175+
// });
176+
// }
177+
178+
// Ok(())
179+
// }
186180
}

0 commit comments

Comments
 (0)