dfinity / motoko

Simple high-level language for writing Internet Computer canisters

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug: from_candid following to_candid fails.

AStepanov25 opened this issue · comments

from_candid called after to_candid produces null.

// @testmode wasi

type GlobalId = (streamId : Nat, queueNumber : Nat);
type TxOutput = { #ftTransfer : { amount : Nat; fee : Nat } };

let a = to_candid (((3, 0), #ftTransfer({ amount = 0; fee = 0 })) : (GlobalId, TxOutput));

assert (from_candid a : ?(GlobalId, TxOutput)) != null;

Minified example:

// @testmode wasi

type T = (Nat, Nat);

let a = (0, 0) : T;

assert (from_candid (to_candid (a)) : ?T) == ?a;

mops.toml:

[toolchain]
moc = "0.10.3"
wasmtime = "16.0.0"

It's a tricky question, but working as intended. from_candid is using the top-level tuple to represent multiple values. So in your minified example, from_candid is asking for two values, Nat and Nat, not a single tuple.

A related forum discussion: https://forum.dfinity.org/t/candid-and-tuples/17800/7?u=chenyan

Related, re-opened issue #4130 that suggest two possible enhancements.

Possible rewrite, tried in playground:

https://play.motoko.org/?tag=3119111542

actor {

  public func test1() : async () { // fails
    type GlobalId = (streamId : Nat, queueNumber : Nat);
    type TxOutput = { #ftTransfer : { amount : Nat; fee : Nat } };

    let a = to_candid (((3, 0), #ftTransfer({ amount = 0; fee = 0 })) : (GlobalId, TxOutput));

    assert (from_candid a : ?(GlobalId, TxOutput)) != null;
  };

  public func test2() : async () { // succeeds
    type GlobalId = (streamId : Nat, queueNumber : Nat);
    type TxOutput = { #ftTransfer : { amount : Nat; fee : Nat } };

    let a = to_candid ((3, 0) : GlobalId, #ftTransfer({ amount = 0; fee = 0 }) : TxOutput);

    assert (from_candid a : ?(GlobalId, TxOutput)) != null;
  }

}

Also this works:

  public func test3() : async () { // succeeds
    type GlobalId = (streamId : Nat, queueNumber : Nat);
    type TxOutput = { #ftTransfer : { amount : Nat; fee : Nat } };

    let a = to_candid ((3, 0) : GlobalId, #ftTransfer({ amount = 0; fee = 0 }) : TxOutput);

    assert (from_candid a : ??(GlobalId, TxOutput)) != null;
  };

And this:

// @testmode wasi
let a : (Nat, Nat) = (0, 0);

assert (from_candid to_candid (a.0, a.1) : ?(Nat, Nat)) == ?a;
assert (from_candid to_candid ((a.0, a.1)) : ??(Nat, Nat)) == ??a;
assert (from_candid to_candid ((a.0, a.1), a) : ?((Nat, Nat), (Nat, Nat))) == ?(a, a);
assert (from_candid to_candid (a) : ??(Nat, Nat)) == ??a;

let b : Blob = to_candid ((0,0) : (Nat,Nat));
let x : ?{ _0_ : Nat; _1_ : Nat } = from_candid b;