Skip to content

Commit bb59c61

Browse files
committed
add wasm test
1 parent 3e478e8 commit bb59c61

File tree

9 files changed

+812
-379
lines changed

9 files changed

+812
-379
lines changed

evm-tests/bittensor/.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Ignore build artifacts from the local tests sub-crate.
2+
/target/
3+
4+
# Ignore backup files creates by cargo fmt.
5+
**/*.rs.bk
6+
7+
# Remove Cargo.lock when creating an executable, leave it for libraries
8+
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
9+
Cargo.lock

evm-tests/bittensor/Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[workspace]
2+
3+
[package]
4+
name = "bittensor"
5+
version = "0.1.0"
6+
authors = ["[your_name] <[your_email]>"]
7+
edition = "2021"
8+
9+
[dependencies]
10+
ink = { version = "5.1.1", default-features = false }
11+
parity-scale-codec = { version = "3.0.0", default-features = false }
12+
13+
[dev-dependencies]
14+
ink_e2e = { version = "5.1.1" }
15+
16+
[lib]
17+
path = "lib.rs"
18+
19+
[features]
20+
default = ["std"]
21+
std = [
22+
"ink/std",
23+
"parity-scale-codec/std",
24+
]
25+
ink-as-dependency = []
26+
e2e-tests = []

evm-tests/bittensor/lib.rs

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
#![cfg_attr(not(feature = "std"), no_std, no_main)]
2+
3+
use parity_scale_codec::{Compact, CompactAs, Error as CodecError};
4+
5+
#[derive(Debug, Clone)]
6+
pub struct CustomEnvironment;
7+
8+
// pub enum FunctionId {
9+
// GetStakeInfoForHotkeyColdkeyNetuidV1 = 0,
10+
// AddStakeV1 = 1,
11+
// RemoveStakeV1 = 2,
12+
// UnstakeAllV1 = 3,
13+
// UnstakeAllAlphaV1 = 4,
14+
// MoveStakeV1 = 5,
15+
// TransferStakeV1 = 6,
16+
// SwapStakeV1 = 7,
17+
// AddStakeLimitV1 = 8,
18+
// RemoveStakeLimitV1 = 9,
19+
// SwapStakeLimitV1 = 10,
20+
// RemoveStakeFullLimitV1 = 11,
21+
// SetColdkeyAutoStakeHotkeyV1 = 12,
22+
// AddProxyV1 = 13,
23+
// RemoveProxyV1 = 14,
24+
// }
25+
26+
#[ink::chain_extension(extension = 0x1000)]
27+
pub trait RuntimeReadWrite {
28+
type ErrorCode = ReadWriteErrorCode;
29+
30+
#[ink(function = 0)]
31+
fn get_stake_info_for_hotkey_coldkey_netuid(
32+
hotkey: ink::primitives::AccountId,
33+
coldkey: ink::primitives::AccountId,
34+
netuid: u16,
35+
) -> Option<StakeInfo<ink::primitives::AccountId>>;
36+
37+
// #[ink(function = 1)]
38+
// fn add_stake(hotkey: &[u8], netuid: &[u8], amount: &[u8]);
39+
40+
// #[ink(function = 2)]
41+
// fn remove_stake(hotkey: &[u8], netuid: &[u8], amount: &[u8]);
42+
43+
// #[ink(function = 3)]
44+
// fn unstake_all(hotkey: &[u8], netuid: &[u8]);
45+
46+
// #[ink(function = 4)]
47+
// fn unstake_all_alpha(hotkey: &[u8], netuid: &[u8]);
48+
49+
// #[ink(function = 5)]
50+
// fn move_stake(hotkey: &[u8], netuid: &[u8], amount: &[u8]);
51+
52+
// #[ink(function = 6)]
53+
// fn transfer_stake(hotkey: &[u8], netuid: &[u8], amount: &[u8]);
54+
55+
// #[ink(function = 7)]
56+
// fn swap_stake(hotkey: &[u8], netuid: &[u8], amount: &[u8]);
57+
58+
// #[ink(function = 8)]
59+
// fn add_stake_limit(hotkey: &[u8], netuid: &[u8], amount: &[u8]);
60+
61+
// #[ink(function = 9)]
62+
// fn remove_stake_limit(hotkey: &[u8], netuid: &[u8], amount: &[u8]);
63+
64+
// #[ink(function = 10)]
65+
// fn swap_stake_limit(hotkey: &[u8], netuid: &[u8], amount: &[u8]);
66+
67+
// #[ink(function = 11)]
68+
// fn remove_stake_full_limit(hotkey: &[u8], netuid: &[u8], amount: &[u8]);
69+
70+
// #[ink(function = 12)]
71+
// fn set_coldkey_auto_stake_hotkey(coldkey: &[u8], hotkey: &[u8]);
72+
73+
// #[ink(function = 13)]
74+
// fn add_proxy(hotkey: &[u8], netuid: &[u8]);
75+
76+
// #[ink(function = 14)]
77+
// fn remove_proxy(hotkey: &[u8], netuid: &[u8]);
78+
}
79+
80+
#[ink::scale_derive(Encode, Decode, TypeInfo)]
81+
pub enum ReadWriteErrorCode {
82+
ReadFailed,
83+
WriteFailed,
84+
}
85+
86+
impl ink::env::chain_extension::FromStatusCode for ReadWriteErrorCode {
87+
fn from_status_code(status_code: u32) -> Result<(), Self> {
88+
match status_code {
89+
0 => Ok(()),
90+
1 => Err(ReadWriteErrorCode::ReadFailed),
91+
2 => Err(ReadWriteErrorCode::WriteFailed),
92+
_ => Err(ReadWriteErrorCode::ReadFailed),
93+
}
94+
}
95+
}
96+
97+
impl ink::env::Environment for CustomEnvironment {
98+
const MAX_EVENT_TOPICS: usize = 4;
99+
100+
type AccountId = ink::primitives::AccountId;
101+
type Balance = u64;
102+
type Hash = ink::primitives::Hash;
103+
type BlockNumber = u32;
104+
type Timestamp = u64;
105+
106+
type ChainExtension = RuntimeReadWrite;
107+
}
108+
109+
#[ink::scale_derive(Encode, Decode, TypeInfo)]
110+
111+
pub struct NetUid(u16);
112+
impl CompactAs for NetUid {
113+
type As = u16;
114+
115+
fn encode_as(&self) -> &Self::As {
116+
&self.0
117+
}
118+
119+
fn decode_from(v: Self::As) -> Result<Self, CodecError> {
120+
Ok(Self(v))
121+
}
122+
}
123+
124+
impl From<Compact<NetUid>> for NetUid {
125+
fn from(c: Compact<NetUid>) -> Self {
126+
c.0
127+
}
128+
}
129+
130+
impl From<NetUid> for u16 {
131+
fn from(val: NetUid) -> Self {
132+
val.0
133+
}
134+
}
135+
136+
impl From<u16> for NetUid {
137+
fn from(value: u16) -> Self {
138+
Self(value)
139+
}
140+
}
141+
142+
#[ink::scale_derive(Encode, Decode, TypeInfo)]
143+
pub struct AlphaCurrency(u64);
144+
impl CompactAs for AlphaCurrency {
145+
type As = u64;
146+
147+
fn encode_as(&self) -> &Self::As {
148+
&self.0
149+
}
150+
151+
fn decode_from(v: Self::As) -> Result<Self, CodecError> {
152+
Ok(Self(v))
153+
}
154+
}
155+
impl From<Compact<AlphaCurrency>> for AlphaCurrency {
156+
fn from(c: Compact<AlphaCurrency>) -> Self {
157+
c.0
158+
}
159+
}
160+
161+
impl From<AlphaCurrency> for u64 {
162+
fn from(val: AlphaCurrency) -> Self {
163+
val.0
164+
}
165+
}
166+
167+
impl From<u64> for AlphaCurrency {
168+
fn from(value: u64) -> Self {
169+
Self(value)
170+
}
171+
}
172+
#[ink::scale_derive(Encode, Decode, TypeInfo)]
173+
pub struct TaoCurrency(u64);
174+
impl CompactAs for TaoCurrency {
175+
type As = u64;
176+
177+
fn encode_as(&self) -> &Self::As {
178+
&self.0
179+
}
180+
181+
fn decode_from(v: Self::As) -> Result<Self, CodecError> {
182+
Ok(Self(v))
183+
}
184+
}
185+
186+
impl From<Compact<TaoCurrency>> for TaoCurrency {
187+
fn from(c: Compact<TaoCurrency>) -> Self {
188+
c.0
189+
}
190+
}
191+
192+
impl From<TaoCurrency> for u64 {
193+
fn from(val: TaoCurrency) -> Self {
194+
val.0
195+
}
196+
}
197+
198+
impl From<u64> for TaoCurrency {
199+
fn from(value: u64) -> Self {
200+
Self(value)
201+
}
202+
}
203+
204+
#[ink::scale_derive(Encode, Decode, TypeInfo)]
205+
pub struct StakeInfo<AccountId> {
206+
hotkey: AccountId,
207+
coldkey: AccountId,
208+
netuid: Compact<NetUid>,
209+
stake: Compact<AlphaCurrency>,
210+
locked: Compact<u64>,
211+
emission: Compact<AlphaCurrency>,
212+
tao_emission: Compact<TaoCurrency>,
213+
drain: Compact<u64>,
214+
is_registered: bool,
215+
}
216+
217+
#[ink::contract(env = crate::CustomEnvironment)]
218+
mod bittensor {
219+
use super::*;
220+
221+
/// Defines the storage of your contract.
222+
/// Add new fields to the below struct in order
223+
/// to add new static storage fields to your contract.
224+
#[ink(storage)]
225+
pub struct Bittensor {}
226+
227+
impl Bittensor {
228+
/// Constructor that initializes the `bool` value to the given `init_value`.
229+
#[ink(constructor)]
230+
pub fn new() -> Self {
231+
Self {}
232+
}
233+
234+
/// Constructor that initializes the `bool` value to `false`.
235+
///
236+
/// Constructors can delegate to other constructors.
237+
#[ink(constructor)]
238+
pub fn default() -> Self {
239+
Self::new()
240+
}
241+
242+
/// A message that can be called on instantiated contracts.
243+
/// This one flips the value of the stored `bool` from `true`
244+
/// to `false` and vice versa.
245+
#[ink(message)]
246+
pub fn a(&self) -> bool {
247+
true
248+
}
249+
250+
// pub fn get_stake_info_for_hotkey_coldkey_netuid(
251+
// &mut self,
252+
// hotkey: [u8; 32],
253+
// coldkey: [u8; 32],
254+
// netuid: u16,
255+
// ) -> Result<Option<StakeInfo<ink::primitives::AccountId>>, ReadWriteErrorCode> {
256+
// self.env()
257+
// .extension()
258+
// .get_stake_info_for_hotkey_coldkey_netuid(
259+
// hotkey.into(),
260+
// coldkey.into(),
261+
// netuid.into(),
262+
// )
263+
// .map_err(|_e| ReadWriteErrorCode::ReadFailed)
264+
// }
265+
}
266+
267+
/// Unit tests in Rust are normally defined within such a `#[cfg(test)]`
268+
/// module and test functions are marked with a `#[test]` attribute.
269+
/// The below code is technically just normal Rust code.
270+
#[cfg(test)]
271+
mod tests {
272+
/// Imports all the definitions from the outer scope so we can use them here.
273+
use super::*;
274+
275+
/// We test if the default constructor does its job.
276+
#[ink::test]
277+
fn default_works() {
278+
let bittensor = Bittensor::default();
279+
assert_eq!(bittensor.get(), false);
280+
}
281+
282+
/// We test a simple use case of our contract.
283+
#[ink::test]
284+
fn it_works() {
285+
let mut bittensor = Bittensor::new(false);
286+
assert_eq!(bittensor.get(), false);
287+
bittensor.flip();
288+
assert_eq!(bittensor.get(), true);
289+
}
290+
}
291+
292+
/// This is how you'd write end-to-end (E2E) or integration tests for ink! contracts.
293+
///
294+
/// When running these you need to make sure that you:
295+
/// - Compile the tests with the `e2e-tests` feature flag enabled (`--features e2e-tests`)
296+
/// - Are running a Substrate node which contains `pallet-contracts` in the background
297+
#[cfg(all(test, feature = "e2e-tests"))]
298+
mod e2e_tests {
299+
/// Imports all the definitions from the outer scope so we can use them here.
300+
use super::*;
301+
302+
/// A helper function used for calling contract messages.
303+
use ink_e2e::ContractsBackend;
304+
305+
/// The End-to-End test `Result` type.
306+
type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
307+
308+
/// We test that we can upload and instantiate the contract using its default constructor.
309+
#[ink_e2e::test]
310+
async fn default_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
311+
// Given
312+
let mut constructor = BittensorRef::default();
313+
314+
// When
315+
let contract = client
316+
.instantiate("bittensor", &ink_e2e::alice(), &mut constructor)
317+
.submit()
318+
.await
319+
.expect("instantiate failed");
320+
let call_builder = contract.call_builder::<Bittensor>();
321+
322+
// Then
323+
let get = call_builder.get();
324+
let get_result = client.call(&ink_e2e::alice(), &get).dry_run().await?;
325+
assert!(matches!(get_result.return_value(), false));
326+
327+
Ok(())
328+
}
329+
330+
/// We test that we can read and write a value from the on-chain contract.
331+
#[ink_e2e::test]
332+
async fn it_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
333+
// Given
334+
let mut constructor = BittensorRef::new(false);
335+
let contract = client
336+
.instantiate("bittensor", &ink_e2e::bob(), &mut constructor)
337+
.submit()
338+
.await
339+
.expect("instantiate failed");
340+
let mut call_builder = contract.call_builder::<Bittensor>();
341+
342+
let get = call_builder.get();
343+
let get_result = client.call(&ink_e2e::bob(), &get).dry_run().await?;
344+
assert!(matches!(get_result.return_value(), false));
345+
346+
// When
347+
let flip = call_builder.flip();
348+
let _flip_result = client
349+
.call(&ink_e2e::bob(), &flip)
350+
.submit()
351+
.await
352+
.expect("flip failed");
353+
354+
// Then
355+
let get = call_builder.get();
356+
let get_result = client.call(&ink_e2e::bob(), &get).dry_run().await?;
357+
assert!(matches!(get_result.return_value(), true));
358+
359+
Ok(())
360+
}
361+
}
362+
}

0 commit comments

Comments
 (0)