Skip to content

Transactions

Introduction

A transaction is the core method of creating, and updating data on STRATO. This section gives a brief overview of how to make the different types of transactions available on STRATO in order to manage data on smart contracts.

Note that in order to make a transaction on STRATO ^8.0, a user must have an on-chain verified X.509 identity.

Basics

Every transaction either creates or interacts with data at a certain address on the chain. This address either represents a real user using STRATO, or a contract with its corresponding state and code. Additionally a transaction also occurs on a specific chain, either the main chain visible to all, or a private chain only accessible to its members. This address-chain pairing is like a street address - there is a street number and street name and a building cannot be located with just one of the data points. Likewise operations within transactions can interact with contracts that exist at another address, allowing for complex data exchange.

Transactions alter the state of the record it is interacting with and creates a new state to be inserted in a block and verified on the chain. Multiple transaction may be put into a single block and generally a large batch of successive transactions in one block can be completed faster than the same number of transactions in several blocks.

Every transaction is an atomic operation and has a single entry point. A transaction cannot simultaneously create a contract and call a function on a different contract in the same transaction (excluding calls from inside the contract code). These would have to be completed in two separate transactions.

Transactions have a hardcoded limit (gas limit) on the maximum amount of operations that can be done in each one. This done to prevent spam and infinitely looping transactions from stalling the network. Note this limit is independent of the gasLimit parameter provided in the transaction parameters so that users cannot set it to a malicious value. See more about gas in SolidVM

There are three types of transactions on STRATO, all using the same POST /strato/2.3/transaction endpoint. Specify the type of transaction by using the type field in the transaction payload:

  • CONTRACT
  • FUNCTION
  • TRANSFER

Every transaction API request must contain a txs array and the txParams object

Reference:

  • txs: array
    • A list of each transaction being made in this request. Each transaction object shape depends on the type of transaction being made.
    • Every transaction object contains a payload to define the details of the transaction and a type to define what type of transaction to run. See the different transactions to see how to form the payload for that transaction type.
  • txParams: object
    • gasLimit: number
      • The maximum amount of gas to use for these transactions.
      • Default: 100000000
    • gasCost: number
      • The price in wei tokens to set for gas for these transactions.
      • Default: 1

By default when sending transactions, the API will immediately return with the transaction hash(es) but not wait for the transactions to fully complete execution on-chain. Alternatively, the API can wait for the transactions to finish by providing the resolve query parameter as true.

Contract Upload

A contract upload, also referred to as posting a contract, is one of the primary ways a contract can be created and allows for all other entry points to the contract to be accessible. A contract when initially created runs its constructor with the provided arguments. The contract's source code must be either provided directly in the API request body, or another existing contract's source code may be referenced by address using STRATO's Code Collection feature.

Contracts contain the programmatic logic necessary to meet your requirements and are the single source of truth for on-chain data updates. Smart contracts use the Solidity programming language to describe these procedures and STRATO uses either SolidVM or the Ethereum Virtual Machine to execute these operations. This allows updates to follow complex instructions seen in any programming language and also instantiate (create) other contracts.

When uploading a new contract, the name of the contract that is meant to be instantiated must also be provided. This is because multiple contract types may be included in the contract source payload.

Contract Upload Transaction payload:

  • contract: string
    • The name of the contract in src to be instantiated. This contract's constructor will be executed.
  • src: string
    • The contract source code.
  • args: object
    • The key/value pairs of arguments to provided to the contract's constructor.
  • metadata: object
    • VM: "SolidVM" or "EVM" (default "EVM")
      • The VM used to execute this contract.
    • history: string (default "")
      • A comma separated list of contract names in src for which to enable Cirrus History tables (Audit logs).
  • chainid: string (optional)
    • The chain ID on which to post this contract.
    • Defaults to the main chain
    • This can also be provided via a query parameter of the same name.

Transaction type: CONTRACT

Example

curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "txs": [
        {
          "payload": {
            "contract": "SimpleStorage",
            "src": "<contract-src>",
            "args": {
              "_storedData": 3
            },
            "metadata": {
              "VM": "SolidVM"
            }
          },
          "type": "CONTRACT"
        }
      ],
      "txParams": {
        "gasLimit": 32100000000,
        "gasPrice": 1
      }
    }' \
  "https://<strato_address>/strato/v2.3/transaction?resolve=true"

Response

Note, the previous request is made with ?resolve=true in order to demonstrate the full results from a contract upload transaction. For un-resolved transactions, the txResult and data fields is null.

[
    { 
        "status": "success",
        "hash": "936f9ea1f29edd4f71af8004c115c90687b9dedc4b99c3e653c6a06f7341e6c1",
        "txResult": {
            "deletedStorage": "",
            "status": "success",
            "contractsDeleted": "",
            "gasUsed": "0000000000000000000000000000000000000000000000000000000000019381",
            "stateDiff": "",
            "time": 0.0006703,
            "kind": "SolidVM",
            "chainId": null,
            "response": "<response>",
            "blockHash": "3a0f1ed3834733a32aafbe948fabbe22db242278dec4efe872c8196a1ccec218",
            "transactionHash": "3edacd3a2a12d1357f836a5e14f7e4294869ce45035eec9b8be5f4c761679188",
            "etherUsed": "0000000000000000000000000000000000000000000000000000000000019381",
            "newStorage": "",
            "message": "Success!",
            "trace": "",
            "contractsCreated": "eca3e4bb4de0e1fbfae17b45fa3dfce5ba9f8784,0000000000000000000000000000000000000000,ea245c8e259dcf724dd4852da6b288f6b0452db6"
        },
        "data": {
            "tag": "Upload",
            "contents": {
                "bin": "<binary>",
                "chainId": null,
                "address": "eca3e4bb4de0e1fbfae17b45fa3dfce5ba9f8784",
                "bin-runtime": "<binary-runtime>",
                "codeHash": {
                    "kind": "SolidVM",
                    "digest": "33e5b1b1cd4f06d3f7400916e96a25b0aa2a8b185b7f833dd59bfea7b283f7c8"
                },
                "name": "SimpleStorage",
                "src": "<contract-src>",
                "xabi": {
                    // ...compiler information for contract functions, variables, modifiers, etc.
                }
            }
        }
    }
]
const contracts = [
  {
    name: "SimpleStorage",
    source: contractSrc,
    args: {
      _storedData: 3,
    },
  }
]

const callOptions = {
  ...options,
  VM: "SolidVM",
  config: {
    ...options.config,
    isAsync: true,
  }
}

const callResults = await rest.createContractList(user, contracts, callOptions)

Response

[
  {
    bin: 'bin removed.',
    chainId: null,
    address: 'df8c753f29b18c464ec889ce5a230acb7ab79be9',
    'bin-runtime': 'bin-runtime removed.',
    codeHash: {
      kind: 'SolidVM',
      name: 'SimpleStorage',
      digest: '55fd5570f483af6c48de3c0ebb8092f90b8d47a9357f7637b7e919f7c8dcd144'
    },
    name: 'SimpleStorage',
    src: 'src removed.',
    xabi: 'xabi removed.'
  }
]

Contract Source

A contract may be created by directly providing the string of its source code to the API request body under the src field of a transaction. Note all referenced contract definitions must be directly included in the source payload - even if that contract type has already been instantiated on the chain. Importing of other contract source code into the main contract code must be done before making a request since STRATO cannot resolve file paths from the API. Everytime a new contract is created using uploaded source code, the code is parsed to be run in the VM, even if the contract code with the same code hash has been uploaded.

Code Collection

A code collection is a bundle of contract source code that has already been uploaded to STRATO but can be reused as the source code of another instantiated contract. Referencing another code collection points to the already parsed code as the source code for this new contract, and the newly instantiated contract can be specified from the contracts in the collection independently of the one originally used. Thus using a code collection for contract source is referred to as using "code pointers".

Code Pointer Diagram

Contracts created from code collections maintain the same X.509 creator information associated with the original code creator. Therefore it allows all of the app source code to be easily identified by organization of the uploader.

Using Code Collections has several advantages:

  • Code Reusability
    • Allows entire application contract code to be uploaded once and reused across other chains.
  • Reduced payload size
    • A smaller payload in the API request body reduces the overall request size and improves networking performance at scale.
  • Improves transaction throughput
    • Since the source code is already parsed from the Code Collection's initial upload, it can be immediately executed in the VM.

As a security measure, Code Collections can only be used as Governance contracts of private chains and cannot be arbitrarily used in any upload contract transaction. Note however that the governance contract may use and instantiate other contracts already in the code collection.

To use a code collection as governance contract, provide the account and name fields in the codePtr body of the private chain creation request. This is used in place of the src field.

Reference:

  • account: string
    • The address:chainId pair of the code collection being used.
  • name: string
    • The name of the contract to be used as the instantiated governance contract.

Example

...chainArgs,
"codePtr": {
    "account": "927...5de1:b1ab...5a6",
    "name": "SimpleStorage"
}

Also see Private Chains for more information.

Function Call

A function call is a way to perform pre-defined operations on a pre-existing contract. A function call requires an address chain ID pair, contract name, and function name to provided in order to properly identify the function being called. Function arguments are also provided via the request body.

Function Call Transaction Payload:

  • contractName: string
    • The name of the contract that the called function is in.
  • contractAddress: string
    • The address of the contract being called.
  • method: string
    • The function (method) name to call within the contract contractName
  • args: object
    • The key/value pair of arguments to provide to the function.
  • chainid: string (optional)
    • The chain ID on which to this contract exists.
    • Defaults to the main chain
    • This can also be provided via a query parameter of the same name.

Transaction type: FUNCTION

Example

curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "txs": [
      {
        "payload": {
          "contractName": "SimpleStorage",
          "contractAddress": "90f0a711cd0ab921bbc9b54c4a6bdb3219e590f9",
          "method": "set",
          "args": {
            "x": 5
          }
        },
        "type": "FUNCTION"
      }
    ],
    "txParams": {
      "gasLimit": 32100000000,
      "gasPrice": 1
    }
  }' \
  "https://<strato_address>/strato/v2.3/transaction?resolve=true"

Response

The returned value of the contract's function call is located in the data.contents property of each transaction response object.

[
  { 
    "status": "Success",
    "hash": "936f9ea1f29edd4f71af8004c115c90687b9dedc4b99c3e653c6a06f7341e6c1",
    "txResult": {...},
    "data": {
      "tag": "Call",
      "contents": [ "5" ]
    }
  }
]
const calls = [
  {
    contract: {
      name: "SimpleStorage",
      address: contractAddress,
    },
    method: "set",
    args: {
      x: 5
    }
  }
]

const callOptions = {
  ...options,
  config: {
    ...options.config,
    isAsync: true
  }
}

const callResults = await rest.callList(user, calls, callOptions)

Response

An array of each function call's return value(s).

[
  // The returned values from the first transaction
  [
    "5"
  ],
]

Token Transfer

A token transfer is a basic transaction that does exactly what the name implies - transfer tokens from one account to another. Tokens are transferred from the transaction sender's account to the toAddress's account. Because tokens are not used for any monetary value or exchange in STRATO, this type of transaction is largely unused for most STRATO applications. Users only need to ensure that their transactions operate within a reasonable number of operations.

Token Transfer Transaction Payload:

  • toAddress: string
    • The account address to send tokens to.
  • value: number
    • The amount of wei to transfer to toAddress. Withdrawn from the transaction sender's balance.

Transaction Type: TRANSFER Example

curl -X POST \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -d '{
    "txs": [
        {
            "payload": {
                "toAddress": "90f0a711cd0ab921bbc9b54c4a6bdb3219e590f9",
                "value": "1000"
            },
            "type": "TRANSFER"
        }
    ],
    "txParams": {
        "gasLimit": 32100000000,
        "gasPrice": 1
    }
    }' \
    "https://<strato_address>/strato/v2.3/transaction?resolve=true"

Accessing Transaction Results

A transactions results can be accessed independently of the actual POST request that makes the transaction. Transaction results are accessed by their Transaction Hash.

Endpoint

Get a single transaction's results:

GET https://<strato_address>/bloc/v2.2/transactions/<tx-hash>/result

Get multiple transactions' results:

POST https://<strato_address>/bloc/v2.2/transactions/results

When accessing multiple transactions' results, provide an array with each requested transactions' hash as elements in the request body:

[
    "936f9ea1f29edd4f71af8004c115c90687b9dedc4b99c3e653c6a06f7341e6c1",
    "3edacd3a2a12d1357f836a5e14f7e4294869ce45035eec9b8be5f4c761679188"
]

Example

curl -X GET \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  "https://<strato_address>/bloc/v2.2/transactions/936f9ea1f29edd4f71af8004c115c90687b9dedc4b99c3e653c6a06f7341e6c1/result"
const transaction = {
  hash: txHash
}

const txResult = await rest.resolveResult(user, transaction, options)

Response

The information of the requested transaction:

  • success
    • Whether or not the API call was successful or not.
  • hash
    • This transaction's transaction hash. Important when querying for multiple transactions.
  • txResult
    • An object containing useful transaction data, such as VM kind, chain ID of the transaction, block hash, and gasUsed.
  • data
    • An object containing a tag for the transaction type, and contents, an object containing information relevant to the type of transaction, like a addresses of contracts created, function returned values, etc.
{
  "status": "Success",
  "hash": "3edacd3a2a12d1357f836a5e14f7e4294869ce45035eec9b8be5f4c761679188",
  "txResult": {
    "deletedStorage": "",
    "status": "success",
    "contractsDeleted": "",
    "gasUsed": "0000000000000000000000000000000000000000000000000000000000019381",
    "stateDiff": "",
    "time": 0.0006703,
    "kind": "SolidVM",
    "chainId": null,
    "response": "<response>",
    "blockHash": "3a0f1ed3834733a32aafbe948fabbe22db242278dec4efe872c8196a1ccec218",
    "transactionHash": "3edacd3a2a12d1357f836a5e14f7e4294869ce45035eec9b8be5f4c761679188",
    "etherUsed": "0000000000000000000000000000000000000000000000000000000000019381",
    "newStorage": "",
    "message": "Success!",
    "trace": "",
    "contractsCreated": "eca3e4bb4de0e1fbfae17b45fa3dfce5ba9f8784,0000000000000000000000000000000000000000,ea245c8e259dcf724dd4852da6b288f6b0452db6"
  },
  "data": {
    "tag": "Upload",
    "contents": {
      "bin": "<binary>",
      "chainId": null,
      "address": "eca3e4bb4de0e1fbfae17b45fa3dfce5ba9f8784",
      "bin-runtime": "<binary-runtime>",
      "codeHash": {
        "kind": "SolidVM",
        "digest": "33e5b1b1cd4f06d3f7400916e96a25b0aa2a8b185b7f833dd59bfea7b283f7c8"
      },
      "name": "SimpleStorage",
      "src": "<contract-src>",
      "xabi": {
        // ...compiler information for contract functions, variables, modifiers, etc.
      }
    }
  }
}
{
  "status": "Success",
  "hash": "936f9ea1f29edd4f71af8004c115c90687b9dedc4b99c3e653c6a06f7341e6c1",
  "txResult": {
    "deletedStorage": "",
    "status": "success",
    "contractsDeleted": "",
    "gasUsed": "000000000000000000000000000000000000000000000000000000000000682d",
    "stateDiff": "",
    "time": 0.0006397,
    "kind": "SolidVM",
    "chainId": null,
    "response": "",
    "blockHash": "2724b0519d2fec120eca6c74504d1ebd3c7d98a4f90e0667f7b67652ba837edc",
    "transactionHash": "936f9ea1f29edd4f71af8004c115c90687b9dedc4b99c3e653c6a06f7341e6c1",
    "etherUsed": "000000000000000000000000000000000000000000000000000000000000682d",
    "newStorage": "",
    "message": "Success!",
    "trace": "",
    "contractsCreated": ""
  },
  "data": {
    "tag": "Call",
    "contents": [
      "5"
    ]
  } 
}