Skip to content

Commit

Permalink
Merge pull request #27 from NatLabs/icrc3
Browse files Browse the repository at this point in the history
Icrc3 value type
  • Loading branch information
tomijaga authored May 9, 2024
2 parents 2046351 + 44aeeeb commit bfe08d2
Show file tree
Hide file tree
Showing 34 changed files with 281 additions and 183 deletions.
93 changes: 55 additions & 38 deletions .github/workflows/makefile.yml
Original file line number Diff line number Diff line change
@@ -1,42 +1,59 @@
name: Makefile CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
on:
push:
branches:
- main
pull_request:
branches:
- "*"

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: aviate-labs/setup-dfx@v0.2.5
with:
dfx-version: 0.14.1

- name: install wasmtime
run: |
curl https://wasmtime.dev/install.sh -sSf | bash
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
npm --yes -g i mocv

- name: Select mocv version
run: mocv use 0.10.3

- name: install mops
run: |
npm --yes -g i ic-mops@0.34.3
mops i
mops sources
# - name: Detect Warnings
# run: make no-warn

- name: Run Tests
run: mops test
build:
runs-on: ubuntu-latest

name: Build and test
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18

- name: Cache Node modules
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Cache mops packages
uses: actions/cache@v3
with:
key: mops-packages-${{ hashFiles('mops.toml') }}
path: |
~/.cache/mops
~/mops
- name: Install dfx
uses: dfinity/setup-dfx@main
- name: Confirm successful installation
run: dfx --version

- name: Install dfx cache
run: dfx cache install

- name: Install mops & mocv
run: |
npm --yes -g i ic-mops
mops i
mops toolchain init
# set moc path for dfx to use
echo "DFX_MOC_PATH=$(mops toolchain bin moc)" >> $GITHUB_ENV
# - name: Detect warnings
# run: make check

- name: Run Tests
run: mops test
16 changes: 8 additions & 8 deletions makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
.PHONY: compile-tests no-warn docs
.PHONY: test compile-tests docs no-warn

compile-tests:
bash compile-tests.sh $(file)
test:
mops test

no-warn:
find src -type f -name '*.mo' -print0 | xargs -0 $(shell mocv bin)/moc -r $(shell mops sources) -Werror -wasi-system-api
check:
find src -type f -name '*.mo' -print0 | \
xargs -0 $(shell mops toolchain bin moc) -r $(shell mops sources) -Werror -wasi-system-api

docs:
$(shell mocv bin)/mo-doc
$(shell mocv bin)/mo-doc --format plain
bench:
mops bench --gc incremental
12 changes: 7 additions & 5 deletions mops.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
[package]
name = "serde"
version = "2.2.0"
version = "2.3.0"
description = "A serialisation and deserialisation library for Motoko."
repository = "https://github.com/NatLabs/serde"
keywords = [ "json", "candid", "cbor", "urlencoded", "serialization" ]
license = "MIT"

[dependencies]
base = "0.10.3"
itertools = "0.1.2"
base = "0.11.1"
itertools = "0.2.1"
candid = "1.0.2"
xtended-numbers = "0.2.1"
json = "https://github.com/NatLabs/json.mo#float"
parser-combinators = "https://github.com/aviate-labs/parser-combinators.mo#v0.1.2@6a331bf78e9dcd7623977f06c8e561fd1a8c0103"
cbor = "0.1.3"

[dev-dependencies]
test = "1.2.0"
test = "2.0.0"

[toolchain]
wasmtime = "14.0.4"
wasmtime = "14.0.4"
moc = "0.11.1"
5 changes: 3 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The library contains four modules:
- `toText()` - Converts serialized candid to its [textual representation](https://internetcomputer.org/docs/current/tutorials/developer-journey/level-2/2.4-intro-candid/#candid-textual-values).
- `encode()` - Converts the [Candid variant](./src/Candid/Types.mo#L6) to a blob.
- `decode()` - Converts a blob to the [Candid variant](./src/Candid/Types.mo#L6).

> encoding and decoding functions also support conversion between the [`ICRC3` value type](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-3#value) and candid. Checkout the example in the [usage guide](./usage.md#icrc3-value)
- **CBOR**
- `encode()` - Converts serialized candid to CBOR.
- `decode()` - Converts CBOR to a serialized candid.
Expand Down Expand Up @@ -159,13 +159,14 @@ assert renamedKeys == ?{ item_type = "bar"; item_label = "foo"; id = 112 };
```

Checkout the [usage guide](https://github.com/NatLabs/serde/blob/main/usage.md) for additional examples:
- [Candid Text](https://github.com/NatLabs/serde/blob/main/usage.md#candid-text)
- [Candid](https://github.com/NatLabs/serde/blob/main/usage.md#candid-text)
- [URL-Encoded Pairs](https://github.com/NatLabs/serde/blob/main/usage.md#url-encoded-pairs)

## Limitations

- Users must provide a list of record keys and variant names during conversions from Motoko to other data formats due to constraints in the candid format.
- Lack of specific syntax for conversion between `Blob`, `Principal`, and bounded `Nat`/`Int` types.
- Floats are only recognised if they have a decimal point, e.g., `1.0` is a Float, but `1` is an `Int` / `Nat`.

## Running Tests

Expand Down
38 changes: 21 additions & 17 deletions src/CBOR/lib.mo
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Int8 "mo:base/Int8";
import Int16 "mo:base/Int16";
import Int32 "mo:base/Int32";
import Int64 "mo:base/Int64";
import Option "mo:base/Option";
import Nat64 "mo:base/Nat64";
import Result "mo:base/Result";
import Principal "mo:base/Principal";
Expand All @@ -12,7 +13,6 @@ import CBOR_Value "mo:cbor/Value";
import CBOR_Encoder "mo:cbor/Encoder";
import CBOR_Decoder "mo:cbor/Decoder";
import NatX "mo:xtended-numbers/NatX";
import IntX "mo:xtended-numbers/IntX";
import FloatX "mo:xtended-numbers/FloatX";

import Candid "../Candid";
Expand All @@ -32,14 +32,14 @@ module {
let decoded_res = Candid.decode(blob, keys, options);
let #ok(candid) = decoded_res else return Utils.send_error(decoded_res);

let json_res = fromCandid(candid[0]);
let json_res = fromCandid(candid[0], Option.get(options, CandidTypes.defaultOptions));
let #ok(json) = json_res else return Utils.send_error(json_res);
#ok(json);
};

/// Convert a Candid value to CBOR blob
public func fromCandid(candid : Candid) : Result<Blob, Text> {
let res = transpile_candid_to_cbor(candid);
public func fromCandid(candid : Candid, options: CandidTypes.Options) : Result<Blob, Text> {
let res = transpile_candid_to_cbor(candid, options);
let #ok(transpiled_cbor) = res else return Utils.send_error(res);

let cbor_with_self_describe_tag = #majorType6({ tag = 55799 : Nat64; value = transpiled_cbor; });
Expand All @@ -50,7 +50,7 @@ module {
};
};

func transpile_candid_to_cbor(candid : Candid) : Result<CBOR, Text> {
func transpile_candid_to_cbor(candid : Candid, options: CandidTypes.Options) : Result<CBOR, Text> {
let transpiled_cbor : CBOR = switch(candid){
case (#Empty) #majorType7(#_undefined);
case (#Null) #majorType7(#_null);
Expand All @@ -75,18 +75,18 @@ module {
let buffer = Buffer.Buffer<CBOR>(arr.size());

for (item in arr.vals()){
let res = transpile_candid_to_cbor(item);
let res = transpile_candid_to_cbor(item, options);
let #ok(cbor_val) = res else return Utils.send_error(res);
buffer.add(cbor_val);
};

#majorType4(Buffer.toArray(buffer));
};
case (#Record(records)) {
case (#Record(records) or #Map(records)) {
let newRecords = Buffer.Buffer<(CBOR, CBOR)>(records.size());

for ((key, val) in records.vals()){
let res = transpile_candid_to_cbor(val);
let res = transpile_candid_to_cbor(val, options);
let #ok(cbor_val) = res else return Utils.send_error(res);
newRecords.add((#majorType3(key), cbor_val));
};
Expand All @@ -100,7 +100,7 @@ module {
//
// check out "CBOR Tests.options" in the tests folder to see how this in action
case (#Option(option)) {
let res = transpile_candid_to_cbor(option);
let res = transpile_candid_to_cbor(option, options);
let #ok(cbor_val) = res else return Utils.send_error(res);
cbor_val
};
Expand All @@ -116,18 +116,18 @@ module {
};

public func decode(blob: Blob, options: ?Options): Result<Blob, Text> {
let candid_res = toCandid(blob);
let candid_res = toCandid(blob, Option.get(options, CandidTypes.defaultOptions));
let #ok(candid) = candid_res else return Utils.send_error(candid_res);
Candid.encodeOne(candid, options);
};

public func toCandid(blob: Blob): Result<Candid, Text> {
public func toCandid(blob: Blob, options: CandidTypes.Options): Result<Candid, Text> {
let cbor_res = CBOR_Decoder.decode(blob);

let candid_res = switch (cbor_res) {
case (#ok(cbor)) {
let #majorType6({ tag = 55799; value }) = cbor else return transpile_cbor_to_candid(cbor);
transpile_cbor_to_candid(value);
let #majorType6({ tag = 55799; value }) = cbor else return transpile_cbor_to_candid(cbor, options);
transpile_cbor_to_candid(value, options);
};
case (#err(cbor_error)) {
switch(cbor_error){
Expand All @@ -142,7 +142,7 @@ module {
#ok(candid);
};

public func transpile_cbor_to_candid(cbor: CBOR) : Result<Candid, Text>{
public func transpile_cbor_to_candid(cbor: CBOR, options: CandidTypes.Options) : Result<Candid, Text>{
let transpiled_candid = switch(cbor){
case (#majorType0(n)) #Nat(Nat64.toNat(n));
case (#majorType1(n)) #Int(n);
Expand All @@ -151,7 +151,7 @@ module {
case (#majorType4(arr)) {
let buffer = Buffer.Buffer<Candid>(arr.size());
for (item in arr.vals()){
let res = transpile_cbor_to_candid(item);
let res = transpile_cbor_to_candid(item, options);
let #ok(candid_val) = res else return Utils.send_error(res);
buffer.add(candid_val);
};
Expand All @@ -162,12 +162,16 @@ module {
for ((cbor_text, val) in records.vals()){
let #majorType3(key) = cbor_text else return #err("Error decoding CBOR: Unexpected key type");

let res = transpile_cbor_to_candid(val);
let res = transpile_cbor_to_candid(val, options);
let #ok(candid_val) = res else return Utils.send_error(res);
buffer.add((key, candid_val));
};

#Record(Buffer.toArray(buffer));
if (options.use_icrc_3_value_type){
#Map(Buffer.toArray(buffer));
} else {
#Record(Buffer.toArray(buffer));
};
};
case (#majorType7(#_undefined)) #Empty;
case (#majorType7(#_null)) #Null;
Expand Down
Loading

0 comments on commit bfe08d2

Please sign in to comment.