Deploy

This tutorial shows you how to deploy smart contracts as transactions onto Metacces blockchain network.

Compile the contract

You first need to create a smart contract. The following examples use the SimpleStorage.sol smart contract.

Create a new file called compile.js with the following content:

Copy

const fs = require("fs").promises;
const solc = require("solc");

async function main() {
  // Load the contract source code
  const sourceCode = await fs.readFile("SimpleStorage.sol", "utf8");
  // Compile the source code and retrieve the ABI and bytecode
  const { abi, bytecode } = compile(sourceCode, "SimpleStorage");
  // Store the ABI and bytecode into a JSON file
  const artifact = JSON.stringify({ abi, bytecode }, null, 2);
  await fs.writeFile("SimpleStorage.json", artifact);
}

function compile(sourceCode, contractName) {
  // Create the Solidity Compiler Standard Input and Output JSON
  const input = {
    language: "Solidity",
    sources: { main: { content: sourceCode } },
    settings: { outputSelection: { "*": { "*": ["abi", "evm.bytecode"] } } },
  };
  // Parse the compiler output to retrieve the ABI and bytecode
  const output = solc.compile(JSON.stringify(input));
  const artifact = JSON.parse(output).contracts.main[contractName];
  return {
    abi: artifact.abi,
    bytecode: artifact.evm.bytecode.object,
  };
}

main().then(() => process.exit(0));

Run the compile code to get the smart contract's output JSON:

Copy

node compile.js

Run solc to get the contract's bytecode and ABI:

Copy

solc SimpleStorage.sol --bin --abi

Once you have the bytecode and ABI, you can rename the output files to make them easier to use. This tutorial refers to them as SimpleStorage.bin and SimpleStorage.abi. You can now deploy this contract to Metacces using a public transaction.

Deployment examples

1. Using eth_sendTransaction

Call eth_sendTransaction with the following parameters:

  • from - Address of the sender's account.

  • to - Address of the receiver. To deploy a contract, set to null.

  • gas - Amount of gas provided by the sender for the transaction.

  • gasPrice - Price for each unit of gas the sender is willing to pay.

  • data - One of the following:

    • For contract deployments (this use case), the compiled binary of the contract.

    • For contract interactions, the hash of the invoked method signature and encoded parameters (see Ethereum Contract ABI).

    • For simple ether transfers, empty.

Example eth_sendTransaction curl HTTP request

Copy

curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0xf0e2db6c8dc6c681bb5d6ad121a107f300e9b2b5", "to":null, "gas":"0x24A22","gasPrice":"0x0", "data":"0x608060405234801561001057600080fd5b5060405161014d38038061014d8339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000819055505060f38061005a6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80632a1afcd914604157806360fe47b114605d5780636d4ce63c146088575b600080fd5b604760a4565b6040518082815260200191505060405180910390f35b608660048036036020811015607157600080fd5b810190808035906020019092919050505060aa565b005b608e60b4565b6040518082815260200191505060405180910390f35b60005481565b8060008190555050565b6000805490509056fea2646970667358221220e6966e446bd0af8e6af40eb0d8f323dd02f771ba1f11ae05c65d1624ffb3c58264736f6c63430007060033"}], "id":1}' -H 'Content-Type: application/json' https://oli.accesscan.io

2. Using web3.eth.Contract

Using the outputs from compiling the contract, create a new file public_tx_web3.js (or run the following commands in a JavaScript console) to send the transaction.

Copy

const path = require("path");
const fs = require("fs-extra");
const web3 = new Web3(host);
// use the existing Member1 account address or make a new account
const address = "f0e2db6c8dc6c681bb5d6ad121a107f300e9b2b5";

// read in the contracts
const contractJsonPath = path.resolve(__dirname, "SimpleStorage.json");
const contractJson = JSON.parse(fs.readFileSync(contractJsonPath));
const contractAbi = contractJson.abi;
const contractByteCode = contractJson.evm.bytecode.object;

async function createContract(
  host,
  contractAbi,
  contractByteCode,
  contractInit,
  fromAddress,
) {
  const web3 = new Web3(host);
  const contractInstance = new web3.eth.Contract(contractAbi);
  const ci = await contractInstance
    .deploy({ data: "0x" + contractByteCode, arguments: [contractInit] })
    .send({ from: fromAddress, gasLimit: "0x24A22" })
    .on("transactionHash", function (hash) {
      console.log("The transaction hash is: " + hash);
    });
  return ci;
}

// create the contract
async function main() {
  // using local RPC to send the transaction from. Change to your choice:
  createContract(
    "http://localhost:20000",
    contractAbi,
    contractByteCode,
    47,
    address,
  )
    .then(async function (ci) {
      console.log("Address of transaction: ", ci.options.address);
    })
    .catch(console.error);
}

3. Using eth_sendSignedTransaction

To deploy a smart contract using eth_sendSignedTransaction, use an account's private key to sign and serialize the transaction, and send the API request. This example uses the web3js library to make the API calls and the outputs from compiling the contract.

Create a new file public_tx.js(or run the following commands in a JavaScript console) to send the transaction.

Copy

const web3 = new Web3("http://localhost:20000");
// use an existing account or make a new account
const privateKey =
  "b9a4bd1539c15bcc83fa9078fe89200b6e9e802ae992f13cd83c853f16e8bed4";
const account = web3.eth.accounts.privateKeyToAccount(privateKey);

// read in the contracts
const contractJsonPath = path.resolve(__dirname, "SimpleStorage.json");
const contractJson = JSON.parse(fs.readFileSync(contractJsonPath));
const contractAbi = contractJson.abi;
const contractBinPath = path.resolve(__dirname, "SimpleStorage.bin");
const contractBin = fs.readFileSync(contractBinPath);
// initialize the default constructor with a value `47 = 0x2F`; this value is appended to the bytecode
const contractConstructorInit =
  "000000000000000000000000000000000000000000000000000000000000002F";

// get txnCount for the nonce value
const txnCount = await web3.eth.getTransactionCount(account.address);

const rawTxOptions = {
  nonce: web3.utils.numberToHex(txnCount),
  from: account.address,
  to: null, // public tx
  value: "0x00",
  data: "0x" + contractBin + contractInit, // contract binary appended with initialization value
  gasPrice: "0x0",
  gasLimit: "0x24A22", // max number of gas units the tx is allowed to use
};
console.log("Creating transaction...");
const tx = new Tx(rawTxOptions);
console.log("Signing transaction...");
tx.sign(privateKey);
console.log("Sending transaction...");
var serializedTx = tx.serialize();
const txr = await web3.eth.sendSignedTransaction(
  "0x" + serializedTx.toString("hex").toString("hex"),
);
console.log("tx transactionHash: " + txr.transactionHash);
console.log("tx contractAddress: " + txr.contractAddress);

rawTxOptions contains the following fields:

  • nonce - Number of transactions sent from this address.

  • from - Address of the EthSigner account.

  • to - Address of the receiver. To deploy a contract, set to null.

  • gas - Amount of gas provided by the sender for the transaction.

  • data - Binary of the contract (in this example there's also a constructor initialization value appended to the binary value).

  • value - Amount of ACCES in Wei transferred from the sender to the recipient.

As the example demonstrates, once the transaction tx is created, you can sign it with the private key of the account. You can then serialize it and call eth_sendSignedTransaction to deploy the contract.

Last updated