Skip to content

SolidVM 3.2

SolidVM 3.2 makes important language improvements to better support developers writing contracts.

SolidVM 3.2 also includes several improvements and additions to the X.509 Identity registration and security, with new built-in certificate and signature verification functions.

3.2 Features

The following SolidVM features are only available in versions ^3.2:

  • The SolidVM Typechecker runs automatically before contract creation.
  • Add support for break and continue statements in loops.
  • Support for do-while loops.
  • Support for pushing to memory arrays (Such as arrays that are passed as arguments to other functions).
  • Unitary negative number declaration (e.g. x = -1;).
  • Add ability to access the chain ID of an account variable using the built-in property of <account-variable>.chainId
  • Add ability to use a string as the chain ID in the account type constructor.
  • Contract-type variable equality operator.
  • Support for returning array values in transaction results.
  • Wide number of language additions and enhancements based on Solidity v8.15 (Note Core SolidVM is based on Solidity v4.25)

In addition to these features, STRATO v7.6 also brought these fixes to all SolidVM runtimes:

  • Fixed a bug for using getter functions on string-type state variables.
  • Improved support and stability for accessing and returning values from complex structures like arrays, structs or mappings.
  • Enhanced reliability of constructors in private chain Governance contracts.

Version Pragma

SolidVM 3.2 requires the following pragma at the top of all contracts:

pragma solidvm 3.2;

Automatic Typechecker

When a contract constructor is run using SolidVM 3.2, the SolidVM typechecker will automatically run on the contract code collection. If the contract fails this step, then a SolidVM type error will be thrown. A transaction that uploads a faulty contracts will have an error state, just as if it were a transaction that failed for other reasons, like an index out-of-bounds or non-existent function call. See the Typechecker Documentation in the IDE page for more information.

X.509 Identity Features

In SolidVM 3.2, support for additional X.509 Certificate related functions was added:

  • verifyCert(string _cert, string _pubkey) returns (bool)
  • This function verifies the given certificate or certificate chain signature(s) with the provided public key (or chain of keys contained with the certficate chain.)
    • _cert : string
      • A DER encoded X.509 certificate
    • _pubkey : string
      • A DER encoded EC Public Key
  • verifySignature(string _mesgHash, string _signature, string _pubkey) returns (bool)
    • This function checks if the signature and message hash can be verified usign the provided public-key.
    • _mesgHash : string
      • A hex-encoded hash of a message signed with ECDSA - must be 64 chars long (32 bytes)
      • Example: "68410110452c1179af159f85d3a4ae72aed12101fcb55372bc97c5108ef6e4d7"
    • _signature : string
      • A DER encoded X.509 certificate
    • _pubkey : string
      • A DER encoded EC Public Key
    • verifyCertSignedBy(string _cert, string _pubkey) returns (bool)
    • This function checks if the certificate is signed by the public key. This contrasts with verifyCert which checks if the certificate is ultimatly signed by the root public key.
      • _cert : string
        • A DER encoded X.509 certificate
      • _pubkey : string
        • A DER encoded EC Public Key

For all arguments accepting a public-key or X.509 certificate, note that newlines contained within the file must be replaced with the "\n" escape character to form a single string.

Warning

In SolidVM 3.2, support for the built-in registerCert function was dropped in favor of registering certificates through the STRATO CertRegistry Dapp. See the X.509 Documentation for detailed steps on the Certificate Registration process.

Language Improvements

SolidVM 3.2 adds support for common programming language constructs such as:

  • break and continue statements in loops.
  • do-while loops.
  • Unitary negative number declaration (e.g. x = -1;).

Contract variable types may also be compared to each other using the == sign (previously unsupported):

pragma solidvm 3.2;
contract A {

    int x;

    constructor(int _x) {
        x = _x;
    }
}

contract CompareA {

    A a1;
    A a2;

    constructor() {
        a1 = new A(1);
        a2 = new A(2);
    }

    function isEqual() returns (bool) {
        return a1 == a2;
    }
}

Account Type Improvements

An account type can be constructed directly using a string as its chainId argument. The chain ID must be prefixed with the "0x" hexadecimal indicator. (Previously this required the chainId to be an integer.)

Example:

pragma solidvm 3.2;
contract MakeAccount {

    account myAccount;

    constructor(address _addr, string _chainId) {
        myAccount = account(_addr, _chainId);
    }
}

The chain ID of an account type can now be read from the new built-in chainId property. The chainId property has type int. If the account is on the main chain, its value will be 0. This property can useful to help ensure contracts do or do not get deployed to private chains.

Example:

pragma solidvm 3.2;
contract RequirePrivateChain {

    int privateData;

    constructor(int _privateData) {
        int curChainId = account(this, "self").chainId;
        require(curChainId != 0, "This contract must not be posted on the main chain");
        privateData = _privateData;
    }
}

Pushing to Memory Arrays

As an additional feature not available in standard Solidity, SolidVM 3.2 allows arrays that are stored in memory to be pushed to. This is possible since SolidVM does not have a dedicated stack limit or heap size. SolidVM does not have to statically allocate memory to arrays during function calls.

Example:

The useStorageArr function could be called and the data appended to the array would be stored in the xs state variable:

pragma solidvm 3.2;
contract PushMemArr {

    int[] xs;

    constructor(int[] _xs) {
        xs = _xs;
    }

    function pushMemArr(int[] _xs, int _x) {
        _xs.push(_x);
    }

    function useStorageArr() {
        pushMemArr(xs, 1); // xs = [...xs, 1]
    }
}

Return Array Types from the API

An array may be returned from a function as the final returned value of a transaction.

Example:

pragma solidvm 3.2;
contract ReturnArr {

  constructor() {}

  function foo() returns (int[]) {
    int[] xs = [1, 2, 3];
    return xs;
  }
}

Then foo could be called directly from an API function call.

Note that returning an array in a tuple or an array state variable is unsupported.

File Level Declarations

When compiling files for SolidVM you can declare constants outside the context of a contract. File level definitions are shared between all contracts in the same code collection, such as multiple contracts defined within the same file. All locally defined values for similarly named file-level values will be replaced for the file-level value - the file level value has precedence over a local one.

Example:

pragma solidvm 3.2;

const int MY_CONST = 1;
contract FileLevelConstants {
    function foo() returns (int) {
        int myConst = MY_CONST; // myConst == 1
    }
}

Warning

File level functions, enums, structs, and library definitions are not currently supported in SolidVM 3.2.

New block Built-In Attributes

  • block.coinbase : address payable

    • The current block miner’s (validator proposing the block in PBFT) address.
  • block.difficulty : uint

    • Current block difficulty
  • block.gaslimit : uint

    • Current block gaslimit

Example:

pragma solidvm 3.2;
contract MyContract {

    function foo() {
        require(block.coinbase == msg.sender, "Only the miner can call this function");
        require(block.difficulty == 100, "Difficulty must be 100");
        require(block.gaslimit == 100000, "Gaslimit must be 100000");
    }
}

mulmod() and addmod() Functions

SolidVM 3.2 adds support for the mulmod and addmod functions.

function addmod(uint a, uint b, uint c) returns (uint);

addmod() takes three arguments: a, b, and c. a and b are integers to be added together. c is the modulus. The result is the sum of a and b modulo c.

function mulmod(uint a, uint b, uint c) returns (uint);

mulmod() takes three arguments: a, b, and c. a and b are integers to be multiplied together. c is the modulus. The result is the product of a and b modulo c.

Example:

pragma solidvm 3.2;
contract ArithmeticTest {

    constructor() {}

    function foo() {
        int x = 1;
        int y = 2;
        int z = 3;
        int result = mulmod(x, y, z); // result == 2
        result = addmod(x, y, z); // result == 1
    }
}

Complex Tuple Destructuring

SolidVM 3.2 adds support for complex tuple destructuring.

Example:

pragma solidvm 3.2;
contract TupleDestructuring {

    uint index;

    function f() returns (uint, bool, uint) {
        return (7, true, 2);
    }

    constructor() {
        // Variables declared with type and assigned from the returned tuple,
        // not all elements have to be specified (but the number must match).
        (uint x, , uint y) = f(); // x == 7, y == 2
        // Common trick to swap values 
        (x, y) = (y, x); // x == 2, y == 7
        // Components can be left out (also for variable declarations).
        (index, , ) = f(); // Sets the index to 7
        return;
    }
}

Multivariate Functions

SolidVM 3.2 supports the multivariate functions: string.concat() and keccak256().

A multivariate function is a function that can take any number of arguments.

String Concat

function string.concat(string arg_0, ...args) returns (string);
Arguments:

  • This function takes an arbitrary number of strings

Returns:

  • A string of the arguments concatenated together.

Keccak256 Hashing

function keccak256(string arg_0, ...args) returns (bytes);
Arguments:

  • This function takes an arbitrary number of strings

Returns:

  • The keccak256 hash of the arguments concatenated together.

Example:

pragma solidvm 3.2;
contract MultivariateTest {

    constructor() {}

    function f() public pure returns (string) {
        return string.concat("Hello", " ", "World");
    }

    function g() public pure returns (bytes) {
        return keccak256(f()); // returns keccak256("Hello World")
    }
}

Extended Address Members and Functions

Several new built-in functions and members have been added in 3.2 to standard address types as well as support for payable address types. These additions allow for EVM developers to have an easier transition from EVM Solidity to SolidVM, as many of these new functions and features are very commonly used in EVM. These new features include the ability to send tokens to other users, check a users balance, and allow users to see the contents of their running contracts.

The following functions have special meaning and can only be used on address/account or the address payable/account payable types.

Balance

<address>.balance` : uint

The balance member allows a user to get the current balance of tokens for a particular address. This address must be a valid address. This balance infomation is public information and anyone can query the balance of any address.

Example:

pragma solidvm 3.2;
contract BalanceChecker{

    account a;

    constructor() {
        a = account(this);
    }

    function getBalance(address acc) returns(uint) {
        bal = acc.balance;
        return bal;
    }

    function getContractBalance() returns (uint) {
        return a.balance;
    }
}

The function getBalance() uses the address balance member to get the balance of the provided address.

Address Payable Type

SolidVM 3.2 introduces the payable classifier on the address type. This can be thought of as a sub-type of an address type that allows the declared address to receive tokens. A non-payable (standard address) type cannot receive tokens. Since the current account is always the sending address, all addresses are implicitly able to send tokens.

address payable types are declared using the keywords address payable varName.

address payable values may be constructed from using the payable() type constructor.

function payable(address _a) returns (address payable);

Address Send/Transfer Balance

The send and transfer functions allow users to move tokens from the current address to the address the function is called on. They are very similar to each other in that they both allow for the transfering of funds between address payable accounts. The main difference is that this send function returns a bool - it will return true if it is successful in paying the receiving party, and return false if the payment is unsuccessful. Furthermore, if the payment is unsuccessful then the transaction will not be reverted using send. transfer will revert a transaction upon an unsucessful payment.

Send

<address payable>.send(uint _amount) returns (bool);

Transfers _amount tokens from the current account to <address-payable>. If the transfer is unsuccessful, because the receiving address is not payable or insufficient balance from the sender, no tokens are transferred and the function returns false. Otherwise the tokens are transferred and the function returns true.

Example:

pragma solidvm 3.2;
contract PayYourFriend {

    account me; 
    account friend; 

    uint myBal; 
    uint friendBal;
    bool success;

    constructor() {
        me = account(this); 
        friend = account(0xdeadbeef);
    }

    function mySend() returns (bool, uint, uint, uint) {
        account payable mePayable = payable(me); 
        account payable friendPayable = payable(friend);
        success = friendPayable.send(13);
        myBal = mePayable.balance;
        friendBal = friendPayable.balance;
        return (success, myBal, friendBal);
    }
}

In the above contract there are two accounts and we assume that account PayYourFriend is funding the contract. The send function will return true or false depending on if the payment to account b is successful.

Transfer

<address payable>.transfer(uint _amount);

This function also allows for the transfering of funds between payable addresses. transfer does not return a value, however it will revert the transaction and raise an exception if the transfer is unsuccessful. This exception will cause the contract to terminate. For this reason it is recommended to use the send function instead of transfer.

Example:

pragma solidvm 3.2;
contract PayMyself {

  account a;
  account payable aPay;
  uint bal;

  constructor() {
    a = account(this);
    aPay = payable(a);
  }

  function myTransfer() returns (uint){
      aPay.transfer(100);
      bal = aPay.balance;
      return bal;
  }
}
In the above example we assume that account a has a starting balance of 13, this contract will fail since account a does not have enough funds to complete the transfer. This function is not considered safe to use as it simply crashes the program, without providing a framework to mitigate the crash.

Code

<address>.code : string

This member will get the code collection from a particular address, in the future it will likely be modified to return the information from a code pointer. This is different from how this same code member works in EVM. In EVM this is bytecode specific and returns the bytecode at the address. Since SolidVM is an interpreted language it does not use the EVM bytecode. code will return a string of the collection of code at the address, rather than the collection of bytecode for the address. This also means that the typical usage of <address>.call(<different_address>.code) does not work.

Important

The code member is not implemented the same way in SolidVM as the similarly named code function in EVM. In the EVM this member is typically used with the the three call functions - call(), staticcall(), and delegatecall().

call, staticcall, and delegatecall are not currently implemented in SolidVM. In a future release of STRATO, Code Pointers will be used to implement a similar concept of these three calls. Instead of running bytecode when these are called, these will run a piece of code referenced by a code pointer rather than EVM bytecode. These three members have not been fully implemented, but they are on their way and likely will be added to STRATO in future releases. Developing SolidVM code using the call and code functions will not currently work.

Codehash

<address>.codehash : string

This member is very similar to the regular .code function but it just gets the code hash of the address, this is useful for referencing particular code snippets. This will get the Keccak-256 hash of the code that is located at that address. This is useful when trying to verify that two accounts are dissimilar or for referencing the codehash for elsewhere in the current contract code.

Example:

pragma solidvm 3.2;
contract Test {

    constructor(){}

}

pragma solidvm 3.2;
contract CodeHashTest{

    string codeHashTest;

    constructor() public {
        Test t = new Test();
        codeHashTest = address(t).codehash;
    }

}
In this example, codeHashTest will contain the code hash generated for the contract t.

SolidVM and EVM Address Differences

While BlockApps keeps SolidVM up-to-date with the latest EVM Solidity standard, there are several differences in the languages' architecture, thus not all functionality can reach complete parity. There are several functions and members that cannot be translated to SolidVM with full equivalency. Several of these are apparent in the address member functions. These include the following:

  • <address>.code

    • Does not return EVM byte code, rather it returns the string of the code collection at the address.
    • Future development will reference code pointers instead of code collections.
  • <address>.call

    • This function is not in SolidVM yet.
  • <address>.staticcall

    • This function is not in SolidVM yet.
  • <address>.delegatecall

    • This function is not in SolidVM yet.