Skip to content

Commit 89c5888

Browse files
committed
feat(relayer): add wasm contract
1 parent bd72100 commit 89c5888

File tree

12 files changed

+2070
-0
lines changed

12 files changed

+2070
-0
lines changed

Diff for: packages/contracts/contracts/relayer/wasm/.gitignore

+9
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

Diff for: packages/contracts/contracts/relayer/wasm/Cargo.toml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
name = "wasm"
3+
version = "0.1.0"
4+
authors = ["[your_name] <[your_email]>"]
5+
edition = "2021"
6+
7+
[dependencies]
8+
ink = { version = "5.0.0", default-features = false }
9+
zink = { git = "https://github.com/scio-labs/zink" }
10+
11+
[dev-dependencies]
12+
ink_e2e = { version = "5.0.0" }
13+
14+
[lib]
15+
path = "lib.rs"
16+
17+
[features]
18+
default = ["std"]
19+
std = [
20+
"ink/std",
21+
]
22+
ink-as-dependency = []
23+
e2e-tests = []

Diff for: packages/contracts/contracts/relayer/wasm/lib.rs

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#![cfg_attr(not(feature = "std"), no_std, no_main)]
2+
3+
#[zink::coating(Ownable2Step[
4+
Error = Error::NotAdmin
5+
])]
6+
#[ink::contract]
7+
mod registration_proxy {
8+
use ink::env::call::{build_call, ExecutionInput, Selector};
9+
use ink::prelude::string::String;
10+
use ink::storage::Mapping;
11+
12+
#[ink(event)]
13+
pub struct Success {
14+
id: u128,
15+
price: Balance,
16+
}
17+
18+
#[derive(Debug)]
19+
#[ink::scale_derive(Encode, Decode, TypeInfo)]
20+
pub enum InnerResult {
21+
Pass(Balance),
22+
Fail(Error),
23+
}
24+
25+
#[ink(storage)]
26+
pub struct RegistrationProxy {
27+
admin: AccountId,
28+
/// Two-step ownership transfer AccountId
29+
pending_admin: Option<AccountId>,
30+
registry_addr: AccountId,
31+
controllers: Mapping<AccountId, ()>,
32+
used_ids: Mapping<u128, ()>,
33+
}
34+
35+
#[derive(Debug)]
36+
#[ink::scale_derive(Encode, Decode, TypeInfo)]
37+
pub enum Error {
38+
/// Caller not allowed to call privileged calls.
39+
NotAdmin,
40+
/// Caller is not approved as controller
41+
NotController,
42+
/// Insufficient balance in the contract
43+
InsufficientBalance,
44+
/// Price exceeds the mentioned cap
45+
TooExpensive,
46+
/// Given Id is already used
47+
DuplicateId,
48+
/// Failed to register
49+
RegisterFailed(u8),
50+
/// Unable to retrieve the price
51+
PriceFetchFailed(u8),
52+
}
53+
54+
impl RegistrationProxy {
55+
#[ink(constructor)]
56+
pub fn new(admin: AccountId, registry_addr: AccountId) -> Self {
57+
Self {
58+
admin,
59+
pending_admin: None,
60+
registry_addr,
61+
controllers: Mapping::default(),
62+
used_ids: Mapping::default(),
63+
}
64+
}
65+
66+
#[ink(message, payable)]
67+
pub fn fund_me(&mut self) -> Result<(), Error> {
68+
Ok(())
69+
}
70+
71+
#[ink(message)]
72+
pub fn withdraw_funds(
73+
&mut self,
74+
account: AccountId,
75+
balance: Balance,
76+
) -> Result<(), Error> {
77+
self.ensure_admin()?;
78+
self.env()
79+
.transfer(account, balance)
80+
.map_err(|_| Error::InsufficientBalance)
81+
}
82+
83+
#[ink(message)]
84+
pub fn register(
85+
&mut self,
86+
id: u128,
87+
name: String,
88+
recipient: AccountId,
89+
years_to_register: u8,
90+
max_fees: Balance,
91+
) -> Result<InnerResult, Error> {
92+
self.ensure_controller()?;
93+
self.ensure_unqiue_id(id)?;
94+
95+
let price = match self.get_name_price(&name, recipient, years_to_register) {
96+
Ok(price) => price,
97+
Err(err) => return Ok(InnerResult::Fail(err)),
98+
};
99+
if price > max_fees {
100+
return Ok(InnerResult::Fail(Error::TooExpensive));
101+
} else if price > self.env().balance() {
102+
return Ok(InnerResult::Fail(Error::InsufficientBalance));
103+
}
104+
105+
const REGISTER_SELECTOR: [u8; 4] = ink::selector_bytes!("register_on_behalf_of");
106+
let result = build_call::<Environment>()
107+
.call(self.registry_addr)
108+
.call_v1()
109+
.exec_input(
110+
ExecutionInput::new(Selector::new(REGISTER_SELECTOR))
111+
.push_arg(name)
112+
.push_arg(recipient)
113+
.push_arg(years_to_register)
114+
.push_arg::<Option<String>>(None)
115+
.push_arg::<Option<String>>(None),
116+
)
117+
.returns::<core::result::Result<(), u8>>()
118+
.transferred_value(price)
119+
.params()
120+
.invoke();
121+
122+
if let Err(e) = result {
123+
return Ok(InnerResult::Fail(Error::RegisterFailed(e)));
124+
}
125+
self.env().emit_event(Success { id, price });
126+
127+
Ok(InnerResult::Pass(price))
128+
}
129+
130+
#[ink(message)]
131+
pub fn set_controller(&mut self, controller: AccountId, enable: bool) -> Result<(), Error> {
132+
self.ensure_admin()?;
133+
134+
if enable {
135+
self.controllers.insert(controller, &());
136+
} else {
137+
self.controllers.remove(controller);
138+
}
139+
140+
Ok(())
141+
}
142+
143+
#[ink(message)]
144+
pub fn is_controller(&self, controller: AccountId) -> bool {
145+
self.controllers.contains(controller)
146+
}
147+
148+
#[ink(message)]
149+
pub fn is_used_id(&self, id: u128) -> bool {
150+
self.used_ids.contains(id)
151+
}
152+
153+
#[ink(message)]
154+
pub fn upgrade_contract(&mut self, code_hash: Hash) {
155+
self.ensure_admin().expect("Not Authorised");
156+
157+
self.env().set_code_hash(&code_hash).unwrap_or_else(|err| {
158+
panic!(
159+
"Failed to `set_code_hash` to {:?} due to {:?}",
160+
code_hash, err
161+
)
162+
});
163+
ink::env::debug_println!("Switched code hash to {:?}.", code_hash);
164+
}
165+
166+
fn get_name_price(
167+
&self,
168+
name: &str,
169+
recipient: AccountId,
170+
years_to_register: u8,
171+
) -> Result<Balance, Error> {
172+
const GET_NAME_PRICE_SELECTOR: [u8; 4] = ink::selector_bytes!("get_name_price");
173+
174+
let result = build_call::<Environment>()
175+
.call(self.registry_addr)
176+
.call_v1()
177+
.exec_input(
178+
ExecutionInput::new(Selector::new(GET_NAME_PRICE_SELECTOR))
179+
.push_arg(name)
180+
.push_arg(recipient)
181+
.push_arg(years_to_register)
182+
.push_arg::<Option<String>>(None),
183+
)
184+
.returns::<core::result::Result<(Balance, Balance, Balance, Option<AccountId>), u16>>()
185+
.params()
186+
.invoke();
187+
188+
let (base_price, premium, _, _) =
189+
result.map_err(|e| Error::PriceFetchFailed((e >> 8) as u8))?;
190+
let price = base_price.checked_add(premium).expect("Overflow");
191+
192+
Ok(price)
193+
}
194+
195+
fn ensure_controller(&self) -> Result<(), Error> {
196+
let caller = self.env().caller();
197+
self.controllers.get(caller).ok_or(Error::NotController)
198+
}
199+
200+
fn ensure_unqiue_id(&mut self, id: u128) -> Result<(), Error> {
201+
if self.used_ids.contains(id) {
202+
return Err(Error::DuplicateId);
203+
}
204+
self.used_ids.insert(id, &());
205+
Ok(())
206+
}
207+
}
208+
}

Diff for: packages/gateway/src/azero-id-relayer.ts

+1
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ class AzeroIdRelayer {
194194
if (data.isErr) {
195195
console.log('Cannot make transaction due to error:', data.err);
196196
// relay failure status back to EVM
197+
if (data.err.type === 'DuplicateId') return
197198
return this.failure(id)
198199
}
199200

0 commit comments

Comments
 (0)