1

When calling a smart contract entrypoint, it is required to pass the needed entrypoint parameters. This is done through a message that must be sent in JSON/Pairs format. My doubt is: Is there a formula to build these parameter messages to be passed to smart contract entrypoints?

For example, when calling the addCustomer entrypoint from SmartPy Explorer:

enter image description here

enter image description here

It generates the correspondent Michelson message:

(Left (Left (Pair (Pair 8000000 "0001") (Pair Älice"99999897))))

Is there a formula to calculate programmatically the number of LEFTs and RIGHTs, maybe according to the number of entrypoints or even parameters?

enter image description here

Any information will be much appreciated. Thanks in advance.

  • I have created once a routine that worked well to do this, until the protocol change to Carthage. Then, apparently the order of the LEFTs and RIGHTs changed somehow. I would like to know the recommended way to do it, so I can avoid future problems after new protocol upgrades. – Luiz Milfont Jun 19 at 1:34
  • Carthage brought no change regarding the treatment of entrypoints, you see a difference because you are calling different contracts. – Raphaël Cauderlier Jun 19 at 9:08
1

There are two ways to indicate which entrypoint you want to call:

  • call it by its name using the --entrypoint command-line argument of tezos-client transfer or the entrypoint optional parameter of the corresponding RPCs. If you do this, you can simply pass the argument of the expected entrypoint type without the leading Lefts and Rights.

  • call it by its position in the parameter type of the smart contract: the parameter type can be seen as a binary tree whose nodes are labeled by or, you can indicate an entrypoint by a giving the path composed of Lefts and Rights from the root of this tree. An example is given in the Michelson documentation.

| improve this answer | |
1

You need to follow the path to the corresponding entry point in the Michelson parameter type. ‘Left’ when you go to the left branch of a ‘or’ and ‘Right’ for the right branch.

| improve this answer | |
1

If we could do a logic schematics for this specific case, do you sirs think it could be described like this? :

parameter (or (or (pair %addCustomer (pair (mutez %balance) (string %id)) (pair (string %name) (nat %phoneNumber))) (string %removeCustomer)) (or (pair %transfer (mutez %amount) (pair (string %idFrom) (string %idTo))) (pair %updateBalance (mutez %amount) (string %id)))) :

enter image description here

| improve this answer | |
  • 1
    A few remarks: 1. I like it 2. update is update Balance 3. I don't fully understand the UNIT but otherwise, it seems pretty accurate to me. – FFF Jun 19 at 16:04
  • Thank you very much, sir. I think this kind of visual thing is important for new Tezos developers. It definetely helps to understand some details. I will edit the image to match your suggestions. – Luiz Milfont Jun 19 at 16:22
0

After this important discussion I've ended with a functional solution which I am sharing here with whoever might find useful. This code is in Java. Details of it might be found on TezosJ_plainJava github repository.

The method "solvePair" transforms recursively a "Pair" of parameters into the expected JSON/Pairs format that is needed to be passed to Tezos smart contracts entrypoints, making it easy for developers to interact with Tezos blockchain.

Example:

INPUT:

Pair : (([1000000],[2000000,003]),([001],[005,002]))

Datatypes : [ "int", "int", "String", "String", "String", "String" ]

OUTPUT:

{ "prim": "Pair", "args": [ { "prim": "Pair", "args": [ { "int": "1000000" }, { "prim": "Pair", "args": [ { "int": "2000000" }, { "string": "003" } ] } ] }, { "prim": "Pair", "args": [ { "string": "001" }, { "prim": "Pair", "args": [ { "string": "005" }, { "string": "002" } ] } ] } ] }

Here are the routines:

   private Object solvePair(Object pair, List datatypes) throws Exception
   {
         
      Object result = null;
      
      // Extract and check contents.
      if (hasPairs((Pair) pair) == false)
      {
         // Here we've got List in both sides. But they might have more than one element.
         Object jsonLeft  = ((Pair) pair).getLeft() == null ? null : toJsonFormat((List)((Pair) pair).getLeft(), datatypes, 0);
         Object jsonRight = ((Pair) pair).getRight() == null ? null : toJsonFormat((List)((Pair) pair).getRight(), datatypes, ((Pair) pair).getLeft() == null ? 0 : ((List)((Pair) pair).getLeft()).size() );
         
         // Test if there is only one parameter.
         if (jsonLeft == null)
            if (jsonRight == null)
               throw new Exception("Pair cannot be (null, null)");
            else
               return jsonRight;
         else if (jsonRight == null)
            return jsonLeft;

         // Build json outter pair.
         JSONObject jsonPair = new JSONObject();
         jsonPair.put("prim", "Pair");
         
         // Create pair contents array.
         JSONArray pairContents = new JSONArray();
         pairContents.put(jsonLeft);
         pairContents.put(jsonRight);
         jsonPair.put("args", pairContents);
         
         return jsonPair;
      }
      else
      {
         Object jsonLeft = solvePair(((Pair<Pair, List>) pair).getLeft(), datatypes);
         Object jsonRight = solvePair(((Pair<Pair, List>) pair).getRight(), datatypes.subList( countPairElements((Pair) ((Pair) pair).getLeft()), datatypes.size()) );
         
         // Build json outter pair.
         JSONObject jsonPair = new JSONObject();
         jsonPair.put("prim", "Pair");
         
         // Create pair contents array.
         JSONArray pairContents = new JSONArray();
         pairContents.put(jsonLeft);
         pairContents.put(jsonRight);
         jsonPair.put("args", pairContents);
         
         return jsonPair;
      }

   }

   private Integer countPairElements(Pair pair)
   {
      Integer leftCount = 0;
      Integer rightCount = 0;
      
      Object left = pair.getLeft();
      Object right = pair.getRight();

      if(left instanceof Pair)
      {
         leftCount = countPairElements((Pair) left);
      }
      else
      {
         leftCount = ((List)left).size();
      }
      
      if(right instanceof Pair)
      {
         rightCount = countPairElements((Pair) right);
      }
      else
      {
         rightCount = ((List)right).size();
      }

      return leftCount+rightCount;

   }
   
   private Boolean hasPairs(Pair pair)
   {
      Object left = pair.getLeft();
      Object right = pair.getRight();
      
      if( (left instanceof Pair) || (right instanceof Pair) )
      {
         return true;
      }
      else
      {
         return false;
      }
   }
   
   private JSONObject toJsonFormat(List list, List datatypes, Integer firstElement)
   { 
      JSONArray result = new JSONArray();
            
      for(int i=0;i<list.size();i++)
      {
         JSONObject element = new JSONObject();
         element.put((String) datatypes.get(firstElement + i), list.get(i));
       
         // Add element to array.
         result.put(element);
      }
               
      if (result.length() > 1)
      {
         // Wrap json result in outter pair.
         JSONObject jsonPair = new JSONObject();
         jsonPair.put("prim", "Pair");
         jsonPair.put("args", result);   
         
         return jsonPair;
      }
      else
      {
         return (JSONObject)result.get(0);
      }

   }

And if you wish to build a Pair object from a String[] parameter array, you might use the routine below.

INPUT: empty JSONObject, empty Pair, List of your parameters values (like String[] {"param1", "param2", "param3"} ).

OUTPUT: (param1,(param2,param3))

   private Pair buildParameterPairs(JSONObject jsonObj, Pair pair, List<String> parameters,
                                    String[] contractEntryPointParameters,
                                    Boolean doSolveLeft) throws Exception
   {
      
      // Test parameters validity.
      if (parameters.isEmpty())
      {
         throw new Exception("Missing parameters to pass to contract entrypoint");
      }

      List<String> left = new ArrayList<String>();
      List<String> right = new ArrayList<String>();
      Pair newPair = null;
      
      if(parameters.size() == 1)
      {         
         // If number of parameters is only 1.
         newPair = new MutablePair<>(null, new ArrayList<String>(Arrays.asList(parameters.get(0))));
      }
      else 
      {

         if (pair == null)
         {
            Integer half = ( Math.abs(parameters.size() / 2) );

            left = parameters.subList(0, half);
            right = parameters.subList(half, parameters.size());

            newPair = new MutablePair<>(left, right);
                        
         }
         else
         {
            List<String> newList;
            
            if (doSolveLeft == true)
            {
               newList = ((List<String>) pair.getLeft());
            }
            else
            {
               newList = ((List<String>) pair.getRight());
            }
            
            Integer half = ( Math.abs( newList.size() / 2) );

            left = newList.subList(0, half);
            right = newList.subList(half, newList.size());

            newPair = new MutablePair<>(left, right);
            
         }

         
         if (  (((List)newPair.getRight()).size() > 2) || (((List)newPair.getLeft()).size() > 2)  )
         {

               newPair = new MutablePair<>(buildParameterPairs(jsonObj, newPair, parameters, contractEntryPointParameters, true),
                                           buildParameterPairs(jsonObj, newPair, parameters, contractEntryPointParameters, false));

         }
         else
         {
            return newPair;
         }

      }

      return newPair;

   }   
   
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.