├── .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 | 
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 |
--------------------------------------------------------------------------------