polygon-edge icon indicating copy to clipboard operation
polygon-edge copied to clipboard

Custom contract predeployment

Open dbrajovic opened this issue 2 years ago • 1 comments

Description

This PR adds support for custom SC predeployment.

Changes include

  • [ ] Bugfix (non-breaking change that solves an issue)
  • [ ] Hotfix (change that solves an urgent issue, and requires immediate attention)
  • [x] New feature (non-breaking change that adds functionality)
  • [ ] Breaking change (change that is not backwards-compatible and/or changes current functionality)

Checklist

  • [x] I have assigned this PR to myself
  • [x] I have added at least 1 reviewer
  • [x] I have added the relevant labels
  • [x] I have updated the official documentation
  • [x] I have added sufficient documentation in code

Testing

  • [x] I have tested this code with the official test suite
  • [x] I have tested this code manually

Manual tests

Manual testing was done with varying Smart Contracts that have different constructor params on a running Edge chain.

Documentation update

Relevant documentation PR

Additional comments

Fixes EDGE-491 Fixes EDGE-492

dbrajovic avatar May 06 '22 12:05 dbrajovic

Codecov Report

Merging #536 (9505935) into develop (66897d8) will decrease coverage by 0.40%. The diff coverage is 29.65%.

:exclamation: Current head 9505935 differs from pull request most recent head 973243a. Consider uploading reports for the commit 973243a to get more accurate results

@@             Coverage Diff             @@
##           develop     #536      +/-   ##
===========================================
- Coverage    52.70%   52.30%   -0.41%     
===========================================
  Files          130      132       +2     
  Lines        17146    17436     +290     
===========================================
+ Hits          9037     9120      +83     
- Misses        7461     7651     +190     
- Partials       648      665      +17     
Impacted Files Coverage Δ
helper/predeployment/predeployment.go 0.00% <0.00%> (ø)
state/executor.go 3.86% <0.00%> (-0.08%) :arrow_down:
state/txn.go 19.76% <0.00%> (-0.10%) :arrow_down:
helper/predeployment/argparser.go 55.48% <55.48%> (ø)
syncer/client.go 61.79% <0.00%> (-1.42%) :arrow_down:

:mega: We’re building smart automated test selection to slash your CI/CD build times. Learn more

codecov[bot] avatar Aug 02 '22 14:08 codecov[bot]

I tried to pre deploy contract according to the description of the flows in doc https://github.com/maticnetwork/matic-docs/pull/1028.

My contract code is here:

pragma solidity ^0.8.7;

contract Greeter {
  struct MyStruct {
    string name;
    int256 number;
  }

  MyStruct[] private greetings;
  int256[] nums;
  string str;

  constructor(
    MyStruct[] memory myGreetings,
    int256[] memory myArgNums,
    string memory myStr
  ) {
    for(uint256 i = 0; i < myGreetings.length; i++) {
        greetings.push(myGreetings[i]);
    }

    nums = myArgNums;
    str = myStr;
  }

  function getGreeting(uint256 index) public view returns (MyStruct memory) {
    return greetings[index];
  }

  function getNum(uint256 index) public view returns (int256) {
    return nums[index];
  }

  function getStr() public view returns (string memory) {
    return str;
  }
}

And the script for predeployment I ran is here:

polygon-edge genesis predeploy --chain ./genesis.json --artifacts-path ./Greeter.json --predeploy-address "0x1110" --constructor-args "[[\"value 1\", 123],[\"value 2\", 456],[\"value 3\", 789]]" --constructor-args "[1,2,3,4]" --constructor-args "hello, world"

(I confirmed I pulled latest commit and build the program before running this command.)

According the section Combination of all types as arguments in the doc, the way to give arguments into --constructor-args looks correct.

I tried to fetch contract state after I ran genesis predeploy command and started nodes.

The script to get contract state is as follows:

import { ethers } from "hardhat";
import { Greeter } from "../types/Greeter";

async function main() {
  console.log("Check current contract information");

  const greeter = (await ethers.getContractAt(
    "Greeter",
    "0x0000000000000000000000000000000000001110"
  )) as Greeter;

  console.log("greetings[0] => ", await greeter.getGreeting(0));
  console.log("greetings[1] => ", await greeter.getGreeting(1));
  console.log("greetings[2] => ", await greeter.getGreeting(2));

  console.log("nums[0] => ", await greeter.getNum(0));
  console.log("nums[1] => ", await greeter.getNum(1));
  console.log("nums[2] => ", await greeter.getNum(2));
  console.log("nums[3] => ", await greeter.getNum(3));

  console.log("str => ", await greeter.getStr());
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

But I've got the following result:

Generating typings for: 0 artifacts in dir: types for target: ethers-v5
Successfully generated 3 typings!
Successfully generated 3 typings for external artifacts!
Check current contract information
ProviderError: execution was reverted
    at HttpProvider.request (/home/kourin/polygon/polygon_edge/staking-contracts/node_modules/hardhat/src/internal/core/providers/http.ts:49:19)
    at LocalAccountsProvider.request (/home/kourin/polygon/polygon_edge/staking-contracts/node_modules/hardhat/src/internal/core/providers/accounts.ts:187:34)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async EthersProviderWrapper.send (/home/kourin/polygon/polygon_edge/staking-contracts/node_modules/@nomiclabs/hardhat-ethers/src/internal/ethers-provider-wrapper.ts:13:20)

When I commented out the section of getting struct values, it worked.

//   console.log("greetings[0] => ", await greeter.getGreeting(0));
//   console.log("greetings[1] => ", await greeter.getGreeting(1));
//   console.log("greetings[2] => ", await greeter.getGreeting(2));

  console.log("nums[0] => ", await greeter.getNum(0));
  console.log("nums[1] => ", await greeter.getNum(1));
  console.log("nums[2] => ", await greeter.getNum(2));
  console.log("nums[3] => ", await greeter.getNum(3));

  console.log("str => ", await greeter.getStr());
...
Generating typings for: 0 artifacts in dir: types for target: ethers-v5
Successfully generated 3 typings!
Successfully generated 3 typings for external artifacts!
Check current contract information
nums[0] =>  BigNumber { value: "1" }
nums[1] =>  BigNumber { value: "2" }
nums[2] =>  BigNumber { value: "3" }
nums[3] =>  BigNumber { value: "4" }
str =>  hello, world

This means error happened when getting struct state. You may think the way to get struct state is wrong. But I tried to deploy contract in the normal way with the following script. The arguments of constructor are the same with what I gave in genesis predeploy command.

import { ContractFactory } from "ethers";
import { ethers } from "hardhat";
import { Greeter } from "../types/Greeter";

async function main() {
  console.log("Check current contract information");

  const greeterFactory = (await ethers.getContractFactory(
    "Greeter",
  ))

  const greeter = await greeterFactory.deploy(
    // myGreetings
    [
      {
        name: "value 1",
        number: 123,
      },
      {
        name: "value 2",
        number: 456,
      },
      {
        name: "value 3",
        number: 789
      }
    ],
    // myArgNums
    [1, 2, 3, 4],
    // myStr,
    "hello, world"
  ) as Greeter;

  await greeter.deployed()

  console.log('Greeter deployed', greeter.address)
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

And I got the states correctly:

Generating typings for: 0 artifacts in dir: types for target: ethers-v5
Successfully generated 3 typings!
Successfully generated 3 typings for external artifacts!
Check current contract information
greetings[0] =>  [
  'value 1',
  BigNumber { value: "123" },
  name: 'value 1',
  number: BigNumber { value: "123" }
]
greetings[1] =>  [
  'value 2',
  BigNumber { value: "456" },
  name: 'value 2',
  number: BigNumber { value: "456" }
]
greetings[2] =>  [
  'value 3',
  BigNumber { value: "789" },
  name: 'value 3',
  number: BigNumber { value: "789" }
]
nums[0] =>  BigNumber { value: "1" }
nums[1] =>  BigNumber { value: "2" }
nums[2] =>  BigNumber { value: "3" }
nums[3] =>  BigNumber { value: "4" }
str =>  hello, world

Please kindly tell me what is wrong point with this.

Kourin1996 avatar Aug 26 '22 09:08 Kourin1996

https://github.com/0xPolygon/polygon-edge/pull/536#issuecomment-1228292292

Fixed this issue and predeploy function is available with the constructor that takes nested object

contract HRM {
    struct Group {
        string name;
        uint128 number;
        bool flag;
    }

    struct Human {
        address addr;
        string name;
        int128 number;
    }

    int256 private id;
    string private name;

    Group[] private groups;
    mapping(uint256 => mapping(uint256 => Human)) people;

    struct ArgGroup {
        string name;
        uint128 number;
        bool flag;
        Human[] people;
    }

    constructor(
        int256 _id,
        string memory _name,
        ArgGroup[] memory _groups
    ) {
        id = _id;
        name = _name;

        for (uint256 idx = 0; idx < _groups.length; idx++) {
            groups.push(
                Group({
                    name: _groups[idx].name,
                    number: _groups[idx].number,
                    flag: _groups[idx].flag
                })
            );

            for (uint256 jdx = 0; jdx < _groups[idx].people.length; jdx++) {
                people[idx][jdx] = Human({
                    addr: _groups[idx].people[jdx].addr,
                    name: _groups[idx].people[jdx].name,
                    number: _groups[idx].people[jdx].number
                });
            }
        }
    }
}

Kourin1996 avatar Sep 08 '22 14:09 Kourin1996

Rebased back to develop due to a mistake

dbrajovic avatar Sep 22 '22 11:09 dbrajovic