Elrond Smart Contract

Overview

The smart contract in Rust represents the multi-bridge on the Elrond blockchain.

Executing transactions on Elrond

#[elrond_wasm_derive::contract]
pub trait Multisig { ... }

Available transactions

1. Freezing eGLD in Elrond & sending it to a target account on a target blockchain

	#[payable("EGLD")]
	#[endpoint(freezeSend)]
	fn freeze_send(
			&self, 
			#[payment] value: Self::BigUint, 
			chain_nonce: u64, 
			to: String
		) -> SCResult<Self::BigUint> {
			require!(value > 0, "Value must be > 0");
			let ident = self.insert_event(EventInfo::new(Event::Transfer { 
			chain_nonce, to, value }));
			Ok(ident)
	}

2. Freezing NFT minted in Elrond & sending it to a target account in a target blockchain

	#[payable("*")]
	#[endpoint(freezeSendNft)]
	fn freeze_send_nft(
			&self, 
			#[payment_token] token: TokenIdentifier, 
			#[payment] value: Self::BigUint, 
			#[payment_nonce] nonce: u64, 
			chain_nonce: u64, to: String
		) -> SCResult<Self::BigUint> {
			require!(nonce > 0, "Not an NFT!");
			require!(value == 1, "SFTs not supported!");
			let ident = self.insert_event(EventInfo::new(Event::TransferNft { 
			chain_nonce, to, token, nonce }));
			Ok(ident)
	}

3. Sending wrapped NFTs to another blockchain

	#[payable("*")]
	#[endpoint(withdrawNft)]
	fn withdraw_nft(
			&self, 
			#[payment_token] token: TokenIdentifier, 
			#[payment_nonce] nonce: u64, 
			to: String
		) -> SCResult<Self::BigUint> {
			require!(token == self.nft_token().get(), "Invalid token!");
			require!(nonce > 0, "Not an NFT!");

      let sc_addr = self.blockchain().get_sc_address();
      let mut dat = self.blockchain().get_esdt_token_data(
        	&sc_addr, 
        	&token, nonce);
      let id = dat.uris.remove(0);
      let chain_nonce = u64::top_decode(dat.attributes.into_box()).unwrap();

			self.send().esdt_local_burn(&token, nonce, &1u32.into());

			let ident = self.insert_event(EventInfo::new(Event::UnfreezeNft { 
					chain_nonce, to, id }));

			Ok(ident)
	}

4. Sending wrapped fungible tokens to another blockchain

	#[payable("*")]
	#[endpoint(withdraw)]
	fn withdraw(
			&self, 
			#[payment] value: Self::BigUint, 
			#[payment_token] token: TokenIdentifier, 
			#[payment_nonce] chain_nonce: u64, to: String
		)  -> SCResult<Self::BigUint> {
			require!(value > 0, "Value must be > 0");
			require!(token == self.token().get(), "Invalid token!");

			self.send().esdt_local_burn(&token, chain_nonce, &value);

			let ident = self.insert_event(EventInfo::new(Event::Unfreeze { 
				chain_nonce, to, value }));
			Ok(ident)
	}

5. Helper function: checks whether a user is a validator

#[view(userRole)]
	fn user_role(&self, user: Address) -> UserRole {
		let user_id = self.user_mapper().get_user_id(&user);
		if user_id == 0 {
			UserRole::None
		} else {
			self.get_user_id_to_role(user_id)
		}
	}
fn validate_action(&self, id: Self::BigUint, action: Action<Self::BigUint>) -> 
        SCResult<PerformActionResult<Self::SendApi>> { ... }

1. The smart contract validates whether a signer has the right to sign & returns an error otherwise.

    let caller_address = self.blockchain().get_caller();
		let caller_id = self.user_mapper().get_user_id(&caller_address);
		let caller_role = self.get_user_id_to_role(caller_id);
		require!(
			caller_role.can_sign(),
			"only board members and proposers can propose"
		);

2. Checks that the BFT threshold is reached

if valid_signers_count == min_valid {
			let res = self.perform_action(action);
      action_mapper.remove(&id).unwrap();
 }

3. Subscribes a new validator

#[endpoint(proposeAddValidator)]
	fn propose_add_validator(
			&self, 
			uuid: Self::BigUint, 
			board_member_address: Address
		) -> SCResult<PerformActionResult<Self::SendApi>> {
			self.validate_action(uuid, Action::AddValidator(board_member_address))
		}

4. Unsubscribes a validator

#[endpoint(proposeRemoveUser)]
	fn propose_remove_user(
			&self, uuid: Self::BigUint, 
			user_address: Address
		) -> SCResult<PerformActionResult<Self::SendApi>> {
			self.validate_action(uuid, Action::RemoveUser(user_address))
		}

5. Updates the BFT threshold

#[endpoint(proposeChangeMinValid)]
	fn propose_change_min_valid(
			&self, 
			uuid: Self::BigUint, 
			new_quorum: usize
		) -> SCResult<PerformActionResult<Self::SendApi>>  {
		self.validate_action(uuid, Action::ChangeMinValid(new_quorum))
	}

6. Provides transaction information for the validator upon request

#[endpoint(eventRead)]
	fn event_read(&self, id: Self::BigUint) -> SCResult<EventInfo<Self::BigUint>> {
		//... if all checks passed
		let info = event_mapper.get(&id);
		//... clean & return the info
	}

Last updated

Was this helpful?