Sworn compiles Clarity smart contracts into SmartWeave contracts that run on the Arweave blockchain.
More specifically, the Sworn compiler, called sworn
, parses .clar
files and
compiles them into an equivalent SmartWeave program in the form of JavaScript
code.
Sworn also includes experimental WebAssembly contract generation, but we recommend JavaScript output since the generated JS contracts are perfectly human readable and thus feasible to audit.
The latest release binaries for macOS and Linux are available here:
To install, after downloading untar the archive and copy the binary to
/usr/local/bin
, as follows:
wget https://github.com/weavery/sworn/releases/download/1.0.0/sworn-1.0.0-macos.tar.gz
tar xf sworn-1.0.0-macos.tar.gz
sudo install sworn-1.0.0-macos /usr/local/bin/sworn
wget https://github.com/weavery/sworn/releases/download/1.0.0/sworn-1.0.0-linux.tar.gz
tar xf sworn-1.0.0-linux.tar.gz
sudo install sworn-1.0.0-linux /usr/local/bin/sworn
If you wish to try out the latest and greatest Sworn, you will need to build it from source code yourself, which entails setting up an OCaml development environment. Reserve at least half an hour of time and see further down in this document for the particulars.
To view Sworn's built-in man page that documents all command-line options, run:
sworn --help
To compile the Clarity counter.clar
example contract, run:
sworn -t js counter.clar
The previous writes out the JavaScript program to standard output, which is helpful during development and debugging.
However, you can alternatively specify an output file name in the usual way, with the target type inferred from the output file extension:
sworn -o counter.js counter.clar
There is preliminary and experimental support for compiling to WebAssembly.
Both the textual representation (.wat
) and binary bytecode (.wasm
) format
are supported as targets:
sworn -t wat counter.clar
sworn -o counter.wasm counter.clar
Note that SmartWeave itself has no WebAssembly interface as yet, so for now you're certainly better off sticking with the JavaScript output. Additionally, JavaScript contracts are human readable and thus auditable.
(define-data-var counter int 0)
(define-read-only (get-counter)
(ok (var-get counter)))
(define-public (increment)
(begin
(var-set counter (+ (var-get counter) 1))
(ok (var-get counter))))
(define-public (decrement)
(begin
(var-set counter (- (var-get counter) 1))
(ok (var-get counter))))
sworn -t js counter.clar
clarity.requireVersion("0.1")
function getCounter(state) {
return clarity.ok(state.counter);
}
function increment(state) {
state.counter = clarity.add(state.counter, 1);
return {state, result: clarity.ok(state.counter)};
}
function decrement(state) {
state.counter = clarity.sub(state.counter, 1);
return {state, result: clarity.ok(state.counter)};
}
export function handle(state, action) {
const input = action.input;
if (input.function === 'getCounter') {
return {result: getCounter(state)};
}
if (input.function === 'increment') {
return increment(state);
}
if (input.function === 'decrement') {
return decrement(state);
}
return {state};
}
The generated SmartWeave code requires Clarity.js, which implements the necessary runtime support for Clarity's standard library.
The generated SmartWeave code may make use of JavaScript's BigInt
feature
to represent 128-bit integers. All modern browsers support this.
On the server side, Node.js 10.4+ supports BigInt
.
Clarity | TypeScript | JavaScript | Notes |
---|---|---|---|
bool |
boolean |
boolean |
|
(buff N) |
Uint8Array |
Uint8Array |
|
err |
Err<T> |
Err |
|
int |
number or bigint |
number or BigInt |
|
(list N T) |
Array<T> |
Array |
|
(optional T) |
T or null |
T or null |
|
principal |
String |
String |
|
(response T E) |
T or Err<E> |
T or Err |
|
(string-ascii N) |
String |
String |
|
(string-utf8 N) |
String |
String |
|
(tuple ...) |
Map<String, any> |
Map |
|
uint |
number or bigint |
number or BigInt |
In order to support Clarity's language semantics of 128-bit integers and safe
arithmetic that traps on numeric overflow and underflow, arithmetic operations
need runtime support. Thus, in the general case, an operation such as (* a b)
must be compiled to clarity.mul(a, b)
instead of the trivial but ultimately
incorrect a * b
.
Sworn is written in OCaml, an excellent programming language for crafting compiler toolchains.
Sworn is a standard multi-pass compiler consisting of the following stages:
The Clarity parser and abstract syntax tree (AST) originally developed for Sworn have been spun off into a standalone project and OCaml library called Clarity.ml. This enables anyone familiar with OCaml to quickly and easily develop more best-of-class tooling for Clarity contracts.
See Clarity.ml's lexer.mll
for the lexical analyzer source code.
See Clarity.ml's parser.mly
and parse.ml
for the parser source code.
See Clarity.ml's grammar.ml
for the structure of the Clarity AST.
Sworn converts Clarity code into an intermediate representation (IR) called
SWIR, standing for SmartWeave Intermediate Representation. (See
SWIR/grammar.ml
for the structure of SWIR.)
Currently, SWIR is very similar to the Clarity AST. However, the Clarity to SWIR conversion stage is nonetheless needed for several reasons:
-
The Clarity language doesn't yet have a stable specification, and it's easier to keep up with language changes upstream if they only require changes to the compiler frontend instead of throughout the compiler.
-
SWIR facilitates essential optimizations, such as avoiding unnecessary code generation of relatively expensive 128-bit arithmetic operations in cases where the compiler can prove that 64-bit arithmetic will be safe.
-
SWIR facilitates code generation for multiple targets such as JavaScript and WebAssembly.
-
Sworn will in the future endeavor to support other input languages beyond Clarity, which will in any case necessitate an IR.
SWIR is converted into either JavaScript or WebAssembly's AST, which can be serialized in binary or text formats.
The JavaScript generation is implemented using OCaml's powerful standard pretty-printing facility, which means that the resulting code is perfectly human readable as well as nicely formatted. (File bug reports if you should find this to not be the case.)
The WebAssembly generation is implemented using the WebAssembly reference implementation, also written in OCaml. This means that Sworn's WebAssembly output is always up-to-date with regards to any changes and features in the latest WebAssembly standard, as well as (by definition) 100% compliant.
This section documents how to get set up with a development environment for building Sworn from source code. It is only of interest to people who wish to contribute to Sworn.
The following baseline tooling is required in order to build Sworn from source code:
We would recommend you don't install OCaml from a package manager.
Rather, get set up with OPAM and then let OPAM install the correct version of OCaml as follows:
opam init -c 4.11.1 # if OPAM not yet initialized
opam switch create 4.11.1 # if OPAM already initialized
Once OPAM and OCaml are available, install Dune as follows:
opam install dune
The following OCaml tools and libraries are required in order to build Sworn from source code:
-
Alcotest for unit tests
-
Clarity.ml for parsing Clarity code
-
Cmdliner for the command-line interface
-
Cppo for code preprocessing
-
ISO8601 for date handling
-
Num for 128-bit integers
-
Ocolor for terminal colors
-
Wasm for WebAssembly code generation
These aforementioned dependencies are all best installed via OPAM:
opam install -y alcotest cmdliner cppo iso8601 num ocolor wasm
opam pin add -y clarity-lang https://github.com/weavery/clarity.ml -k git
alias sworn='dune exec bin/sworn/sworn.exe --'
sworn --help
git clone https://github.com/weavery/sworn.git
cd sworn
dune build
sudo install _build/default/bin/sworn/sworn.exe /usr/local/bin/sworn
We thank Arweave and Blockstack for sponsoring the development of Sworn.
We thank Blockstack and Algorand for having developed the Clarity language, an important evolution for the future of smart contracts.
Feature | Type | JavaScript | WebAssembly | Notes |
---|---|---|---|---|
* |
function | ✅ | ✅ | |
+ |
function | ✅ | ✅ | |
- |
function | ✅ | ✅ | |
/ |
function | ✅ | ✅ | |
< |
function | ✅ | 🚧 | |
<= |
function | ✅ | 🚧 | |
> |
function | ✅ | 🚧 | |
>= |
function | ✅ | 🚧 | |
and |
syntax | ✅ | 🚧 | |
append |
function | ✅ | 🚧 | |
as-contract |
syntax | ✅ | 🚧 | |
as-max-len? |
syntax | ✅ | 🚧 | |
asserts! |
syntax | ✅ | 🚧 | |
at-block |
syntax | ❌ | ❌ | Not supported by SmartWeave. |
begin |
syntax | ✅ | ✅ | |
block-height |
keyword | ✅ | 🚧 | |
bool |
type | ✅ | ✅ | |
buff |
type | ✅ | 🚧 | |
concat |
function | ✅ | 🚧 | |
contract-call? |
function | ❌ | ❌ | Not supported by SmartWeave. |
contract-caller |
keyword | ✅ | 🚧 | |
contract-of |
function | ❌ | ❌ | Not supported by SmartWeave. |
default-to |
function | ✅ | 🚧 | |
define-constant |
syntax | ✅ | ✅ | |
define-data-var |
syntax | ✅ | ✅ | |
define-fungible-token |
syntax | 🚧 | 🚧 | |
define-map |
syntax | ✅ | 🚧 | |
define-non-fungible-token |
syntax | 🚧 | 🚧 | |
define-private |
syntax | ✅ | ✅ | |
define-public |
syntax | ✅ | ✅ | |
define-read-only |
syntax | ✅ | ✅ | |
define-trait |
syntax | ❌ | ❌ | Not supported by SmartWeave. |
err |
function | ✅ | 🚧 | |
false |
constant | ✅ | ✅ | |
filter |
function | ✅ | 🚧 | |
fold |
function | ✅ | 🚧 | |
ft-get-balance |
function | ✅ | 🚧 | |
ft-mint? |
function | ✅ | 🚧 | |
ft-transfer? |
function | ✅ | 🚧 | |
get |
syntax | ✅ | 🚧 | |
get-block-info? |
function | ❌ | ❌ | Not supported by SmartWeave. |
hash160 |
function | ✅ | 🚧 | |
if |
syntax | ✅ | 🚧 | |
impl-trait |
syntax | ❌ | ❌ | Not supported by SmartWeave. |
int |
type | ✅ | ✅ | |
is-eq |
function | ✅ | 🚧 | |
is-err |
function | ✅ | 🚧 | |
is-none |
function | ✅ | 🚧 | |
is-ok |
function | ✅ | 🚧 | |
is-some |
function | ✅ | 🚧 | |
keccak256 |
function | ✅ | 🚧 | |
len |
function | ✅ | 🚧 | |
let |
syntax | ✅ | 🚧 | |
list |
type | ✅ | 🚧 | |
list |
function | ✅ | 🚧 | |
map |
function | ✅ | 🚧 | |
map-delete |
function | ✅ | 🚧 | |
map-get? |
function | ✅ | 🚧 | |
map-insert |
function | ✅ | 🚧 | |
map-set |
function | ✅ | 🚧 | |
match |
syntax | ✅ | 🚧 | |
mod |
function | ✅ | ✅ | |
nft-get-owner? |
function | ✅ | 🚧 | |
nft-mint? |
function | ✅ | 🚧 | |
nft-transfer? |
function | ✅ | 🚧 | |
none |
constant | ✅ | 🚧 | |
not |
function | ✅ | 🚧 | |
ok |
function | ✅ | ✅ | |
optional |
type | ✅ | ✅ | |
or |
syntax | ✅ | 🚧 | |
pow |
function | ✅ | ✅ | |
principal |
type | ✅ | 🚧 | |
print |
function | ✅ | 🚧 | |
response |
type | ✅ | 🚧 | |
sha256 |
function | ✅ | 🚧 | |
sha512 |
function | ✅ | 🚧 | |
sha512/256 |
function | ✅ | 🚧 | |
some |
function | ✅ | 🚧 | |
stx-burn? |
function | ❌ | ❌ | Not supported by SmartWeave. |
stx-get-balance |
function | ❌ | ❌ | Not supported by SmartWeave. |
stx-transfer? |
function | ❌ | ❌ | Not supported by SmartWeave. |
to-int |
function | ✅ | 🚧 | |
to-uint |
function | ✅ | 🚧 | |
true |
constant | ✅ | ✅ | |
try! |
function | ✅ | 🚧 | |
tuple |
type | ✅ | 🚧 | |
tx-sender |
keyword | ✅ | 🚧 | |
uint |
type | ✅ | ✅ | |
unwrap! |
function | ✅ | 🚧 | |
unwrap-err! |
function | ✅ | 🚧 | |
unwrap-err-panic |
function | ✅ | 🚧 | |
unwrap-panic |
function | ✅ | 🚧 | |
use-trait |
syntax | ❌ | ❌ | Not supported by SmartWeave. |
var-get |
function | ✅ | ✅ | |
var-set |
function | ✅ | ✅ | |
xor |
function | ✅ | 🚧 |
Legend: ❌ = not supported. 🚧 = work in progress. ✅ = supported.