[Ethereum] Creating a contract with constructor in ethereumj

contract-deploymentcontract-developmentcontract-invocationethereumj

contract Calculator{
    int result;
    function Calculator(int initialResult){
       result=initialResult;
    }
}

If above is the contract source code I have created then how to call Calculator function using ethereumj API. I have already used

contract.callFunction("Calculator", 10)

But this did not work. Also called

contract.callConstFunction("Calculator", 10)

But this too didn't not work.

And when saw the ABI it had the function type ="constructor" but couldn't find any method that represents calling constructor.

Please suggest if there is any way to call constructor functions?

Best Answer

I finally found a solution. As discussed in the other answer, you need to parse and append the init parameters to the contract code before sending the transaction. I did't found actually an elegant and generic way to parse arguments directly in Java (but it should not hard to do it) so I decoded the ABI syntax into byte[].

First of all copy the StandaloneBlockchain.java and add one method (sorry, I can't find an easy way to do it via extension):

public SolidityContract submitNewContract(String soliditySrc, String contractName, byte[] initParameters) {
    SolidityContractImpl contract = createContract(soliditySrc, contractName);
    submitNewTx(new PendingTx(new byte[0], BigInteger.ZERO, ByteUtil.merge(Hex.decode(contract.getBinary()), initParameters), contract, null));
    return contract;
}

Then in the StandaloneBlockchainSample.java, refer the modified class, modify the sample contract to have 2 parameters in the constructor (I didn't have time to check all types, but it should work without problems) and assign the parameters init values:

// Pretty simple (and probably the most expensive) Calculator
private static final String contractSrc =
        "contract Calculator {" +
        "  int public result;" +  // public field can be accessed by calling 'result' function
        "  string testString;" +
        "  bool testBool;"+
        "  function add(int num) {" +
        "    result = result + num;" +
        "  }" +
        "  function sub(int num) {" +
        "    result = result - num;" +
        "  }" +
        "  function mul(int num) {" +
        "    result = result * num;" +
        "  }" +
        "  function div(int num) {" +
        "    result = result / num;" +
        "  }" +
        "  function clear() {" +
        "    result = 0;" +
        "  }" +
        "  function getString() constant returns (string) { return testString; }" +
        "  function getBool() constant returns (bool) { return testBool; }" +
        "  function Calculator(string _testString, bool _testBool){" +
        "    testString = _testString;" +
        "    testBool = _testBool;" +
        "  }" +
        "}";

[...]

String parameters = "{ 'inputs': [ { 'name': '_test', 'type': 'string' }, { 'name': '_test2', 'type': 'bool' } ] }".replaceAll("'", "\"");
byte[] initParameters = CallTransaction.Function.fromJsonInterface(parameters).encodeArguments("Test", true);
SolidityContract calc = bc.submitNewContract(contractSrc, null, initParameters);
System.out.println("Calculating...");

After that, check the correct value of the parameters with:

assertEqual("Test", (String) calc.callConstFunction("getString")[0]);
assertEqual(true, (boolean) calc.callConstFunction("getBool")[0]);

Note that you need to modify the private assertEqual method inside the class to assert other types aside from the already defined BigInteger. I tested this solution with EthereumJ 1.2.0: maybe next releases will include a better way to pass init values to the constructor, so update the answer if you will be aware of that.