← Back to list

Zilliqa — Accepting Payments in Scilla Smart Contracts

Learn how to accept payments in your Scilla contract, refund excess amount paid, and transfer the payment to the owner of the contract

Wei-Meng Lee in CryptoStars · 2022-09-04 11:04 · 6 claps · 10.1 min read paywalled
#zilliqa #scilla #payments #accept #smart-contracts
Open on Medium ↗
Wiki topics: CRY · Crypto & Web3 FIN · Fintech & Banking

Zilliqa — Accepting Payments in Scilla Smart Contracts

Learn how to accept payments in your Scilla contract, refund excess amount paid, and transfer the payment to the owner of the contract

Photo by Alexander Mils on Unsplash

Photo by Alexander Mils on Unsplash

In my earlier article on the Zilliqa blockchain, I talked about how to create smart contracts using the Scilla language. One common task that smart contracts deal with is the transfer of cryptos. In this article, I will show you how you can program your Scilla smart contract to accept Zil payments, and how to transfer excess amount to senders and transfer the balance of the contract back to the owner of the contract.

Making the Contract Accept Payments

For this article, I will be using the smart contract that I have used in my earlier article:

[embed]Writing Smart Contracts for the Zilliqa Blockchain using the Scilla Programming Language Learn Scilla by Exampleblog.cryptostars.is

Load up the Neo Savant IDE (https://ide.zilliqa.com/) and using the contract written in the previous article, add the following statements in bold:

scilla_version 0
import IntUtils
(* Library definition *)
library MyLibrary
(*Constants in library*)
let failure  = Int32 -1
let document_existed    = Int32 1
let document_notarized  = Int32 2
let document_exists     = Int32 3
let document_not_found  = Int32 4
contract DocumentNotarizer (owner: ByStr20)
(* owner is an immutable variable *)
(* Mutable Field *)
field documents: Map String BNum = Emp String BNum
transition notarize (document : String)
  is_owner = builtin eq owner _sender;
  match is_owner with
  | False =>
    e = { _eventname : "notarize_fail"; 
          code : failure; 
          reason : "Only the owner can notarize"};
    event e
  | True =>
      document_already_exists <- exists documents[document];
      match document_already_exists with
      | True =>
        e = { _eventname : "notarize_fail"; 
              code : document_existed; 
              reason : "Document already existed"};
        event e
      | False =>
        blk <- &BLOCKNUMBER;
        documents[document] := blk;
        e = { _eventname : "notarize_success"; 
              code : document_notarized; 
              reason : "Document notarized"};
        event e
      end
  end
end
transition checkDocument (document: String)
  (* Accept the payment *)
  accept;

  doc_exists <- exists documents[document];
  match doc_exists with
  | True =>
    block_number <- documents[document];
    e = { _eventname : "checkDocument_success"; 
          code : document_exists; 
          reason : "Document exists!";
          result : block_number
        };
    event e
  | False =>  
    e = { _eventname : "checkDocument_fail"; 
          code : document_not_found; 
          reason : "Document does not exist" 
        };
    event e
  end
end

The accept statement indicates that the contract is able to accept payments from callers.

Deploy the contract onto the test net.

Calling the checkDocument Transition with a Payment

With the contract deployed, click the checkDocument button and fill in the details as shown below:

  • Amount1000000000000 (1 followed by twelve 0's, which is equivalent to 1 Zil)
  • String* Hello, world!* (or anything, it doesn't really matter)

Click the Call transition button.

Once the transaction is confirmed, you will see the following. Click on the transaction ID:

This will open up the Zilliqa blockchain explorer. Observe that your account is sending 1 Zil to the contract.

Click on the contract address (see above). You will observe that the contract now holds the 1 Zil that was sent to it:

If a contract does not have the accept statement and an account sends funds to it, no funds will be accepted by the contract and the sending account will not have the funds deducted from its account.

Accepting Exact Payment

While your contract can accept payments of any amount, it is often useful to be able to specify exactly how much payment your contract needs in order to process certain operations.

Add the following statements to the contract:

scilla_version 0
import IntUtils
(* Library definition *)
library MyLibrary
(*Constants in library*)
let failure  = Int32 -1
let document_existed    = Int32 1
let document_notarized  = Int32 2
let document_exists     = Int32 3
let document_not_found  = Int32 4
contract DocumentNotarizer (owner: ByStr20)
(* owner is an immutable variable *)
(* Mutable Field *)
field documents: Map String BNum = Emp String BNum
transition notarize (document : String)
  ...
end
transition checkDocument (document: String)
  (* Our expected fee *)
  expected_amount = Uint128 1000000000000;   (* 1 Zil *)
  (* Check to see if the amt sent is equal to the expected amt *)
  correct_amount_sent = builtin eq _amount expected_amount;
  match correct_amount_sent with
  | False =>
    e = { _eventname : "incorrect_amount"; 
          code : failure; 
          reason : "Incorrect amount sent"
        };
    event e
  | True =>
    (* Accept the payment *)
    accept;

    doc_exists <- exists documents[document];
    match doc_exists with
    | True =>
      block_number <- documents[document];
      e = { _eventname : "checkDocument_success"; 
            code : document_exists; 
            reason : "Document exists!";
            result : block_number
          };
      event e
    | False =>  
      e = { _eventname : "checkDocument_fail"; 
            code : document_not_found; 
            reason : "Document does not exist" 
          };
      event e
    end
  end
end

In the above statements, you:

  • Set the expected_amount payable to be 1 Zil
  • Checked to see if the amount sent to the contract is exactly 1 Zil. If it is not, an event is fired indicating that the amount is incorrect. If the amount sent is exactly 1 ZIL, then the payment is accepted by the contract

Re-deploy the contract.

Once the contract is deployed, call the checkDocument transition with 2 Zil (2 followed by twelve 0’s). Then, click the Call transition button:

After the transaction is confirmed, you will see the event indicating that the incorrect amount was sent:

At this moment, the contract will only accept the fund if it is sent exactly 1 Zil, else it will reject the fund.

Obviously, if the user sends more than what you expected, the contract should accept it (who doesn’t want more money???). And so in this next step, you will modify the contract to check if the minimum amount expected is sent by the user:

transition checkDocument (document: String)
  (* Our expected fee *)
  expected_amount = Uint128 1000000000000;   (* 1 Zil *)
  (* ---comment out the following line--- *)
  (* correct_amount_sent = builtin eq _amount expected_amount; *)
  (* if _amount is greater than or equal to expected_amount *)
  correct_amount_sent = uint128_ge _amount expected_amount;  

  match correct_amount_sent with
  | False =>
    e = { _eventname : "incorrect_amount"; 
          code : failure; 
          reason : "Incorrect amount sent"
        };
    event e
  | True =>
    (* Accept the payment *)
    accept;    

    doc_exists <- exists documents[document];
    match doc_exists with
    | True =>
      block_number <- documents[document];
      e = { _eventname : "checkDocument_success"; 
            code : document_exists; 
            reason : "Document exists!";
            result : block_number
          };
      event e
    | False =>  
      e = { _eventname : "checkDocument_fail"; 
            code : document_not_found; 
            reason : "Document does not exist" 
          };
      event e
    end
  end
end

The above bolded statement checks if the amount sent is greater than or equal to (**uint128_ge**) the expected amount.

You can now redeploy the contract.

Once the contract is deployed, call the checkDocument transition with 2 Zil (2 followed by 12 0’s). Then, click the Call transition button:

After the transaction is confirmed, you can explore the details of the contract on the Zilliqa blockchain explorer. You will see that the contract has a balance of 2 Zil:

Processing Refund

While it is good to accept more than what you expect, it would be more ethical for your contract to accept only what you expected and refund all the excess amount back to the sender.

Add the following statements in bold to the contract:

scilla_version 0
import IntUtils
(* Library definition *)
library MyLibrary
(*Constants in library*)
let failure  = Int32 -1
let document_existed    = Int32 1
let document_notarized  = Int32 2
let document_exists     = Int32 3
let document_not_found  = Int32 4
(* Define a library function named one_msg to construct a list consisting of one message *)
let one_msg =
  fun (msg : Message) =>
  let nil_msg = Nil {Message} in
    Cons {Message} msg nil_msg
contract DocumentNotarizer (owner: ByStr20)
(* owner is an immutable variable *)
(* Mutable Field *)
field documents: Map String BNum = Emp String BNum
transition notarize (document : String)
  is_owner = builtin eq owner _sender;
  match is_owner with
  | False =>
      e = { _eventname : "notarize_fail"; 
            code : failure; 
            reason : "Only the owner can notarize"};
      event e
  | True =>
      document_already_exists <- exists documents[document];
      match document_already_exists with
      | True =>
        e = { _eventname : "notarize_fail"; 
              code : document_existed; 
              reason : "Document already existed"};
        event e
      | False =>
        blk <- &BLOCKNUMBER;
        documents[document] := blk;
        e = { _eventname : "notarize_success"; 
              code : document_notarized; 
              reason : "Document notarized"};
        event e
      end
  end
end
(*---Procedure to check if excess amount has been sent---*)
procedure refund_excess( expected_amount: Uint128 )
  (* if _amount > expected_amount  *)
  sent_more_than_expected = uint128_gt _amount expected_amount;
  match sent_more_than_expected with
  | True =>
    (*---process refund---*)
    (* refund = _amount - expected_amount *)
    amount_to_refund = builtin sub _amount expected_amount;
    (* construct the message *)
    msg = { _tag : ""; 
            _recipient: _sender; 
            _amount: amount_to_refund };
    (* insert the message into a list *)
    msgs = one_msg msg;
    (* send the message *)
    send msgs
  | False =>
    accept
  end
end
transition checkDocument (document: String)
  (* Our expected fee *)
  expected_amount = Uint128 1000000000000;   (* 1 Zil *)
  (*  correct_amount_sent = builtin eq _amount expected_amount;*)
  (* if _amount is greater than or equal to expected_amount  *)
  correct_amount_sent = uint128_ge _amount expected_amount;

  match correct_amount_sent with
  | False =>
    e = { _eventname : "incorrect_amount"; 
          code : failure; 
          reason : "Incorrect amount sent"
        };
    event e
  | True =>
    (* Accept the payment *)
    accept;
    (* check if excess amount has been sent and process refund *)
    refund_excess expected_amount;

    doc_exists <- exists documents[document];
    match doc_exists with
    | True =>
      block_number <- documents[document];
      e = { _eventname : "checkDocument_success"; 
            code : document_exists; 
            reason : "Document exists!";
            result : block_number
          };
      event e
    | False =>  
      e = { _eventname : "checkDocument_fail"; 
            code : document_not_found; 
            reason : "Document does not exist" 
          };
      event e
    end
  end
end

In Scilla, the **send command is used to send messages to other accounts so that you can either invoke a transition on another contract, or to transfer money to another account. A message must contain the compulsory fields — `_tag**(of typeString`; name of the transition if you are invoking a transition, or **“” if you are transferring funds), `_recipient**(of typeByStr20`), and **_amount**.

You can now redeploy the contract.

Once the contract is deployed, call the checkDocument transition with 2 Zil (2 followed by 12 0’s). Click the Call transition button.

If you examine the transaction details on the Zilliqa blockchain explorer, you will see that 2 Zil was sent to the contract:

At the bottom of the page, you will see that the contract sent back 1 Zil to the sender:

When you examine the details of the contract, you will see that the contract has a balance of 1 Zil:

Remitting the Money Received to the Owner of the Contract

So far you have programmed the smart contract to accept payments, and that in the past few examples you have seen how the contract has net positive amount of funds in it. Except that….the funds are locked into the contract forever! When funds are transferred into the contract, you need to ensure that there are ways for the funds to be transferred out of the contract — if you don’t do this, the funds are locked forever and there is absolutely no way for them to be recovered.

In the following statements, you will write the code to transfer the payments sent to a contract as soon as it is received, to the owner of the contract.

Add the following statements in bold to the contract:

transition checkDocument (document: String)
  (* Our expected fee *)
  expected_amount = Uint128 1000000000000;   (* 1 Zil *)
  (*  correct_amount_sent = builtin eq _amount expected_amount; *)    
  (* if _amount is greater than or equal to expected_amount  *)
  correct_amount_sent = uint128_ge _amount expected_amount;  

  match correct_amount_sent with
  | False =>
      e = { _eventname : "incorrect_amount"; 
            code : failure; 
            reason : "Incorrect amount sent"
          };
      event e
  | True =>
    (* Accept the payment *)
    accept;

    (* transfer the expected amount to the owner *)
    msg = { _tag : ""; 
            _recipient: owner; 
            _amount: expected_amount };
    msgs = one_msg msg;
    send msgs;
    (* check if excess amount has been sent and process refund *)
    refund_excess expected_amount;    

    doc_exists <- exists documents[document];
    match doc_exists with
    | True =>
      block_number <- documents[document];
      e = { _eventname : "checkDocument_success"; 
            code : document_exists; 
            reason : "Document exists!";
            result : block_number
          };
      event e
    | False =>  
      e = { _eventname : "checkDocument_fail"; 
            code : document_not_found; 
            reason : "Document does not exist" 
          };
      event e
    end
  end
end

You can now redeploy the contract. For this example, you shall use two accounts for testing — Account 0 and Account 1.

Once the contract has been successfully deployed, record the amount of Zil in the two accounts that you have (the values below are the balances of my accounts):

  • Account 0: 350.37
  • Account 1: 491.24

Using Account 1, call the checkDocument transition with the following values:

When the transaction is confirmed, you can check the contract’s details using the Zilliqa blockchain explorer. You will observe that the contract has a balance of 0 Zil:

Check the account balances of Account 0 and Account 1:

  • Account 0: 351.37 (an increment of 1 Zil, which was transferred by the contract)
  • Account 1: 489.36 (an reduction of about 1.88 Zil, of which 1 Zil was used to pay to the contract and 0.88 is the gas fee)

[embed]Join Medium with my referral link - Wei-Meng Lee Read every story from Wei-Meng Lee (and thousands of other writers on Medium). Your membership fee directly supports…weimenglee.medium.com

Summary

I hope this article has covered all the common scenarios in which your Scilla smart contract have to deal with whenever payment is concerned. Stay tuned for more interesting articles on Scilla and Zilliqa!


메타데이터
post_id
e46410dfe74c
slug
zilliqa-accepting-payments-in-scilla-smart-contracts-e46410dfe74c
url
https://medium.com/cryptostars/zilliqa-accepting-payments-in-scilla-smart-contracts-e46410dfe74c
canonical_url
https://medium.com/cryptostars/zilliqa-accepting-payments-in-scilla-smart-contracts-e46410dfe74c
author_url
https://medium.com/@weimenglee
status
ok
fetched_at
2026-07-26 17:05:25