Solang works with Parity Substrate 3.0. This target is the most mature and has received the most testing so far.
The Parity Substrate has the following differences to Ethereum Solidity:
- The address type is 32 bytes, not 20 bytes
- An address literal has to be specified using the
- ABI encoding and decoding is done using the SCALE encoding
- Multiple constructors are allowed, and can be overloaded
There is an solidity example which can be found in the examples directory. Write this to flipper.sol and run:
solang --target substrate flipper.sol
Now you should have a file called
flipper.contract. The file contains both the ABI and contract wasm.
It can be used directly in the
Polkadot UI, as if the contract was written in ink!.
The Solana target requires Solana 1.5.0 or later. This target is in the early stages right now, however it is under active development. All data types are supported, but the builtin functions, external calls, and constructor calls have not been implemented yet. This is how to build your Solidity for Solana:
solang --target solana flipper.sol -v
This will produce two files called flipper.abi and flipper.so. The first is an ethereum style abi file and the latter being the ELF BPF shared object which can be deployed on Solana.
The contract storage model in Solana is different from Ethereum; it is consists of a contigious piece of memory, which can be
accessed directly from the smart contract. This means that there are no storage slots, and that a mapping must be implemented
using a simple hashmap. The same hashmap is used for fixed-length arrays which are a larger than 1kb. So, if you declare an
contract storage array of
int, then this is implemented using a hashmap.
Solana has execution model which allows one program to interact with multiple accounts. Those accounts can be used for different purposes. In Solang’s case, each time the contract is executed, it needs two accounts. The first account is for the return data, i.e. either the ABI encoded return values or the revert buffer. The second account is to hold the contract storage variables.
The output of the compiler will tell you how large the second account needs to be. For the flipper.sol example,
the output contains “info: contract flipper uses exactly 9 bytes account data”. This means the second account
should be exactly 9 bytes; anything larger is wasted. If the output is
“info: contract store uses at least 168 bytes account data” then some storage elements are dynamic, so the size
depends on the data stored. For example there could be a
string type, and storage depends on the length of
the string. The minimum is 168 bytes, but storing any non-zero-length dynamic types will fail.
If either account is too small, the transaction will fail with the error account data too small for instruction.
Before any function on a smart contract can be used, the constructor must be first be called. This ensures that the constructor as declared in the solidity code is executed, and that the contract storage account is correctly initialized. To call the constructor, abi encode (using ethereum abi encoding) the constructor arguments, and pass in two accounts to the call, the 2nd being the contract storage account.
Once that is done, any function on the contract can be called. To do that, abi encode the function call, pass this as input, and provide two accounts on the call. The second account must be the same contract storage account as used in the constructor. If there are any return values for the function, they are stored in the first return data account. The first 8 bytes is a 64 bits length, followed by the data itself. You can pass this into an ethereum abi decoder to get the expected return values.
There is an example of this written in node.
Hyperledger Burrow (ewasm)¶
The ewasm specification is not finalized yet. There is no create2 or chainid call, and the keccak256 precompile contract has not been finalized yet.
In Burrow, Solang is used transparently by the
burrow deploy tool if it is given the
When building and deploying a Solidity contract, rather than running the
solc compiler, it will run
solang compiler and deploy it as a wasm contract.
This is documented in the burrow documentation.
ewasm has been tested with Hyperledger Burrow. Please use the latest master version of burrow, as ewasm support is still maturing in Burrow.
Some language features have not been fully implemented yet on ewasm:
- The built in function
- Contract storage variables types
bytesand function types are not implemented
This is merely a proof-of-concept target, and has seen very little testing. Unless anyone is interested in maintaining this target, it is likely to be removed. On sawtooth, many Solidity concepts are impossible to implement:
- Return values from contract calls
- Calling other contracts
- Value transfers
- Instantiating contracts
When using Solang on Sawtooth Sabre, the constructor and function calls must be encoded with Ethereum ABI encoding. This can be done in different ways. In this guide we use ethabi. This can be installed using cargo:
cargo install ethabi-cli
In order to abi encode the calls, we need the abi for the contract. Let’s compile flipper.sol for Sabre:
solang --target sabre --verbose flipper.sol
We now have a file
flipper.abi. To deploy this, we need to create the constructor
ABI encoding. Unfortunately ethabi already falls short here; we cannot encode constructor calls using the cli
tools. However we can work round this by specify the constructor arguments explicitly. Note that if the
constructor does not take any arguments, then the constructor data should be empty (0 bytes). So, since the
constructor in flipper.sol takes a single bool, create it like so:
ethabi encode params -v bool true | xxd -r -p > constructor
For flipping the value, create it so:
ethabi encode function flipper.abi flip | xxd -r -p > flip
You’ll also need a yaml file with the following contents. Save it to flipper.yaml.
name: flipper version: '1.0' wasm: flipper.wasm inputs: - '12cd3c' outputs: - '12cd3c'
Now we have to start the Sawtooth Sabre environment. First clone the Sawtooth Sabre github repo and then run:
docker-compose -f docker-compose-installed.yaml up --build
Now enter the sabre-cli container:
docker exec -it sabre-cli bash
To create the flipper contract, run the following:
sabre cr --create flipper --owner $(cat /root/.sawtooth/keys/root.pub) --url http://rest-api:9708 sabre upload --filename flipper.yaml --url http://rest-api:9708 sabre ns --create 12cd3c --url http://rest-api:9708 --owner $(cat /root/.sawtooth/keys/root.pub) sabre perm 12cd3c flipper --read --write --url http://rest-api:9708
To run the constructor, run:
sabre exec --contract flipper:1.0 --payload ./constructor --inputs 12cd3c --outputs 12cd3c --url http://rest-api:9708
Lastly, to run the flip function:
sabre exec --contract flipper:1.0 --payload ./flip --inputs 12cd3c --outputs 12cd3c --url http://rest-api:9708
For the Sawtooth target,
returning values from Solidity is not yet implemented, and neither is
revert(). If you
attempt to call a function which returns a value, it will fail.