Smart Contract Raw TX

In this chapter we will deploy and interact with a smart contract using the JSON RPC. We will also see the underlying raw transaction that implements QTUM's Smart Contract platform.

The example is a contract with one single storage variable:

pragma solidity ^0.4.18;
contract SimpleStore {
  function SimpleStore(uint _value) public {
    value = _value;
  }

    function set(uint newValue) public {
        value = newValue;
    }

    function get() public constant returns (uint) {
        return value;
    }

    uint value;
}

Create The Contract

Compile the contract to bytecode:

solc --optimize --bin SimpleStore.sol

======= SimpleStore.sol:SimpleStore =======
Binary:
608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b200029

To create this contract, we'll essentially need to create a special transaction that uses the bytecode as the transaction payload data.

The contract constructor requires an initializing _value:

function SimpleStore(uint _value) public {
  value = _value;
}

Let's use 1 as the initial value. We'll need to ABI encode all the constructor parameters. The ABI encoded value for 1 is a 32 bytes hexadecimal string:

0000000000000000000000000000000000000000000000000000000000000001

How do we pass in the ABI-encoded constructor parameters? Simple, just append it to the end of the bytecode. So, the transaction payload for creating a smart contract should follow this structure:

<bytecode><constructor parameters>

The RPC command to create a contract is createcontract. Let's call it, combining together the compiler bytecode output and the constructor parameters:

qcli createcontract \
  608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001

{
  "txid": "4caa93a015ecf4e9fafd84c7cda2ad4897068777f29a1b1e1804e50553990c68",
  "sender": "qZ2kTyK4vLvGSngtYynmRdfd8n9UBFNwMv",
  "hash160": "a9b6e7e59974d92b949a9c9aa8eb420f73518364",
  "address": "ecb30fa67545211d0e4b859213872442cd9d37c5"
}

Raw TX For CREATECONTRACT

To gain some insight into the createcontract RPC call, let's examine the underlying UTXO that's used to create the TX. First, get the raw tx data associated with the txid:

qcli getrawtransaction 4caa93a015ecf4e9fafd84c7cda2ad4897068777f29a1b1e1804e50553990c68
02000000019b96d26a797a9952dbd8d81400de9143e30cc73ae2d83fe6d3bcc46dc665ebb5000000004847304402207601cc15aa2abc535de1673e1fe07d902d7970d2ea46892a5dc7dd7fbd394f5002201a6dd6dba6fe73f065cd2b2906d8b0632cfee68622835de23a8af6e73f7f968d01feffffff02107851a3d10100001976a91441d6ac4feec70a7b28c59c23e92be5ea2a15879a88ac0000000000000000fd1e01010403a0252601284d1201608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001c1fe010000

Then decode it:

qcli decoderawtransaction \
  02000000019b96d26a797a9952dbd8d81400de9143e30cc73ae2d83fe6d3bcc46dc665ebb5000000004847304402207601cc15aa2abc535de1673e1fe07d902d7970d2ea46892a5dc7dd7fbd394f5002201a6dd6dba6fe73f065cd2b2906d8b0632cfee68622835de23a8af6e73f7f968d01feffffff02107851a3d10100001976a91441d6ac4feec70a7b28c59c23e92be5ea2a15879a88ac0000000000000000fd1e01010403a0252601284d1201608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001c1fe010000

The output:

{
  "txid": "4caa93a015ecf4e9fafd84c7cda2ad4897068777f29a1b1e1804e50553990c68",
  "hash": "4caa93a015ecf4e9fafd84c7cda2ad4897068777f29a1b1e1804e50553990c68",
  "size": 454,
  "vsize": 454,
  "version": 2,
  "locktime": 510,
  "vin": [
    {
      "txid": "b5eb65c66dc4bcd3e63fd8e23ac70ce34391de0014d8d8db52997a796ad2969b",
      "vout": 0,
      "scriptSig": {
        "asm": "304402207601cc15aa2abc535de1673e1fe07d902d7970d2ea46892a5dc7dd7fbd394f5002201a6dd6dba6fe73f065cd2b2906d8b0632cfee68622835de23a8af6e73f7f968d[ALL]",
        "hex": "47304402207601cc15aa2abc535de1673e1fe07d902d7970d2ea46892a5dc7dd7fbd394f5002201a6dd6dba6fe73f065cd2b2906d8b0632cfee68622835de23a8af6e73f7f968d01"
      },
      "sequence": 4294967294
    }
  ],
  "vout": [
    {
      "value": 19998.99818000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 41d6ac4feec70a7b28c59c23e92be5ea2a15879a OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a91441d6ac4feec70a7b28c59c23e92be5ea2a15879a88ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "qPZWFFMrHtxsaop1wJKh6SCnnurMFjPexC"
        ]
      }
    },
    {
      "value": 0.00000000,
      "n": 1,
      "scriptPubKey": {
        "asm": "4 2500000 40 608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001 OP_CREATE",
        "hex": "010403a0252601284d1201608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001c1",
        "type": "create"
      }
    }
  ]
}
  • The first vout is a the change returned to the wallet of the sender
  • The second vout is an OP_CREATE script

The OP_CREATE is a new instruction added by QTUM to support smart contract platforms. It takes four arguments on the stack, as commented below:

# VM version
4
# gas limit
2500000
# gas price
40
# bytecode + constructor params
608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001
# create instruction
OP_CREATE

Deploy Using Solar

Let's deploy the SimpleStore contract again using solar. Under the hood, it's doing exactly what you just did:

solar deploy SimpleStore.sol '[1]'

🚀   All contracts confirmed
   deployed SimpleStore.sol => cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4

Interacting With A Contract

There are two ways to interact with a deployed contract.

  1. callcontract - read-only query of a contract's method.
  2. sendtocontract - create a tx that changes the state of the contract.

Both of these will invoke a contract's method. The parameters will need to be ABI encoded, and we will use solar to create the "data payload" that we'll need to send to the underlying RPC commands.

Calling A Contract

The "call" a contract, we ABI encode a method invokation, and send it to the callcontract RPC call.

Let's call the get method, to return the current value of the SimpleStore contract:

solar encode SimpleStore.sol get
6d4ce63c

The callcontract using the contract address, and the ABI encoded method invokation:

qcli callcontract cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4 \
  6d4ce63c

The result is:

{
  "address": "cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4",
  "executionResult": {
    "gasUsed": 21678,
    "excepted": "None",
    "newAddress": "cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4",
    "output": "0000000000000000000000000000000000000000000000000000000000000001",
    "codeDeposit": 0,
    "gasRefunded": 0,
    "depositSize": 0,
    "gasForDeposit": 0
  },
  "transactionReceipt": {
    "stateRoot": "88407020db33e8aca4ac47b16940fb31f9942bf8ff734e68156e899992465eda",
    "gasUsed": 21678,
    "bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "log": [
    ]
  }
}

You can see that the output property returned the value we used to construct the contract.

Sending A Contract Transaction

Now let's use sendtocontract to create a transaction that modifies the state of the SimpleStore contract.

To encode set(2):

solar encode SimpleStore.sol set '[2]'
60fe47b10000000000000000000000000000000000000000000000000000000000000002

Then we use the sendtocontract RPC call, with the address and ABI encoded method invokation:

qcli sendtocontract cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4 \
  60fe47b10000000000000000000000000000000000000000000000000000000000000002

The returned transaction id:

{
  "txid": "f6e934db283949a91a3f134246f48c52a7c8d580db36e998fa838d8c10c3b139",
  "sender": "qKNc7xc44W9teoxoWLUaxeJNq1967JaUoD",
  "hash160": "13e67f7e74b6e81a841292ebeaabfc7333d3e1cb"
}

Once the transaction had been confirmed, we can call get() again to get the new value:

qcli callcontract cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4 \
  6d4ce63c

{
  "address": "cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4",
  "executionResult": {
    "gasUsed": 21678,
    "excepted": "None",
    "newAddress": "cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4",
    "output": "0000000000000000000000000000000000000000000000000000000000000002",
    "codeDeposit": 0,
    "gasRefunded": 0,
    "depositSize": 0,
    "gasForDeposit": 0
  },
  "transactionReceipt": {
    "stateRoot": "c4c0aac17cdde49eb2ae303941c8721c7e4b2f394d27e15aec4f15778e4cd087",
    "gasUsed": 21678,
    "bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "log": [
    ]
  }
}

Yay, the value storage variable had indeeded been modified.

The SendToContract Transaction

Finally, let's examine the raw transaction that implements sendtocontract:

qcli getrawtransaction f6e934db283949a91a3f134246f48c52a7c8d580db36e998fa838d8c10c3b139

0200000001c919f89ddefa4bc09ad83330f993360fc419c9c91ef1560ede7f2df4f680eca6010000006a47304402207401aa5c7f4fddb9e3917c26a383fe250cecda61b51f722ce2fbc2c47bcb1aa40220585badcf06a9b77156d54d5bd5f5d30e9606c340012e9e5097d52c2bd8e1839b012102f0d1abe6acc590a400c3719a562299637c55e45e56d4c75658e07d263ab8b30ffeffffff02c0348d9bd10100001976a91458a1b81dbdeedd166bb166f528bb861048baf9b288ac00000000000000004301040390d00301282460fe47b1000000000000000000000000000000000000000000000000000000000000000214cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4c221020000

Then decode it:

qcli decoderawtransaction 0200000001c919f89ddefa4bc09ad83330f993360fc419c9c91ef1560ede7f2df4f680eca6010000006a47304402207401aa5c7f4fddb9e3917c26a383fe250cecda61b51f722ce2fbc2c47bcb1aa40220585badcf06a9b77156d54d5bd5f5d30e9606c340012e9e5097d52c2bd8e1839b012102f0d1abe6acc590a400c3719a562299637c55e45e56d4c75658e07d263ab8b30ffeffffff02c0348d9bd10100001976a91458a1b81dbdeedd166bb166f528bb861048baf9b288ac00000000000000004301040390d00301282460fe47b1000000000000000000000000000000000000000000000000000000000000000214cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4c221020000

The tx:

{
  "txid": "f6e934db283949a91a3f134246f48c52a7c8d580db36e998fa838d8c10c3b139",
  "hash": "f6e934db283949a91a3f134246f48c52a7c8d580db36e998fa838d8c10c3b139",
  "size": 267,
  "vsize": 267,
  "version": 2,
  "locktime": 545,
  "vin": [
    {
      "txid": "a6ec80f6f42d7fde0e56f11ec9c919c40f3693f93033d89ac04bfade9df819c9",
      "vout": 1,
      "scriptSig": {
        "asm": "304402207401aa5c7f4fddb9e3917c26a383fe250cecda61b51f722ce2fbc2c47bcb1aa40220585badcf06a9b77156d54d5bd5f5d30e9606c340012e9e5097d52c2bd8e1839b[ALL] 02f0d1abe6acc590a400c3719a562299637c55e45e56d4c75658e07d263ab8b30f",
        "hex": "47304402207401aa5c7f4fddb9e3917c26a383fe250cecda61b51f722ce2fbc2c47bcb1aa40220585badcf06a9b77156d54d5bd5f5d30e9606c340012e9e5097d52c2bd8e1839b012102f0d1abe6acc590a400c3719a562299637c55e45e56d4c75658e07d263ab8b30f"
      },
      "sequence": 4294967294
    }
  ],
  "vout": [
    {
      "value": 19997.69515200,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 58a1b81dbdeedd166bb166f528bb861048baf9b2 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a91458a1b81dbdeedd166bb166f528bb861048baf9b288ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "qRe2MMp8cyzXs7WpCGZKvpdrJJYyoKm9Lg"
        ]
      }
    },
    {
      "value": 0.00000000,
      "n": 1,
      "scriptPubKey": {
        "asm": "4 250000 40 60fe47b10000000000000000000000000000000000000000000000000000000000000002 cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4 OP_CALL",
        "hex": "01040390d00301282460fe47b1000000000000000000000000000000000000000000000000000000000000000214cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4c2",
        "type": "call"
      }
    }
  ]
}

The second vout is the script that invokes a smart contract. Its structure is very similar to the OP_CREATE script that was used to create the contract.

The commented script:

# VM version
4
# gas limit
250000
# gas price
40
# ABI-encoded method invokation
60fe47b10000000000000000000000000000000000000000000000000000000000000002
# address of contract
cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4
OP_CALL

results matching ""

    No results matching ""