Remote State Fetch in Scilla

Bibek Koirala
4 min readOct 26, 2021

With recent release of Zilliqa, remote state read has been made possible. Now we can fetch the state of another contract through the another smart contract. An important thing to note here is that, in terms of gas consumption, it is similar to fetch the state of the own contract and the another contract. This simplifies a lot of things for the smart contract developers in Zilliqa. Now, the Zilliqa smart contracts are closer than ever. I am hoping to see DEXs with multiple smart contracts to manage the funds like what Uniswap, Aave and other De-Fi protocols have been doing in other platforms. The structure of the contract might be different but the functionality will be made possible and as we already know that Scilla/Zilliqa has security by design feature, building De-Fi applications in Zilliqa will be better than ever.

Now lets see how remote state reads work in Scilla. We can fetch the remote state of any contract by using the simple syntax:

x <- & c.f : Remote fetch. Fetch the value of the contract field f at address c, and store it into the local variable x. Note that the type of c must be an address type containing the field f.

Here, the type of c must be an address type containing the field f. We can see in the addresses section that address of any contract can be defined with the contract type with the field that it contains as:

ByStr20 with contract field f1 : t1, field f2 : t2, ... end: A ByStr20 which, when interpreted as a network address, refers to the address of a contract containing the mutable fields f1 of type t1, f2 of type t2, and so on. The contract in question may define more fields than the ones specified in the type, but the fields specified in the type must be defined in the contract.

If you have followed my previous article about the quadratic voting then we will see how we can use remote state read to complete the quad voting based upon the number of tokens user holds.

scilla_version 0import BoolUtils IntUtils ListUtils PairUtilslibrary QVotelet zero = Uint32 0 
let uint128_zero = Uint128 0
let one = Uint32 1
let bool_true = True

let pos_uint128_sqrt: Uint128 -> Uint128 =
fun(i: Uint128) =>
let uint = builtin to_uint128 i in
match uint with
| Some c =>
let sqrt = builtin isqrt c in
let uint = builtin to_uint128 sqrt in
match uint with
| Some cast_int =>
cast_int
| None =>
Uint128 0
end
| None =>
Uint128 0
end
let uint128_credit_to_vote: Uint128 -> Uint128 =
fun(i : Uint128) =>
pos_uint128_sqrt i

let uint32_to_uint128: Uint32 -> Uint128 =
fun(i : Uint32) =>
let res = builtin to_uint128 i in
match res with
| Some c => c
| None => Uint128 0
end
let check_valid_times =
fun(current: BNum) =>
fun(decision: BNum) =>
builtin blt current decision

let get_val =
fun (some_val: Option Uint128) =>
match some_val with
| Some val => val
| None => uint128_zero
end
(* registration codes *)
let owner_register_success_code = Uint32 1
(* vote codes *)
let vote_success_code = Uint32 2
(* events *)
let vote_success_event = {_eventname: "vote_success"; code: vote_success_code}
let owner_register_event_success = {_eventname: "owner_register_success"; code: owner_register_success_code}
(* Error events *)
type Error =
| CodeSenderIsNotOwner
| CodeTransitionOwnerRegisterMutipleCall
| CodeNotInTime
| CodeSenderNotRegisteredToVote
(* Library functions *)
let make_error_event =
fun (result : Error) =>
let result_code =
match result with
| CodeSenderIsNotOwner => Int32 -1
| CodeTransitionOwnerRegisterMutipleCall => Int32 -2
| CodeNotInTime => Int32 -3
| CodeSenderNotRegisteredToVote => Int32 -4
end
in
{ _exception : "Error"; code : result_code }
contract QVote
(
owner: ByStr20,
expiration_block: BNum, (* last block at which votes are accepted *)
name: String, (* decision name *)
description: String,
token: ByStr20 with contract field balances: Map ByStr20 Uint128 end
)
field votes : Map String Uint128 = Emp String Uint128(* Emit Errors *)
procedure EmitError(err : Error)
e = make_error_event err;
throw e
end
(*
@notice: vote on the decision
@param: option: Option to vote for
*)
transition vote(option: String)
blk <- & BLOCKNUMBER;
in_time = check_valid_times blk expiration_block;
match in_time with
| False =>
e = CodeNotInTime;
EmitError e
| True =>
(* get voter balance *)
voter_balance <- & token.balances[_sender];
match voter_balance with
| Some voter_b =>
(* voter balance to uint128 *)
voter_votes_uint128 = uint128_credit_to_vote voter_b;

(* do voting stuff *)
some_prev_vote <- votes[option];
prev_vote = get_val some_prev_vote;
new_vote = builtin add prev_vote voter_votes_uint128;
votes[option] := new_vote;

e = vote_success_event;
event e
| None =>
e = CodeSenderNotRegisteredToVote;
EmitError e
end
end
end

If you see in the constructor of the smart contract then we define the token contract address with the contract field type:

token: ByStr20 with contract field balances: Map ByStr20 Uint128 end

This will help us to fetch the data from the token contract as voter_balance using the syntax as:

voter_balance <- & token.balances[_sender];

In this way, we can enable remote state read in Scilla smart contracts. The code for the whole contract is updated in github in the following repository:

--

--

Bibek Koirala

A blockchain dev | Zilliqa Developer Ambassador | RedChillies Labs, Inc. | Pastel Soft | JS Security Technologies | AART