├── .gitignore ├── LICENSE.md ├── README.md ├── banner.svg ├── contracts └── cw-as-counter │ ├── asconfig.json │ ├── assembly │ ├── index.ts │ ├── src │ │ ├── contract.ts │ │ ├── msg.ts │ │ └── state.ts │ └── tsconfig.json │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── tests │ ├── index.js │ └── works.test.ts │ ├── yarn-error.log │ └── yarn.lock ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── rewrite-wasm │ ├── README.md │ ├── package.json │ └── rewrite-wasm.js └── std │ ├── README.md │ ├── as.ts │ ├── cw-storage-plus.ts │ ├── exports.ts │ ├── imports.ts │ ├── index.ts │ ├── package.json │ ├── tsconfig.json │ └── types.ts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *debug.log 3 | dist/ 4 | build/ 5 | raw/ 6 | .history 7 | *.backup 8 | .vscode 9 | .idea 10 | cli/index.generated.js 11 | src/diagnosticMessages.generated.ts 12 | coverage/ 13 | .idea/ 14 | .DS_Store 15 | *.iml 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2022 Terran One LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CosmWasm smart contracts in AssemblyScript 2 | 3 |
4 | 5 | ![image](./banner.svg) 6 | 7 |
8 | 9 | 10 | 11 | * [CosmWasm in AssemblyScript](#cosmwasm-in-assemblyscript) 12 | * [Quickstart](#quickstart) 13 | * [Project Structure](#project-structure) 14 | * [Architecture](#architecture) 15 | * [Wasm Imports](#wasm-imports) 16 | * [Wasm Exports](#wasm-exports) 17 | * [Required](#required) 18 | * [Optional](#optional) 19 | * [Copyright](#copyright) 20 | 21 | 22 | 23 | **NOTE: This is purely for study and experimentation. Confio has expressed doubt regarding AssemblyScript's viability as 24 | a serious CosmWasm language due to concerns about security.** 25 | 26 | This repository contains a sample implementation of several CosmWasm smart contracts written in AssemblyScript. We test 27 | the behavior of our contracts against [`cosmwasm-vm-js`](https://github.com/terran-one/cosmwasm-vm-js), a 28 | JavaScript-based runtime for CosmWasm that is convenient to instrument and run locally. 29 | 30 | ### Uploaded Contracts 31 | 32 | The AssemblyScript contract shown in this repository are uploaded to the following addresses: 33 | 34 | - Terra 35 | Testnet (`pisco-1`): [terra1rnvm38z2d9aksqlx7hkcrjj9cyvn3wad4nhe3d6a87h2g7f0lrtqdy3fl6](https://finder.terra.money/testnet/address/terra1rnvm38z2d9aksqlx7hkcrjj9cyvn3wad4nhe3d6a87h2g7f0lrtqdy3fl6) 36 | - Juno Testnet (`uni-5`): `juno13qfr40ewq0ng63ukgxm4xxue6uv4u5d65xpqes3srpq39jjux4hqeqg484` 37 | 38 | Feel free to play around with them -- it's just a simple counter. 39 | 40 | ## Quickstart 41 | 42 | 1. First, clone the repository. 43 | 44 | ```bash 45 | $ git clone https://github.com/terran-one/cosmwasm-as 46 | $ cd cosmwasm-as 47 | ``` 48 | 49 | 2. Install the dependencies. We used `yarn`, but you can use `npm` as well. 50 | 51 | ```bash 52 | $ yarn 53 | ``` 54 | 55 | 3. Run `yarn build` in the contract directory to build the AssemblyScript Wasm binaries. 56 | 57 | **NOTE:** This compiles using AssemblyScript, then rewrites it using `@cosmwasm-as/rewrite-wasm`, which uses 58 | Binaryen. 59 | 60 | ```bash 61 | $ cd contracts/cw-as-counter 62 | $ yarn build 63 | ``` 64 | 65 | 4. Run the tests. 66 | 67 | ```bash 68 | $ yarn test 69 | ``` 70 | 71 | ## Project Structure 72 | 73 | This project was created with the [`asbuild`](https://github.com/AssemblyScript/asbuild) tool and follows a directory 74 | organization similar to other AssemblyScript projects. 75 | 76 | ```text 77 | cw-as-counter 78 | ├── assembly/ -- AssemblyScript source root 79 | │ ├── src/ -- Contract implementation 80 | │ │ ├── contract.ts -- `contract.rs` analog 81 | │ │ ├── msg.ts -- `msg.rs` analog 82 | │ │ └── state.ts -- `state.rs` analog 83 | │ └── index.ts -- directs compiler on assembling Wasm module -- `lib.rs` analog 84 | ├── build/ 85 | │ ├── debug.wasm -- Wasm binary: with debug symbols 86 | │ └── release.wasm -- Wasm binary: production-optimized 87 | └── tests/ 88 | └── works.test.js -- Simple acceptance test 89 | ``` 90 | 91 | ## Architecture 92 | 93 | A *CosmWasm contract* is a Wasm module that adheres to the following structure[^1]. 94 | 95 | [^1]: Discussed in further detail on 96 | the [CosmWasm official repository README](https://github.com/CosmWasm/cosmwasm/blob/007fd626c67945fc548a99b6ba06aefcd0bb4195/README.md) 97 | 98 | ### Wasm Imports 99 | 100 |
Imports provided by CosmWasm VM 101 | 102 | ```rust 103 | extern "C" { 104 | #[cfg(feature = "abort")] 105 | fn abort(source_ptr: u32); 106 | 107 | fn db_read(key: u32) -> u32; 108 | fn db_write(key: u32, value: u32); 109 | fn db_remove(key: u32); 110 | 111 | #[cfg(feature = "iterator")] 112 | fn db_scan(start_ptr: u32, end_ptr: u32, order: i32) -> u32; 113 | #[cfg(feature = "iterator")] 114 | fn db_next(iterator_id: u32) -> u32; 115 | 116 | fn addr_validate(source_ptr: u32) -> u32; 117 | fn addr_canonicalize(source_ptr: u32, destination_ptr: u32) -> u32; 118 | fn addr_humanize(source_ptr: u32, destination_ptr: u32) -> u32; 119 | 120 | fn secp256k1_verify(message_hash_ptr: u32, signature_ptr: u32, public_key_ptr: u32) -> u32; 121 | fn secp256k1_recover_pubkey( 122 | message_hash_ptr: u32, 123 | signature_ptr: u32, 124 | recovery_param: u32, 125 | ) -> u64; 126 | 127 | fn ed25519_verify(message_ptr: u32, signature_ptr: u32, public_key_ptr: u32) -> u32; 128 | fn ed25519_batch_verify(messages_ptr: u32, signatures_ptr: u32, public_keys_ptr: u32) -> u32; 129 | 130 | fn debug(source_ptr: u32); 131 | 132 | fn query_chain(request: u32) -> u32; 133 | } 134 | ``` 135 | 136 |
137 | 138 | We declare them inside `@cosmwasm-as/std/imports.ts` and use where needed inside our library code. 139 | Note that since these are quite low level, the end-user consuming the `cosmwasm-as` API probably won't need to import 140 | them directly. 141 | 142 | ### Wasm Exports 143 | 144 |
Exports expected by CosmWasm VM 145 | 146 | ##### Required 147 | 148 | ```rust 149 | extern "C" { 150 | fn allocate(size: usize) -> u32; 151 | fn deallocate(pointer: u32); 152 | fn instantiate(env_ptr: u32, info_ptr: u32, msg_ptr: u32) -> u32; 153 | fn interface_version_8() -> (); 154 | } 155 | ``` 156 | 157 | #### Optional 158 | 159 | ```rust 160 | extern "C" { 161 | fn execute(env_ptr: u32, info_ptr: u32, msg_ptr: u32) -> u32; 162 | fn query(env_ptr: u32, msg_ptr: u32) -> u32; 163 | 164 | // TODO: the following have yet to be implemented 165 | fn migrate(env_ptr: u32, msg_ptr: u32) -> u32; 166 | fn reply(env_ptr: u32, msg_ptr: u32) -> u32; 167 | fn sudo(env_ptr: u32, msg_ptr: u32) -> u32; 168 | fn ibc_channel_open(env_ptr: u32, msg_ptr: u32) -> u32; 169 | fn ibc_channel_connect(env_ptr: u32, msg_ptr: u32) -> u32; 170 | fn ibc_channel_close(env_ptr: u32, msg_ptr: u32) -> u32; 171 | fn ibc_packet_receive(env_ptr: u32, msg_ptr: u32) -> u32; 172 | fn ibc_packet_ack(env_ptr: u32, msg_ptr: u32) -> u32; 173 | fn ibc_packet_timeout(env_ptr: u32, msg_ptr: u32) -> u32; 174 | } 175 | ``` 176 | 177 |
178 | 179 | This is the main "meat" that is relevant to our implementation, which must get explicitly exported 180 | by `assembly/index.ts` to get picked up by the AssemblyScript compiler. 181 | Their implementation resides in `@cosmwasm-as/std/exports.ts` -- we simply re-export them in our `assembly/index.ts`: 182 | 183 | ```ts 184 | // assembly/index.ts 185 | 186 | // This file is important for the AssemblyScript compiler to correctly construct 187 | // the WASM module, and should be common to all CosmWasm AssemblyScript projects. 188 | // To program your contract, you should modify code in the `./contract` folder. 189 | 190 | // Required Wasm exports 191 | import {do_instantiate, do_execute, do_query} from "@cosmwasm-as/std"; 192 | import {ExecuteMsg, InstantiateMsg, QueryMsg} from "./src/msg"; 193 | import {instantiateFn, executeFn, queryFn} from "./src/contract"; 194 | 195 | export { 196 | interface_version_8, 197 | allocate, 198 | deallocate, 199 | } from '@cosmwasm-as/std'; 200 | 201 | export function instantiate(env: i32, info: i32, msg: i32): i32 { 202 | return do_instantiate(env, info, msg, instantiateFn); 203 | } 204 | 205 | export function execute(env: i32, info: i32, msg: i32): i32 { 206 | return do_execute(env, info, msg, executeFn); 207 | } 208 | 209 | export function query(env: i32, msg: i32): i32 { 210 | return do_query(env, msg, queryFn); 211 | } 212 | ``` 213 | 214 | ### Passing data to/from CosmWasm VM (host environment) 215 | 216 | The CosmWasm VM <> contract data protocol is quite simple: 217 | 218 | - VM to contract: values are serialized to JSON and loaded into a pointer into Wasm linear memory, requested by 219 | calling `allocate`. 220 | - Contract to VM: values are serialized to JSON and a pointer to a `Region` struct describing the section of VM memory 221 | is returned to the VM. 222 | 223 | The "start"-ing `Region` struct consists of only 3 members: 224 | 225 | - `offset` - the underlying pointer 226 | - `capacity` - max size of encoded object 227 | - `length` - current length of data stored in memory 228 | 229 | ### Build Changes 230 | 231 | We altered the build script slightly to make it work with CosmWasm. 232 | 233 | #### Step 1: Compile AssemblyScript to Wasm 234 | 235 | ```diff 236 | asc assembly/index.ts 237 | --target debug 238 | --sourceMap 239 | --debug 240 | + --disable bulk-memory 241 | + --use abort=~lib/@cosmwasm-as/std/as/ABORT 242 | + --runtime stub 243 | + --exportStart 244 | ``` 245 | 246 | A couple changes here: 247 | 248 | ##### 1. `--disable bulk-memory` 249 | 250 | If not set, Wasmer will complain about missing 0xFC opcode. 251 | 252 | ##### 2. `--use abort=~lib/@cosmwasm-as/std/as/ABORT` 253 | 254 | AssemblyScript usually requires the host environment to supply `env`.`abort`. 255 | However, CosmWasm supplies a different function with the same name, so we rewire it according 256 | to [Simon Warta's example](https://github.com/CosmWasm/cosmwasm/blob/1a356a249c7f0fc655c9070776775a765ab7da2f/contracts/assemblyscript-poc/contract/src/cosmwasm-std/cosmwasm.ts#L106-L126) 257 | . 258 | 259 | ##### 3. `--runtime stub` 260 | 261 | To disable garbage collection (GC). 262 | 263 | ##### 4. `--exportStart` 264 | 265 | Export the `(start ...)` instruction rather than have it being implicity called. 266 | 267 | #### Step 2: Rewrite binary 268 | 269 | We authored a tool using [`Binaryen`](https://github.com/bytecode-alliance/binaryen) to remove the `(start ...)` 270 | instruction and replace it by prepending it to each entrypoint's function body. 271 | 272 | ```bash 273 | cosmwasm-as-rewrite-wasm [--optimize=1] build/debug.wasm 274 | ``` 275 | 276 | ## Copyright 277 | 278 | Copyright © 2022 Terran One LLC 279 | 280 | -------------------------------------------------------------------------------- /banner.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/asconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "targets": { 3 | "debug": { 4 | "outFile": "build/debug.wasm", 5 | "textFile": "build/debug.wat", 6 | "sourceMap": true, 7 | "debug": true 8 | }, 9 | "release": { 10 | "outFile": "build/release.wasm", 11 | "textFile": "build/release.wat", 12 | "sourceMap": true, 13 | "optimizeLevel": 3, 14 | "shrinkLevel": 0, 15 | "converge": false, 16 | "noAssert": false 17 | } 18 | }, 19 | "options": { 20 | "bindings": "esm", 21 | "transform": "json-as/transform" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/assembly/index.ts: -------------------------------------------------------------------------------- 1 | // This file is important for the AssemblyScript compiler to correctly construct 2 | // the WASM module, and should be common to all CosmWasm AssemblyScript projects. 3 | // To program your contract, you should modify code in the `./contract` folder. 4 | 5 | // Required Wasm exports 6 | import {do_instantiate, do_execute, do_query} from "@cosmwasm-as/std"; 7 | import {ExecuteMsg, InstantiateMsg, QueryMsg} from "./src/msg"; 8 | import {instantiateFn, executeFn, queryFn} from "./src/contract"; 9 | 10 | export { 11 | interface_version_8, 12 | allocate, 13 | deallocate, 14 | } from '@cosmwasm-as/std'; 15 | 16 | export function instantiate(env: i32, info: i32, msg: i32): i32 { 17 | return do_instantiate(env, info, msg, instantiateFn); 18 | } 19 | 20 | export function execute(env: i32, info: i32, msg: i32): i32 { 21 | return do_execute(env, info, msg, executeFn); 22 | } 23 | 24 | export function query(env: i32, msg: i32): i32 { 25 | return do_query(env, msg, queryFn); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/assembly/src/contract.ts: -------------------------------------------------------------------------------- 1 | import {Binary, Response, Env, Info, to_binary} from "@cosmwasm-as/std"; 2 | import {Result} from "as-container"; 3 | import {InstantiateMsg, ExecuteMsg, QueryMsg, CountResponse} from "./msg"; 4 | import {STATE} from "./state"; 5 | 6 | function Ok(res: Response): Result { 7 | return Result.Ok(res); 8 | } 9 | 10 | function Err(msg: string): Result { 11 | return Result.Err(msg); 12 | } 13 | 14 | export function instantiateFn(env: Env, info: Info, msg: InstantiateMsg): Result { 15 | 16 | STATE().save({ 17 | owner: info.sender, 18 | count: msg.count, 19 | }); 20 | 21 | let res = Response.new_(); 22 | return Ok(res); 23 | } 24 | 25 | export function executeFn(env: Env, info: Info, msg: ExecuteMsg): Result { 26 | if (msg.increment) { 27 | return try_increment(env, info); 28 | } else if (msg.reset) { 29 | return try_reset(env, info, msg.reset!.count); 30 | } else { 31 | return Err("Unknown message"); 32 | } 33 | } 34 | 35 | function try_increment(env: Env, info: Info): Result { 36 | let _state = STATE().load(); 37 | if (_state.isErr) { 38 | return Err(_state.unwrapErr()); 39 | } 40 | let state = _state.unwrap(); 41 | state.count += 1; 42 | let _save = STATE().save(state); 43 | if (_save.isErr) { 44 | return Err(_save.unwrapErr()); 45 | } 46 | 47 | return Ok(Response.new_().addAttribute("method", "increment")); 48 | } 49 | 50 | function try_reset(env: Env, info: Info, count: i32): Result { 51 | let _state = STATE().load(); 52 | if (_state.isErr) { 53 | return Err(_state.unwrapErr()); 54 | } 55 | let state = _state.unwrap(); 56 | if (info.sender !== state.owner) { 57 | return Err("Unauthorized"); 58 | } 59 | 60 | state.count = count; 61 | let _save = STATE().save(state); 62 | if (_save.isErr) { 63 | return Err(_save.unwrapErr()); 64 | } 65 | 66 | return Ok(Response.new_().addAttribute("method", "reset")); 67 | } 68 | 69 | 70 | export function queryFn(env: Env, msg: QueryMsg): Result { 71 | if (msg.get_count) { 72 | let _q = query_count(); 73 | if (_q.isOk) { 74 | return to_binary(_q.unwrap()); 75 | } 76 | } 77 | return Result.Err("Unknown query"); 78 | } 79 | 80 | export function query_count(): Result { 81 | let _state = STATE().load(); 82 | if (_state.isErr) { 83 | return Result.Err(_state.unwrapErr()); 84 | } 85 | let state = _state.unwrap(); 86 | return Result.Ok({count: state.count}); 87 | 88 | } 89 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/assembly/src/msg.ts: -------------------------------------------------------------------------------- 1 | import { JSON } from "json-as/assembly"; 2 | 3 | @json 4 | export class InstantiateMsg { 5 | count: i32 6 | } 7 | 8 | @json 9 | export class IncrementBody {} 10 | 11 | @json 12 | export class ResetBody { 13 | count: i32 14 | } 15 | 16 | @json 17 | export class ExecuteMsg { 18 | increment: IncrementBody | null; 19 | reset: ResetBody | null; 20 | } 21 | 22 | @json 23 | export class GetCountBody {} 24 | 25 | @json 26 | export class QueryMsg { 27 | get_count: GetCountBody | null; 28 | } 29 | 30 | 31 | @json 32 | export class CountResponse { 33 | count: i32; 34 | } 35 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/assembly/src/state.ts: -------------------------------------------------------------------------------- 1 | import {JSON} from "json-as/assembly"; 2 | import {Item} from "@cosmwasm-as/std"; 3 | 4 | @json 5 | export class State { 6 | owner: string; 7 | count: i32; 8 | } 9 | 10 | /** 11 | * Although it would be more similar to CosmWasm to do: 12 | * 13 | * ```ts 14 | * export const STATE = new Item("state"); 15 | * ``` 16 | * 17 | * Wasmer doesn't like it, so we have to do this: 18 | */ 19 | 20 | export function STATE(): Item { 21 | return new Item("state"); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/assembly/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "assemblyscript/std/assembly.json", 3 | "include": [ 4 | "./**/*.ts" 5 | ] 6 | } -------------------------------------------------------------------------------- /contracts/cw-as-counter/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | transform: { 5 | '^.+\\.ts?$': 'ts-jest', 6 | }, 7 | transformIgnorePatterns: ['/node_modules/'], 8 | }; 9 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cw-as-counter", 3 | "version": "0.0.8", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "cw-as-counter", 9 | "version": "0.0.8", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "assemblyscript": "^0.24.1" 13 | } 14 | }, 15 | "node_modules/assemblyscript": { 16 | "version": "0.24.1", 17 | "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.24.1.tgz", 18 | "integrity": "sha512-uqR7NW0z/QFuqOkszoCb2F2jJI0VsmmmATRYG2JGzspC9nkct/8+6Pad7amCnIRriAC/T9AknEs+Qv/U/+fNpQ==", 19 | "dev": true, 20 | "dependencies": { 21 | "binaryen": "110.0.0-nightly.20221105", 22 | "long": "^5.2.0" 23 | }, 24 | "bin": { 25 | "asc": "bin/asc.js", 26 | "asinit": "bin/asinit.js" 27 | }, 28 | "engines": { 29 | "node": ">=16.0.0", 30 | "npm": ">=7.0.0" 31 | }, 32 | "funding": { 33 | "type": "opencollective", 34 | "url": "https://opencollective.com/assemblyscript" 35 | } 36 | }, 37 | "node_modules/binaryen": { 38 | "version": "110.0.0-nightly.20221105", 39 | "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-110.0.0-nightly.20221105.tgz", 40 | "integrity": "sha512-OBESOc51q3SwgG8Uv8nMzGnSq7LJpSB/Fu8B3AjlZg6YtCEwRnlDWlnwNB6mdql+VdexfKmNcsrs4K7MYidmdQ==", 41 | "dev": true, 42 | "bin": { 43 | "wasm-opt": "bin/wasm-opt", 44 | "wasm2js": "bin/wasm2js" 45 | } 46 | }, 47 | "node_modules/long": { 48 | "version": "5.2.1", 49 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", 50 | "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==", 51 | "dev": true 52 | } 53 | }, 54 | "dependencies": { 55 | "assemblyscript": { 56 | "version": "0.24.1", 57 | "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.24.1.tgz", 58 | "integrity": "sha512-uqR7NW0z/QFuqOkszoCb2F2jJI0VsmmmATRYG2JGzspC9nkct/8+6Pad7amCnIRriAC/T9AknEs+Qv/U/+fNpQ==", 59 | "dev": true, 60 | "requires": { 61 | "binaryen": "110.0.0-nightly.20221105", 62 | "long": "^5.2.0" 63 | } 64 | }, 65 | "binaryen": { 66 | "version": "110.0.0-nightly.20221105", 67 | "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-110.0.0-nightly.20221105.tgz", 68 | "integrity": "sha512-OBESOc51q3SwgG8Uv8nMzGnSq7LJpSB/Fu8B3AjlZg6YtCEwRnlDWlnwNB6mdql+VdexfKmNcsrs4K7MYidmdQ==", 69 | "dev": true 70 | }, 71 | "long": { 72 | "version": "5.2.1", 73 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", 74 | "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==", 75 | "dev": true 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cw-as-counter", 3 | "version": "0.0.8", 4 | "description": "AssemblyScript implementation of basic CosmWasm counter contract", 5 | "main": "index.js", 6 | "author": "William Chen", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "@cosmwasm-as/rewrite-wasm": "^0.0.8", 10 | "assemblyscript": "^0.24.1", 11 | "binaryen": "^111.0.0", 12 | "jest": "^29.3.1", 13 | "ts-jest": "^29.0.3", 14 | "visitor-as": "^0.11.3" 15 | }, 16 | "exports": { 17 | ".": { 18 | "import": "./build/release.js", 19 | "types": "./build/release.d.ts" 20 | } 21 | }, 22 | "scripts": { 23 | "build": "yarn asbuild && yarn rewrite-wasm", 24 | "asbuild:debug": "asc assembly/index.ts --target debug --disable bulk-memory --sourceMap --runtime stub --exportStart --use abort=~lib/@cosmwasm-as/std/as/ABORT --debug", 25 | "asbuild:release": "asc assembly/index.ts --target release --disable bulk-memory --sourceMap --runtime stub --exportStart --use abort=~lib/@cosmwasm-as/std/as/ABORT --debug", 26 | "asbuild": "npm run asbuild:debug && npm run asbuild:release", 27 | "rewrite-wasm": "cosmwasm-as-rewrite-wasm build/debug.wasm && cosmwasm-as-rewrite-wasm --optimize=1 build/release.wasm", 28 | "test": "jest" 29 | }, 30 | "dependencies": { 31 | "@cosmwasm-as/std": "^0.0.8", 32 | "as-base64": "^0.2.0", 33 | "as-container": "^0.6.1", 34 | "json-as": "^0.5.4" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/tests/index.js: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | import { add } from "../build/debug.js"; 3 | assert.strictEqual(add(1, 2), 3); 4 | console.log("ok"); 5 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/tests/works.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | VMInstance, 3 | IBackend, 4 | BasicBackendApi, 5 | BasicKVIterStorage, 6 | BasicQuerier, 7 | } from "@terran-one/cosmwasm-vm-js"; 8 | import {readFileSync} from "fs"; 9 | import {Env} from "@terran-one/cosmwasm-vm-js/dist/types"; 10 | 11 | let wasm: Buffer; 12 | let storage: BasicKVIterStorage; 13 | let backend: IBackend; 14 | let vm: VMInstance; 15 | let env: Env; 16 | 17 | describe("cw-as", () => { 18 | beforeEach(async () => { 19 | wasm = readFileSync(__dirname + "/../build/debug.wasm"); 20 | storage = new BasicKVIterStorage(); 21 | backend = { 22 | backend_api: new BasicBackendApi("terra"), 23 | storage, 24 | querier: new BasicQuerier(), 25 | }; 26 | vm = new VMInstance(backend); 27 | await vm.build(wasm); 28 | 29 | 30 | env = { 31 | "block": {"height": 2868163, "time": "1669260984649833191", "chain_id": "pisco-1"}, 32 | "contract": {"address": "terra1evndrg98clcjjgdpcq4tch57fvgqe2yu2u5ehgxvxtzcrkad37lspdler0"} 33 | }; 34 | }); 35 | 36 | it("works", async () => { 37 | let info1 = {sender: "A", funds: []}; 38 | let info2 = {sender: "B", funds: []}; 39 | 40 | let res = vm.instantiate(env, info1, {count: 0}); 41 | res = vm.execute(env, info1, {increment: {}}); 42 | console.log(res.json); 43 | res = vm.execute(env, info1, {increment: {}}); 44 | console.log(res.json); 45 | res = vm.execute(env, info1, {reset: {}}); 46 | console.log(res.json); 47 | res = vm.execute(env, info1, {increment: {}}); 48 | console.log(res.json); 49 | res = vm.execute(env, info1, {increment: {}}); 50 | console.log(res.json); 51 | res = vm.query(env, {get_count: {}}); 52 | console.log(res.str); 53 | }); 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/yarn-error.log: -------------------------------------------------------------------------------- 1 | Arguments: 2 | /opt/homebrew/Cellar/node/19.1.0/bin/node /opt/homebrew/bin/yarn build 3 | 4 | PATH: 5 | /opt/homebrew/Cellar/go/1.18/libexec/bin:/Users/william/go/bin:/Users/william/.local/share/fig/plugins/git-open:/Users/william/bin:/Users/william/.cargo/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/MacGPG2/bin:/usr/local/share/dotnet:~/.dotnet/tools:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/Users/william/.cargo/bin:/Users/william/IdeaProjects/cosmwasm-as/node_modules/.bin:/Users/william/.fig/bin:/Users/william/.local/bin 6 | 7 | Yarn version: 8 | 1.22.18 9 | 10 | Node version: 11 | 19.1.0 12 | 13 | Platform: 14 | darwin arm64 15 | 16 | Trace: 17 | SyntaxError: /Users/william/IdeaProjects/cosmwasm-as/contracts/cw-as-counter/package.json: Unexpected non-whitespace character after JSON at position 1098 18 | at JSON.parse () 19 | at /opt/homebrew/lib/node_modules/yarn/lib/cli.js:1625:59 20 | at Generator.next () 21 | at step (/opt/homebrew/lib/node_modules/yarn/lib/cli.js:310:30) 22 | at /opt/homebrew/lib/node_modules/yarn/lib/cli.js:321:13 23 | 24 | npm manifest: 25 | { 26 | "name": "cw-as-counter", 27 | "version": "1.0.0", 28 | "description": "AssemblyScript implementation of basic CosmWasm counter contract", 29 | "main": "index.js", 30 | "author": "William Chen", 31 | "license": "MIT", 32 | "devDependencies": { 33 | "assemblyscript": "^0.24.1", 34 | "binaryen": "^111.0.0", 35 | "visitor-as": "^0.11.3", 36 | "@terran-one/cosmwasm-as-rewrite-wasm": "*" 37 | }, 38 | "type": "module", 39 | "exports": { 40 | ".": { 41 | "import": "./build/release.js", 42 | "types": "./build/release.d.ts" 43 | } 44 | }, 45 | "scripts": { 46 | "build": "yarn asbuild && yarn rewrite-wasm", 47 | "asbuild:debug": "asc assembly/index.ts --target debug --disable bulk-memory --sourceMap --runtime stub --exportStart --use abort=assembly/index/logAndCrash --debug", 48 | "asbuild:release": "asc assembly/index.ts --target release --disable bulk-memory --sourceMap --runtime stub --exportStart --use abort=assembly/index/logAndCrash --debug", 49 | "asbuild": "npm run asbuild:debug && npm run asbuild:release", 50 | "rewrite-wasm": "cosmwasm-as-rewrite-wasm build/debug.wasm && cosmwasm-as-rewrite-wasm --optimize=1 build/release.wasm" 51 | }, 52 | "test": "node tests" 53 | }, 54 | "dependencies": { 55 | "@terran-one/cosmwasm-as-std": "*", 56 | "as-base64": "^0.2.0", 57 | "as-container": "^0.6.1", 58 | "json-as": "^0.5.4" 59 | } 60 | } 61 | 62 | yarn manifest: 63 | No manifest 64 | 65 | Lockfile: 66 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 67 | # yarn lockfile v1 68 | 69 | 70 | "@assemblyscript/loader@^0.19.5": 71 | version "0.19.23" 72 | resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.19.23.tgz#7fccae28d0a2692869f1d1219d36093bc24d5e72" 73 | integrity sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw== 74 | 75 | as-base64@^0.2.0: 76 | version "0.2.0" 77 | resolved "https://registry.yarnpkg.com/as-base64/-/as-base64-0.2.0.tgz#b4bed937c93bfb6524b932b517e1b8571a0dd518" 78 | integrity sha512-j6JxprAVN4SXUcZiSChuMBf0JJOCdh5OfrHAepluxmFWuDZZm+YjmfNSk8djUHRPEB+Ui5HGvrz46GLvTJf3ig== 79 | 80 | as-container@^0.6.1: 81 | version "0.6.1" 82 | resolved "https://registry.yarnpkg.com/as-container/-/as-container-0.6.1.tgz#94806a91e96b68622c55f301fc04e6339a1228c2" 83 | integrity sha512-cgQ7P/dQAGeU2lLhPpinThYfFcUw3HzV8b00CoqV+l5Tsgpl7xhXdoc/2srCyK0um1BVAQwlrEpNTtthzkMF+g== 84 | 85 | as-string-sink@^0.5.0: 86 | version "0.5.2" 87 | resolved "https://registry.yarnpkg.com/as-string-sink/-/as-string-sink-0.5.2.tgz#39da159c01a2064a1dadd872a38c743369dd567f" 88 | integrity sha512-HJmhbgJWqfllV7ltZxpMaj/AxSkokb3MzYo1Qd+0HIB9zyzChu1SXBmNc3n0U3XrT6/NIeQvBUegxaXwoUNXAg== 89 | 90 | as-variant@^0.4.0: 91 | version "0.4.0" 92 | resolved "https://registry.yarnpkg.com/as-variant/-/as-variant-0.4.0.tgz#14dad464a7aef8bf61c5f587b396f6915459befa" 93 | integrity sha512-EJUrF7R/G+TQ8t/cH2S7Z9t3v4eKfggCr7HBQGVRXCk+eehs7YOfvR7ScTSAt6vsOui9S5zCkXa1gAGZ9F7vJg== 94 | dependencies: 95 | "@assemblyscript/loader" "^0.19.5" 96 | 97 | assemblyscript@^0.24.1: 98 | version "0.24.1" 99 | resolved "https://registry.yarnpkg.com/assemblyscript/-/assemblyscript-0.24.1.tgz#87a32da2cd2962ffdb4d44e159751b52d3594421" 100 | integrity sha512-uqR7NW0z/QFuqOkszoCb2F2jJI0VsmmmATRYG2JGzspC9nkct/8+6Pad7amCnIRriAC/T9AknEs+Qv/U/+fNpQ== 101 | dependencies: 102 | binaryen "110.0.0-nightly.20221105" 103 | long "^5.2.0" 104 | 105 | binaryen@110.0.0-nightly.20221105: 106 | version "110.0.0-nightly.20221105" 107 | resolved "https://registry.yarnpkg.com/binaryen/-/binaryen-110.0.0-nightly.20221105.tgz#9e3c47e8ffa31521acd125013dca3ceea143d0bf" 108 | integrity sha512-OBESOc51q3SwgG8Uv8nMzGnSq7LJpSB/Fu8B3AjlZg6YtCEwRnlDWlnwNB6mdql+VdexfKmNcsrs4K7MYidmdQ== 109 | 110 | binaryen@^111.0.0: 111 | version "111.0.0" 112 | resolved "https://registry.yarnpkg.com/binaryen/-/binaryen-111.0.0.tgz#dd970a11d8fe61959f77d609dfee3c19ad80b80a" 113 | integrity sha512-PEXOSHFO85aj1aP4t+KGzvxQ00qXbjCysWlsDjlGkP1e9owNiYdpEkLej21Ax8LDD7xJ01rEmJDqZ/JPoW2GXw== 114 | 115 | json-as@^0.5.4: 116 | version "0.5.4" 117 | resolved "https://registry.yarnpkg.com/json-as/-/json-as-0.5.4.tgz#57f3d528a08357b65e1ca98696b851228186801f" 118 | integrity sha512-eTr7Yia9QXC8G4Lto1BPoaJpAa9EiiPKaUl/hdCT0nzlSFKl4cF9UwV98OQW7XySVn6rbq4AYX6I+RMO39ufPg== 119 | dependencies: 120 | as-string-sink "^0.5.0" 121 | as-variant "^0.4.0" 122 | 123 | lodash.clonedeep@^4.5.0: 124 | version "4.5.0" 125 | resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" 126 | integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== 127 | 128 | long@^5.2.0: 129 | version "5.2.1" 130 | resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" 131 | integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== 132 | 133 | ts-mixer@^6.0.2: 134 | version "6.0.2" 135 | resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.2.tgz#3e4e4bb8daffb24435f6980b15204cb5b287e016" 136 | integrity sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A== 137 | 138 | visitor-as@^0.11.3: 139 | version "0.11.3" 140 | resolved "https://registry.yarnpkg.com/visitor-as/-/visitor-as-0.11.3.tgz#93c0afcb4ca1564ad58ccebb06c868d4ee11cc13" 141 | integrity sha512-5dUn/URe/Ky2gPBFj+a5teKcGgWoOfpNiJ0ApGWBbrGWVZC/Nf8ATuCeeEGqMNFfzqhmqe6e68/dSHYiSYTDJw== 142 | dependencies: 143 | lodash.clonedeep "^4.5.0" 144 | ts-mixer "^6.0.2" 145 | -------------------------------------------------------------------------------- /contracts/cw-as-counter/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@assemblyscript/loader@^0.19.5": 6 | version "0.19.23" 7 | resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.19.23.tgz#7fccae28d0a2692869f1d1219d36093bc24d5e72" 8 | integrity sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw== 9 | 10 | as-base64@^0.2.0: 11 | version "0.2.0" 12 | resolved "https://registry.yarnpkg.com/as-base64/-/as-base64-0.2.0.tgz#b4bed937c93bfb6524b932b517e1b8571a0dd518" 13 | integrity sha512-j6JxprAVN4SXUcZiSChuMBf0JJOCdh5OfrHAepluxmFWuDZZm+YjmfNSk8djUHRPEB+Ui5HGvrz46GLvTJf3ig== 14 | 15 | as-container@^0.6.1: 16 | version "0.6.1" 17 | resolved "https://registry.yarnpkg.com/as-container/-/as-container-0.6.1.tgz#94806a91e96b68622c55f301fc04e6339a1228c2" 18 | integrity sha512-cgQ7P/dQAGeU2lLhPpinThYfFcUw3HzV8b00CoqV+l5Tsgpl7xhXdoc/2srCyK0um1BVAQwlrEpNTtthzkMF+g== 19 | 20 | as-string-sink@^0.5.0: 21 | version "0.5.2" 22 | resolved "https://registry.yarnpkg.com/as-string-sink/-/as-string-sink-0.5.2.tgz#39da159c01a2064a1dadd872a38c743369dd567f" 23 | integrity sha512-HJmhbgJWqfllV7ltZxpMaj/AxSkokb3MzYo1Qd+0HIB9zyzChu1SXBmNc3n0U3XrT6/NIeQvBUegxaXwoUNXAg== 24 | 25 | as-variant@^0.4.0: 26 | version "0.4.0" 27 | resolved "https://registry.yarnpkg.com/as-variant/-/as-variant-0.4.0.tgz#14dad464a7aef8bf61c5f587b396f6915459befa" 28 | integrity sha512-EJUrF7R/G+TQ8t/cH2S7Z9t3v4eKfggCr7HBQGVRXCk+eehs7YOfvR7ScTSAt6vsOui9S5zCkXa1gAGZ9F7vJg== 29 | dependencies: 30 | "@assemblyscript/loader" "^0.19.5" 31 | 32 | assemblyscript@^0.24.1: 33 | version "0.24.1" 34 | resolved "https://registry.yarnpkg.com/assemblyscript/-/assemblyscript-0.24.1.tgz#87a32da2cd2962ffdb4d44e159751b52d3594421" 35 | integrity sha512-uqR7NW0z/QFuqOkszoCb2F2jJI0VsmmmATRYG2JGzspC9nkct/8+6Pad7amCnIRriAC/T9AknEs+Qv/U/+fNpQ== 36 | dependencies: 37 | binaryen "110.0.0-nightly.20221105" 38 | long "^5.2.0" 39 | 40 | binaryen@110.0.0-nightly.20221105: 41 | version "110.0.0-nightly.20221105" 42 | resolved "https://registry.yarnpkg.com/binaryen/-/binaryen-110.0.0-nightly.20221105.tgz#9e3c47e8ffa31521acd125013dca3ceea143d0bf" 43 | integrity sha512-OBESOc51q3SwgG8Uv8nMzGnSq7LJpSB/Fu8B3AjlZg6YtCEwRnlDWlnwNB6mdql+VdexfKmNcsrs4K7MYidmdQ== 44 | 45 | binaryen@^111.0.0: 46 | version "111.0.0" 47 | resolved "https://registry.yarnpkg.com/binaryen/-/binaryen-111.0.0.tgz#dd970a11d8fe61959f77d609dfee3c19ad80b80a" 48 | integrity sha512-PEXOSHFO85aj1aP4t+KGzvxQ00qXbjCysWlsDjlGkP1e9owNiYdpEkLej21Ax8LDD7xJ01rEmJDqZ/JPoW2GXw== 49 | 50 | json-as@^0.5.4: 51 | version "0.5.4" 52 | resolved "https://registry.yarnpkg.com/json-as/-/json-as-0.5.4.tgz#57f3d528a08357b65e1ca98696b851228186801f" 53 | integrity sha512-eTr7Yia9QXC8G4Lto1BPoaJpAa9EiiPKaUl/hdCT0nzlSFKl4cF9UwV98OQW7XySVn6rbq4AYX6I+RMO39ufPg== 54 | dependencies: 55 | as-string-sink "^0.5.0" 56 | as-variant "^0.4.0" 57 | 58 | lodash.clonedeep@^4.5.0: 59 | version "4.5.0" 60 | resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" 61 | integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== 62 | 63 | long@^5.2.0: 64 | version "5.2.1" 65 | resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" 66 | integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== 67 | 68 | ts-mixer@^6.0.2: 69 | version "6.0.2" 70 | resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.2.tgz#3e4e4bb8daffb24435f6980b15204cb5b287e016" 71 | integrity sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A== 72 | 73 | visitor-as@^0.11.3: 74 | version "0.11.3" 75 | resolved "https://registry.yarnpkg.com/visitor-as/-/visitor-as-0.11.3.tgz#93c0afcb4ca1564ad58ccebb06c868d4ee11cc13" 76 | integrity sha512-5dUn/URe/Ky2gPBFj+a5teKcGgWoOfpNiJ0ApGWBbrGWVZC/Nf8ATuCeeEGqMNFfzqhmqe6e68/dSHYiSYTDJw== 77 | dependencies: 78 | lodash.clonedeep "^4.5.0" 79 | ts-mixer "^6.0.2" 80 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "useWorkspaces": true, 4 | "version": "0.0.8" 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cosmwasm-as", 3 | "private": true, 4 | "workspaces": [ 5 | "packages/*", 6 | "contracts/*" 7 | ], 8 | "devDependencies": { 9 | "lerna": "^6.0.3" 10 | }, 11 | "prettier": { 12 | "printWidth": 80, 13 | "semi": true, 14 | "singleQuote": true, 15 | "trailingComma": "es5", 16 | "useTabs": false, 17 | "tabWidth": 2 18 | }, 19 | "publishConfig": { 20 | "access": "public", 21 | "registry": "https://registry.npmjs.org/" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/rewrite-wasm/README.md: -------------------------------------------------------------------------------- 1 | # `@cosmwasm-as/rewrite-wasm` 2 | 3 | A tool for rewriting `.wasm` binaries outputted by the AssemblyScript compiler to be compatible with CosmWasm. 4 | 5 | ## Features 6 | 7 | - replaces `(start ...)` operation and inserts it before every exported function 8 | 9 | ## Installation 10 | 11 | Install globally: 12 | 13 | ```bash 14 | npm install -g @cosmwasm-as/rewrite-wasm 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```bash 20 | cosmwasm-as-rewrite-wasm [--optimize=1] <.wasm file> 21 | ``` 22 | 23 | - The `optimize=1` flag is used for release versions and uses Binaryen to optimize the result binary. 24 | - Binaries are rewritten **IN PLACE**. 25 | -------------------------------------------------------------------------------- /packages/rewrite-wasm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cosmwasm-as/rewrite-wasm", 3 | "version": "0.0.8", 4 | "type": "module", 5 | "devDependencies": { 6 | "binaryen": "^111.0.0", 7 | "minimist": "^1.2.7" 8 | }, 9 | "main": "rewrite-wasm.js", 10 | "bin": { 11 | "cosmwasm-as-rewrite-wasm": "./rewrite-wasm.js" 12 | }, 13 | "publishConfig": { 14 | "access": "public", 15 | "registry": "https://registry.npmjs.org/" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/rewrite-wasm/rewrite-wasm.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // rewrite the wasm file to call ~start before each entrypoint 3 | import {readFileSync, writeFileSync} from "fs"; 4 | import binaryen from "binaryen"; 5 | import minimist from "minimist"; 6 | 7 | const argv = minimist(process.argv.slice(2)); 8 | const wasm = readFileSync(argv._[0]); 9 | const mod = binaryen.readBinary(wasm); 10 | 11 | function addStartToFunction(funcName) { 12 | const func = mod.getFunction(funcName); 13 | if (func === 0) { // function does not exist 14 | return; 15 | } 16 | 17 | const funcInfo = binaryen.getFunctionInfo(func); 18 | const callStart = mod.call("~start", [], binaryen.none); 19 | const block = mod.block("", [callStart, funcInfo.body], funcInfo.results); 20 | mod.addFunction("tmp", funcInfo.params, funcInfo.results, funcInfo.vars, block); 21 | const tmpInfo = binaryen.getFunctionInfo(mod.getFunction("tmp")); 22 | mod.removeFunction(funcName); 23 | mod.addFunction(funcName, tmpInfo.params, tmpInfo.results, tmpInfo.vars, tmpInfo.body); 24 | mod.removeFunction("tmp"); 25 | } 26 | 27 | 28 | mod.removeExport("_start"); 29 | 30 | // get all exports 31 | const numExports = mod.getNumExports(); 32 | for (let i = 0; i < numExports; i++) { 33 | const exp = mod.getExportByIndex(i); 34 | const info = binaryen.getExportInfo(exp); 35 | // if it's a function, add a start call 36 | if (info.kind === binaryen.ExternalFunction) { 37 | addStartToFunction(info.value); 38 | } 39 | } 40 | 41 | // remove file extension from path 42 | let wasmName = argv._[0].split(".")[0]; 43 | 44 | if (argv['optimize']) { 45 | mod.optimize(); 46 | } 47 | 48 | mod.validate(); 49 | 50 | // write the new wat file 51 | writeFileSync(wasmName + ".wat", mod.emitText()); 52 | 53 | // write the new wasm file 54 | writeFileSync(wasmName + ".wasm", mod.emitBinary()); 55 | -------------------------------------------------------------------------------- /packages/std/README.md: -------------------------------------------------------------------------------- 1 | # `@cosmwasm-as/std` 2 | 3 | Standard library bringing many types / implementations provided by `cosmwasm-std`. 4 | 5 | Refer to [`cosmwasm-as`](https://github.com/terran-one/cosmwasm-as) main GitHub repo for information on usage. 6 | -------------------------------------------------------------------------------- /packages/std/as.ts: -------------------------------------------------------------------------------- 1 | // stuff to work nicely with AssemblyScript 2 | 3 | import {Region} from "./types"; 4 | import {abort} from "./imports"; 5 | 6 | /* 7 | * AssemblyScript normally imports a `env.abort`, which we need to override 8 | * because CosmWasm has its own `env.abort` function provided. This is an 9 | * adapter to make it work nicely with AssemblyScript. 10 | * 11 | * In your build script, you should have: 12 | * 13 | * ```bash 14 | * asc <...> --use abort=~lib/@cosmwasm-as/std/as/ABORT <...> 15 | * ``` 16 | */ 17 | export function ABORT( 18 | message: string | null, 19 | fileName: string | null, 20 | lineNumber: u32, 21 | columnNumber: u32, 22 | ): void { 23 | const msg = 24 | "Aborted with message '" + 25 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 26 | (message || "unset")! + 27 | " (in '" + 28 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 29 | (fileName || "unset")! + 30 | "', line " + 31 | lineNumber.toString() + 32 | ", column " + 33 | columnNumber.toString() + 34 | ")"; 35 | 36 | // Allocate msg 37 | const msgPtr = Region.allocateAndWriteStr(msg); 38 | abort(msgPtr.ptr); 39 | unreachable(); // crash hard 40 | } 41 | -------------------------------------------------------------------------------- /packages/std/cw-storage-plus.ts: -------------------------------------------------------------------------------- 1 | import {JSON} from "json-as/assembly"; 2 | import {Option, Result} from "as-container"; 3 | 4 | import {Region} from "./types"; 5 | import {db_read, db_write} from "./imports"; 6 | 7 | 8 | export class Storage { 9 | get(key: Uint8Array): Option { 10 | let rKey = Region.allocate(key.length); 11 | rKey.write(key); 12 | 13 | let res = db_read(rKey.ptr); 14 | let rRes = Region.fromPtr(res); 15 | return Option.Some(rRes.read()); 16 | } 17 | 18 | set(key: Uint8Array, value: Uint8Array): void { 19 | let rKey = Region.allocate(key.length); 20 | rKey.write(key); 21 | 22 | let rValue = Region.allocate(value.length); 23 | rValue.write(value); 24 | 25 | db_write(rKey.ptr, rValue.ptr); 26 | } 27 | } 28 | 29 | class StorageHelper { 30 | store: Storage; 31 | storageKey: Uint8Array; 32 | storageKeyStr: string; 33 | 34 | constructor(storageKey: string) { 35 | this.store = new Storage(); 36 | this.storageKeyStr = storageKey; 37 | this.storageKey = Uint8Array.wrap(String.UTF8.encode(storageKey)); 38 | } 39 | } 40 | 41 | export class Item extends StorageHelper { 42 | 43 | save(value: T): Result<'unit', string> { 44 | let valueBuffer = Uint8Array.wrap(String.UTF8.encode(JSON.stringify(value))); 45 | this.store.set(this.storageKey, valueBuffer); 46 | return Result.Ok<'unit', string>("unit"); 47 | } 48 | 49 | load(): Result { 50 | let valueBuffer = this.store.get(this.storageKey); 51 | if (valueBuffer.isNone) { 52 | return Result.Err("No value found"); 53 | } 54 | let value = JSON.parse(String.UTF8.decode(valueBuffer.unwrap().buffer)); 55 | 56 | return Result.Ok(value); 57 | } 58 | 59 | // TODO: to uncomment when AssemblyScript implements closures 60 | // update(action: (value: T) => Result): Result { 61 | // let input = this.load(); 62 | // if (input.isErr) { 63 | // return input; 64 | // } 65 | // let output = action(input.unwrap()); 66 | // if (output.isErr) { 67 | // return output; 68 | // } 69 | // 70 | // let res = this.save(output.unwrap()); 71 | // if (res.isErr) { 72 | // return Result.Err(res.unwrapErr()); 73 | // } 74 | // 75 | // return Result.Ok(output.unwrap()); 76 | // } 77 | } 78 | 79 | export class Map extends StorageHelper { 80 | 81 | private getKeyWithPrefix(key: K): Uint8Array { 82 | let keyWithPrefix = this.storageKeyStr.concat(JSON.stringify(key).slice(1, -1)); 83 | return Uint8Array.wrap(String.UTF8.encode(keyWithPrefix)); 84 | } 85 | 86 | save(key: K, value: V): Result<'unit', string> { 87 | // remove quotes 88 | let valueBuffer = Uint8Array.wrap(String.UTF8.encode(JSON.stringify(value))); 89 | this.store.set(this.getKeyWithPrefix(key), valueBuffer); 90 | return Result.Ok<'unit', string>("unit"); 91 | } 92 | 93 | load(key: K): Result { 94 | let valueBuffer = this.store.get(this.getKeyWithPrefix(key)); 95 | if (valueBuffer.isNone) { 96 | return Result.Err("No value found"); 97 | } 98 | let value = JSON.parse(String.UTF8.decode(valueBuffer.unwrap().buffer)); 99 | return Result.Ok(value); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /packages/std/exports.ts: -------------------------------------------------------------------------------- 1 | import {JSON} from "json-as"; 2 | import {Result} from "as-container"; 3 | 4 | import {Binary, Response, Env, Info, Region} from "./types"; 5 | import {abort} from "./imports"; 6 | 7 | @json 8 | class ErrorRes { 9 | error: string; 10 | } 11 | 12 | @json 13 | class OkRes { 14 | ok: T; 15 | } 16 | 17 | function stringifyResult(res: Result): string { 18 | if (res.isOk) { 19 | return JSON.stringify>({ok: res.unwrap()}); 20 | } else { 21 | return JSON.stringify({error: res.unwrapErr()}); 22 | } 23 | } 24 | 25 | type InstantiateFn = (env: Env, info: Info, msg: T) => Result; 26 | type ExecuteFn = (env: Env, info: Info, msg: T) => Result; 27 | type QueryFn = (env: Env, msg: T) => Result; 28 | 29 | export function do_instantiate(env: i32, info: i32, msg: i32, instantiateFn: InstantiateFn): i32 { 30 | let rEnv = Region.fromPtr(env); 31 | let rInfo = Region.fromPtr(info); 32 | let rMsg = Region.fromPtr(msg); 33 | 34 | let envObj = JSON.parse(rEnv.readStr()); 35 | let infoObj = JSON.parse(rInfo.readStr()); 36 | let msgObj = JSON.parse(rMsg.readStr()); 37 | 38 | let res = instantiateFn(envObj, infoObj, msgObj); 39 | let resJson = stringifyResult(res); 40 | return Region.allocateAndWriteStr(resJson).ptr; 41 | } 42 | 43 | export function do_execute(env: i32, info: i32, msg: i32, executeFn: ExecuteFn): i32 { 44 | let rEnv = Region.fromPtr(env); 45 | let rInfo = Region.fromPtr(info); 46 | let rMsg = Region.fromPtr(msg); 47 | 48 | let envObj = JSON.parse(rEnv.readStr()); 49 | let infoObj = JSON.parse(rInfo.readStr()); 50 | let msgObj = JSON.parse(rMsg.readStr()); 51 | 52 | let res = executeFn(envObj, infoObj, msgObj); 53 | let resJson = stringifyResult(res); 54 | return Region.allocateAndWriteStr(resJson).ptr; 55 | } 56 | 57 | export function do_query(env: i32, msg: i32, queryFn: QueryFn): i32 { 58 | let rEnv = Region.fromPtr(env); 59 | let rMsg = Region.fromPtr(msg); 60 | 61 | let envObj = JSON.parse(rEnv.readStr()); 62 | let msgObj = JSON.parse(rMsg.readStr()); 63 | 64 | let res = queryFn(envObj, msgObj); 65 | let resJson = stringifyResult(res); 66 | return Region.allocateAndWriteStr(resJson).ptr; 67 | } 68 | 69 | export function allocate(size: usize): usize { 70 | let ptr = heap.alloc(size); 71 | let regionPtr = heap.alloc(12); 72 | 73 | store(regionPtr, ptr); 74 | store(regionPtr + 4, size); 75 | store(regionPtr + 8, size); 76 | 77 | return regionPtr; 78 | } 79 | 80 | export function deallocate(regionPtr: usize): void { 81 | let ptr = load(regionPtr); 82 | heap.free(ptr); 83 | heap.free(regionPtr); 84 | } 85 | 86 | export function interface_version_8(): void { 87 | } 88 | -------------------------------------------------------------------------------- /packages/std/imports.ts: -------------------------------------------------------------------------------- 1 | @external("env", "abort") 2 | export declare function abort(messagePtr: usize): void; 3 | 4 | @external("env", "db_read") 5 | export declare function db_read(keyPtr: usize): usize; 6 | @external("env", "db_write") 7 | export declare function db_write(keyPtr: usize, valuePtr: usize): void; 8 | @external("env", "db_remove") 9 | export declare function db_remove(keyPtr: usize): void; 10 | 11 | // Iterator 12 | @external("env", "db_scan") 13 | export declare function db_scan(startPtr: usize, endPtr: usize, order: i32): usize; 14 | @external("env", "db_next") 15 | export declare function db_next(iteratorId: u32): usize; 16 | 17 | // Addr 18 | @external("env", "addr_validate") 19 | export declare function addr_validate(sourcePtr: usize): usize; 20 | @external("env", "addr_humanize") 21 | export declare function addr_humanize(sourcePtr: usize, destPtr: usize): usize; 22 | @external("env", "addr_canonicalize") 23 | export declare function addr_canonicalize(sourcePtr: usize, destPtr: usize): usize; 24 | 25 | @external("env", "sec256k1_verify") 26 | export declare function secp256k1_verify(messageHashPtr: usize, signaturePtr: usize, publicKeyPtr: usize): u32; 27 | @external("env", "secp256k1_recover_pubkey") 28 | export declare function secp256k1_recover_pubkey(messageHashPtr: usize, signaturePtr: usize, recoveryParam: u32): u64; 29 | @external("env", "ed25519_verify") 30 | export declare function ed25519_verify(messagePtr: usize, signaturePtr: usize, publicKeyPtr: usize): usize; 31 | @external("env", "ed25519_batch_verify") 32 | export declare function ed25519_batch_verify(messagesPtr: usize, signaturesPtr: usize, publicKeysPtr: usize): usize; 33 | 34 | @external("env", "debug") 35 | export declare function debug(sourcePtr: usize): void; 36 | 37 | @external("env", "query_chain") 38 | export declare function query_chain(requestPtr: usize): u32; 39 | -------------------------------------------------------------------------------- /packages/std/index.ts: -------------------------------------------------------------------------------- 1 | export * from './imports'; 2 | export * from './types'; 3 | export * from './exports'; 4 | export * from './as'; 5 | export * from './cw-storage-plus'; 6 | -------------------------------------------------------------------------------- /packages/std/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cosmwasm-as/std", 3 | "version": "0.0.8", 4 | "license": "MIT", 5 | "types": "index.ts", 6 | "main": "index.ts", 7 | "devDependencies": { 8 | "@terran-one/cosmwasm-vm-js": "^0.2.15", 9 | "@types/jest": "^29.2.2", 10 | "assemblyscript": "^0.22.0", 11 | "binaryen": "^111.0.0", 12 | "jest": "^29.3.1", 13 | "minimist": "^1.2.7", 14 | "ts-jest": "^29.0.3", 15 | "ts-node": "^10.9.1", 16 | "typescript": "^4.8.4" 17 | }, 18 | "scripts": { 19 | "build": "yarn asbuild", 20 | "asbuild:debug": "asc index.ts --target debug --disable bulk-memory --sourceMap --runtime stub --exportStart --use abort=assembly/index/__ABORT__ --debug && node util/rewrite-wasm.js build/debug.wasm", 21 | "asbuild:release": "asc index.ts --target release --disable bulk-memory --sourceMap --runtime stub --exportStart --use abort=assembly/index/__ABORT__ --debug && node util/rewrite-wasm.js build/release.wasm --optimize=1", 22 | "asbuild": "npm run asbuild:debug && npm run asbuild:release", 23 | "test": "jest" 24 | }, 25 | "prettier": { 26 | "printWidth": 80, 27 | "semi": true, 28 | "singleQuote": true, 29 | "trailingComma": "es5" 30 | }, 31 | "publishConfig": { 32 | "access": "public", 33 | "registry": "https://registry.npmjs.org/" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/std/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "assemblyscript/std/assembly.json", 3 | "include": [ 4 | "./**/*.ts" 5 | ], 6 | } 7 | -------------------------------------------------------------------------------- /packages/std/types.ts: -------------------------------------------------------------------------------- 1 | import {JSON} from "json-as/assembly"; 2 | import {allocate} from './exports'; 3 | import {encode, decode} from "as-base64/assembly"; 4 | import {Result} from "as-container"; 5 | 6 | @json 7 | export class Info { 8 | sender: string; 9 | funds: Coin[]; 10 | } 11 | 12 | 13 | @json 14 | export class Env { 15 | block: EnvBlock; 16 | contract: EnvContract; 17 | transaction_info: EnvTransaction | null; 18 | } 19 | 20 | @json 21 | export class EnvBlock { 22 | height: u64; 23 | time: string; 24 | chain_id: string; 25 | } 26 | 27 | @json 28 | export class EnvContract { 29 | address: string; 30 | } 31 | 32 | @json 33 | export class EnvTransaction { 34 | index: u64; 35 | } 36 | 37 | @json 38 | export class Coin { 39 | denom: string; 40 | amount: string; 41 | } 42 | 43 | @json 44 | export class Attribute { 45 | key: string; 46 | value: string; 47 | } 48 | 49 | @json 50 | export class Event { 51 | type: string; 52 | attributes: Attribute[]; 53 | } 54 | 55 | @json 56 | export class CosmosMsg { 57 | wasm: WasmMsg | null; 58 | bank: BankMsg | null; 59 | 60 | static WasmMsg(msg: WasmMsg): CosmosMsg { 61 | return { 62 | wasm: msg, 63 | bank: null, 64 | } 65 | } 66 | 67 | static BankMsg(msg: BankMsg): CosmosMsg { 68 | return { 69 | wasm: null, 70 | bank: msg, 71 | } 72 | } 73 | } 74 | 75 | @json 76 | export class BankMsg { 77 | send: BankSendMsg | null; 78 | } 79 | 80 | @json 81 | export class BankSendMsg { 82 | to_address: string; 83 | amount: Coin[]; 84 | } 85 | 86 | @json 87 | export class WasmMsg { 88 | execute: WasmExecuteMsg | null; 89 | instantiate: WasmInstantiateMsg | null; 90 | 91 | static Execute(msg: WasmExecuteMsg): WasmMsg { 92 | return { 93 | execute: msg, 94 | instantiate: null, 95 | } 96 | } 97 | 98 | static Instantiate(msg: WasmInstantiateMsg): WasmMsg { 99 | return { 100 | execute: null, 101 | instantiate: msg, 102 | } 103 | } 104 | } 105 | 106 | @json 107 | export class WasmExecuteMsg { 108 | contract_addr: string; 109 | msg: Binary; 110 | funds: Coin[]; 111 | } 112 | 113 | @json 114 | export class WasmInstantiateMsg { 115 | admin: string | null; 116 | code_id: u64; 117 | msg: Binary; 118 | funds: Coin[]; 119 | label: string; 120 | } 121 | 122 | 123 | @json 124 | export class Response { 125 | messages: SubMsg[]; 126 | attributes: Attribute[]; 127 | events: Event[]; 128 | data: Binary | null; 129 | 130 | constructor(messages: SubMsg[] = [], attributes: Attribute[] = [], events: Event[] = [], data: Binary | null = null) { 131 | this.messages = messages; 132 | this.attributes = attributes; 133 | this.events = events; 134 | this.data = data; 135 | } 136 | 137 | static new(): Response { 138 | return new Response(); 139 | } 140 | 141 | // alias 142 | static new_(): Response { 143 | return new Response(); 144 | } 145 | 146 | addMessage(msg: CosmosMsg): Response { 147 | this.messages.push({ 148 | id: 0, 149 | reply_on: "never", 150 | gas_limit: null, 151 | msg 152 | }); 153 | return this; 154 | } 155 | 156 | addSubmessage(submsg: SubMsg): Response { 157 | this.messages.push(submsg); 158 | return this; 159 | } 160 | 161 | addAttribute(key: string, value: string): Response { 162 | this.attributes.push({key, value}); 163 | return this; 164 | } 165 | 166 | addEvent(type: string, attributes: Attribute[]): Response { 167 | this.events.push({type, attributes}); 168 | return this; 169 | } 170 | 171 | setData(data: Binary): Response { 172 | this.data = data; 173 | return this; 174 | } 175 | } 176 | 177 | @json 178 | export class SubMsg { 179 | id: u64; 180 | msg: CosmosMsg; 181 | reply_on: string; 182 | gas_limit: Box | null = null; 183 | } 184 | 185 | export class Box { 186 | value: T; 187 | 188 | constructor(value: T) { 189 | this.value = value; 190 | } 191 | 192 | __JSON_Serialize(): string { 193 | return JSON.stringify(this.value); 194 | } 195 | } 196 | 197 | 198 | export class Binary { 199 | value: string; 200 | 201 | constructor(value: string) { 202 | this.value = value; 203 | } 204 | 205 | __JSON_Serialize(): string { 206 | let stringifiedBzUtf8 = Uint8Array.wrap(String.UTF8.encode(this.value)); 207 | return JSON.stringify(encode(stringifiedBzUtf8)); 208 | } 209 | } 210 | 211 | export function to_binary(value: T): Result { 212 | return Result.Ok(new Binary(JSON.stringify(value))); 213 | } 214 | 215 | export function from_binary(value: string): Result { 216 | let bzB64 = decode(value); 217 | let bzUtf8 = String.UTF8.decode(bzB64.buffer); 218 | return Result.Ok(JSON.parse(bzUtf8)); 219 | } 220 | 221 | export class Region { 222 | constructor(public ptr: u32, public offset: u32, public capacity: u32, public length: u32) { 223 | } 224 | 225 | public static fromPtr(ptr: usize): Region { 226 | let offset = load(ptr); 227 | let capacity = load(ptr + 4); 228 | let length = load(ptr + 8); 229 | return new Region(ptr, offset, capacity, length); 230 | } 231 | 232 | public static allocate(size: u32): Region { 233 | let regionPtr = allocate(size); 234 | return Region.fromPtr(regionPtr); 235 | } 236 | 237 | public static allocateAndWriteStr(str: string): Region { 238 | let region = Region.allocate(str.length); 239 | region.writeStr(str); 240 | return region; 241 | } 242 | 243 | public static allocateAndWrite(data: Uint8Array): Region { 244 | let region = Region.allocate(data.byteLength); 245 | region.write(data); 246 | return region; 247 | } 248 | 249 | write(data: Uint8Array): void { 250 | memory.copy(this.offset, data.dataStart, data.byteLength); 251 | } 252 | 253 | writeStr(str: string): void { 254 | let strBuffer = String.UTF8.encode(str, true); 255 | this.write(Uint8Array.wrap(strBuffer)); 256 | } 257 | 258 | read(): Uint8Array { 259 | let buffer = new DataView(new ArrayBuffer(this.length)); 260 | memory.copy(buffer.dataStart, this.offset, this.length); 261 | return Uint8Array.wrap(buffer.buffer); 262 | } 263 | 264 | readStr(): string { 265 | return String.UTF8.decode(this.read().buffer); 266 | } 267 | } 268 | --------------------------------------------------------------------------------