{"id":13524527,"url":"https://github.com/edjcase/motoko_candid","last_synced_at":"2026-01-23T11:49:18.158Z","repository":{"id":87351009,"uuid":"524309518","full_name":"edjCase/motoko_candid","owner":"edjCase","description":null,"archived":false,"fork":false,"pushed_at":"2024-06-30T23:23:54.000Z","size":114,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-01T02:36:06.259Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Motoko","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edjCase.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-13T05:23:40.000Z","updated_at":"2024-08-12T07:43:13.000Z","dependencies_parsed_at":null,"dependency_job_id":"f440d31c-dfe8-4d9c-b61a-4f8c14b57c87","html_url":"https://github.com/edjCase/motoko_candid","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edjCase%2Fmotoko_candid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edjCase%2Fmotoko_candid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edjCase%2Fmotoko_candid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edjCase%2Fmotoko_candid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edjCase","download_url":"https://codeload.github.com/edjCase/motoko_candid/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248819370,"owners_count":21166474,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:10.959Z","updated_at":"2026-01-23T11:49:18.126Z","avatar_url":"https://github.com/edjCase.png","language":"Motoko","funding_links":[],"categories":["Candid","Libraries"],"sub_categories":["Candid implementations","Encoding"],"readme":"# Motoko Candid\n\n[![MOPS](https://img.shields.io/badge/MOPS-candid-blue)](https://mops.one/candid)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/edjCase/motoko_candid/blob/main/LICENSE)\n\nA comprehensive Motoko implementation of the Candid binary format for encoding and decoding structured data on the Internet Computer.\n\n## Funding\n\nThis library was originally incentivized by [ICDevs](https://ICDevs.org). You\ncan view more about the bounty on the\n[forum](https://forum.dfinity.org/t/icdevs-org-bounty-18-cbor-and-candid-motoko-parser-3-000/11398)\nor [website](https://icdevs.org/bounties/2022/02/22/CBOR-and-Candid-Motoko-Parser.html). The\nbounty was funded by The ICDevs.org commuity and the award paid to\n@Gekctek. If you use this library and gain value from it, please consider\na [donation](https://icdevs.org/donations.html) to ICDevs.\n\n## Package\n\n### MOPS\n\n```bash\nmops add candid\n```\n\nTo set up MOPS package manager, follow the instructions from the [MOPS Site](https://mops.one)\n\n## What is Candid?\n\nCandid is an interface description language (IDL) developed by the DFINITY Foundation for the Internet Computer. It provides a language-agnostic way to describe the public interfaces of services, including the signatures of methods and the structure of data. This library implements the binary serialization format that allows Motoko programs to encode and decode Candid data.\n\n## Supported Features\n\n-   **Complete Candid type system support** including all primitive and compound types\n-   **Type-safe value representation** with comprehensive type definitions\n-   **Binary encoding/decoding** following the official Candid specification\n-   **Text parsing and generation** for human-readable value representation\n-   **Automatic type inference** to derive minimal types from values\n-   **Streaming support** with buffer-based encoding for memory efficiency\n-   **Full round-trip fidelity** between Motoko types and binary format\n-   **Principal and service support** for Internet Computer integration\n-   **Comparison and hashing** utilities for types and values\n-   **Rich type system** including:\n    -   All integer types (Int, Int8, Int16, Int32, Int64, Nat, Nat8, Nat16, Nat32, Nat64)\n    -   Floating-point types (Float32, Float64)\n    -   Text and Boolean types\n    -   Optional and Vector types\n    -   Record and Variant types\n    -   Function and Service types\n    -   Principal type\n\n## Quick Start\n\nThe `lib.mo` module provides the primary API for working with Candid data. Import it as `mo:candid` to access all core functionality including encoding/decoding bytes and text representation. Additional modules (`Arg`, `Value`, `Type`) are available for advanced operations like type comparison, custom text formatting, and type utilities.\n\n### Example 1: Basic Encoding and Decoding\n\n```motoko\nimport Candid \"mo:candid\";\nimport Runtime \"mo:core/Runtime\";\n\n// Create arguments with values and types\nlet args : [Candid.Arg] = [\n    {\n        value = #text(\"Hello, Candid!\");\n        type_ = #text\n    },\n    {\n        value = #nat(42);\n        type_ = #nat\n    },\n    {\n        value = #bool(true);\n        type_ = #bool\n    }\n];\n\n// Encode to bytes\nlet bytes: [Nat8] = Candid.toBytes(args);\n\n// Decode back to arguments\nlet ?decodedArgs = Candid.fromBytes(bytes.vals()) else Runtime.trap(\"Failed to decode candid\");\n```\n\n### Example 2: Buffer-based Encoding\n\n```motoko\nimport Candid \"mo:candid\";\nimport List \"mo:core/List\";\n\nlet args : [Candid.Arg] = [\n    {\n        value = #record([\n            { tag = #name(\"name\"); value = #text(\"Alice\") },\n            { tag = #name(\"age\"); value = #nat(30) }\n        ]);\n        type_ = #record([\n            { tag = #name(\"name\"); type_ = #text },\n            { tag = #name(\"age\"); type_ = #nat }\n        ])\n    }\n];\n\n// Create a buffer for streaming encoding\nlet buffer = List.empty\u003cNat8\u003e();\n\n// Encode to buffer\nCandid.toBytesBuffer(Buffer.fromList(buffer), args);\n\n// Buffer now contains the encoded data\nlet encodedBytes = List.toArray(buffer);\n```\n\n### Example 3: Working with Complex Types\n\n```motoko\nimport Candid \"mo:candid\";\n\n// Create a variant value\nlet variantArg : Candid.Arg = {\n    value = #variant({ tag = #name(\"success\"); value = #text(\"Operation completed\") });\n    type_ = #variant([\n        { tag = #name(\"success\"); type_ = #text },\n        { tag = #name(\"error\"); type_ = #text }\n    ]);\n};\n\n// Create an optional value\nlet optionalArg : Candid.Arg = {\n    value = #opt(#nat(123));\n    type_ = #opt(#nat);\n};\n\n// Create a vector value\nlet vectorArg : Candid.Arg = {\n    value = #vector([#nat(1), #nat(2), #nat(3)]);\n    type_ = #vector(#nat);\n};\n\nlet args = [variantArg, optionalArg, vectorArg];\nlet bytes = Candid.toBytes(args);\n```\n\n### Example 4: Text Representation\n\n```motoko\nimport Candid \"mo:candid\";\n\n// Create arguments\nlet args : [Candid.Arg] = [\n    {\n        value = #record([\n            { tag = #name(\"name\"); value = #text(\"Alice\") },\n            { tag = #name(\"age\"); value = #nat(30) }\n        ]);\n        type_ = #record([\n            { tag = #name(\"name\"); type_ = #text },\n            { tag = #name(\"age\"); type_ = #nat }\n        ])\n    }\n];\n\n// Convert arguments to text\nlet text = Candid.toText(args);\n// text is \"(record { age = 30; name = \"Alice\" })\"\n\n// Parse arguments from text\nlet result = Candid.fromText(\"(42, \\\"hello\\\", true)\");\nswitch (result) {\n    case (#ok(parsedArgs)) {\n        // parsedArgs contains the parsed arguments with inferred types\n        let bytes = Candid.toBytes(parsedArgs);\n    };\n    case (#err(e)) { /* Handle error */ };\n};\n```\n\n### Example 5: Advanced Value Operations\n\nFor advanced operations on individual values, types, or arguments, use the specialized modules:\n\n```motoko\nimport CandidValue \"mo:candid/Value\";\nimport CandidType \"mo:candid/Type\";\n\n// Parse a Value from text (returns value and its type)\nlet textValue = \"record { age = 30; name = \\\"Alice\\\"; active = true }\";\nlet result = CandidValue.fromText(textValue);\n\nswitch (result) {\n  case (#ok((value, type_))) {\n    // Convert back to text with different formats\n    let compactText = CandidValue.toText(value);\n    let indentedText = CandidValue.toTextIndented(value);\n\n    // Compare values\n    let value2 = #record([{ tag = #name(\"age\"); value = #nat(30) }]);\n    let areEqual = CandidValue.equal(value, value2);\n\n    // Get the implicit type from a value\n    let implicitType = CandidValue.toImplicitType(value);\n\n    // Type operations\n    let typeHash = CandidType.hash(type_);\n    let typeText = CandidType.toText(type_);\n  };\n  case (#err(e)) {\n    // Handle parse error\n  };\n};\n```\n\n## API Reference\n\n### Main Module (`mo:candid`)\n\nThe primary API for encoding/decoding Candid data. Import this module for standard operations:\n\n```motoko\nimport Candid \"mo:candid\";\n```\n\n#### Core Functions\n\n-   **`toBytes(args : [Arg]) : [Nat8]`** - Encode arguments to binary format\n-   **`fromBytes(bytes : Iter.Iter\u003cNat8\u003e) : ?[Arg]`** - Decode binary data to arguments\n-   **`toBytesBuffer(buffer : Buffer.Buffer\u003cNat8\u003e, args : [Arg])`** - Stream encoding to a buffer\n-   **`toText(args : [Arg]) : Text`** - Convert arguments to text representation\n-   **`fromText(text : Text) : Result.Result\u003c[Arg], Text\u003e`** - Parse arguments from text\n\n### Advanced Modules\n\nFor specialized operations, import the specific modules:\n\n-   **`mo:candid/Value`** - Value operations (comparison, text formatting, type inference)\n-   **`mo:candid/Type`** - Type operations (comparison, hashing, text representation)\n-   **`mo:candid/Arg`** - Argument utilities (same functions as main module, plus `fromValue`)\n-   **`mo:candid/Tag`** - Tag hashing utilities\n-   **`mo:candid/FuncMode`** - Function mode type definitions\n\n### Types\n\n```motoko\n// Main argument type combining value and type information\npublic type Arg = {\n    value : Value;\n    type_ : Type;\n};\n\n// Comprehensive value type supporting all Candid data types\npublic type Value = {\n    #int : Int;                    // Arbitrary precision integers\n    #int8 : Int8;                  // 8-bit signed integers\n    #int16 : Int16;                // 16-bit signed integers\n    #int32 : Int32;                // 32-bit signed integers\n    #int64 : Int64;                // 64-bit signed integers\n    #nat : Nat;                    // Natural numbers\n    #nat8 : Nat8;                  // 8-bit natural numbers\n    #nat16 : Nat16;                // 16-bit natural numbers\n    #nat32 : Nat32;                // 32-bit natural numbers\n    #nat64 : Nat64;                // 64-bit natural numbers\n    #bool : Bool;                  // Boolean values\n    #float32 : Float;              // 32-bit floating point\n    #float64 : Float;              // 64-bit floating point\n    #text : Text;                  // UTF-8 text strings\n    #null_;                        // Null value\n    #reserved;                     // Reserved placeholder\n    #empty;                        // Empty type\n    #opt : Value;                  // Optional values\n    #vector : [Value];             // Homogeneous arrays\n    #record : [RecordFieldValue];  // Named field records\n    #variant : VariantOptionValue; // Tagged union types\n    #func_ : Func;                 // Function references\n    #service : Principal;          // Service references\n    #principal : Principal;        // Principal identifiers\n};\n\n// Record field with tag and value\npublic type RecordFieldValue = {\n    tag : Tag;\n    value : Value;\n};\n\n// Function reference\npublic type Func = {\n    service : Principal;\n    method : Text;\n};\n\n// Type system mirroring the value system\npublic type Type = {\n    #int; #int8; #int16; #int32; #int64;\n    #nat; #nat8; #nat16; #nat32; #nat64;\n    #bool; #float32; #float64; #text;\n    #null_; #reserved; #empty; #principal;\n    #opt : Type;\n    #vector : Type;\n    #record : [RecordFieldType];\n    #variant : [VariantOptionType];\n    #func_ : FuncType;\n    #service : ServiceType;\n    #recursive : {\n        id : Nat32;\n        type_ : Type;\n    };\n};\n\n// Tag for field identification\npublic type Tag = {\n    #hash : Nat32;    // Hash-based tag\n    #name : Text;     // Name-based tag\n};\n```\n\n### Additional Module Functions\n\n#### Value Module (`mo:candid/Value`)\n\nFor operations on individual values:\n\n```motoko\n// Parse a Value from text (returns value and inferred type)\npublic func fromText(text : Text) : Result.Result\u003c(Value, Type), Text\u003e;\n\n// Convert Value to text\npublic func toText(value : Value) : Text;\npublic func toTextIndented(value : Value) : Text;\npublic func toTextAdvanced(value : Value, options : ToTextOptions) : Text;\n\n// Get the implicit minimum type for a Value\npublic func toImplicitType(value : Value) : Type;\n\n// Compare Values\npublic func equal(v1 : Value, v2 : Value) : Bool;\npublic func compare(v1 : Value, v2 : Value) : Order.Order;\n```\n\n#### Type Module (`mo:candid/Type`)\n\nFor operations on types:\n\n```motoko\n// Convert Type to text\npublic func toText(type_ : Type) : Text;\npublic func toTextIndented(type_ : Type) : Text;\npublic func toTextAdvanced(type_ : Type, options : ToTextOptions) : Text;\n\n// Compare Types\npublic func equal(t1 : Type, t2 : Type) : Bool;\npublic func compare(t1 : Type, t2 : Type) : Order.Order;\n\n// Compute hash of a Type\npublic func hash(t : Type) : Nat32;\npublic func hashText(t : Text) : Nat32;\n```\n\n#### Arg Module (`mo:candid/Arg`)\n\nSame functions as main module, plus:\n\n```motoko\n// Create an Arg from a Value by inferring its implicit type\npublic func fromValue(value : Value) : Arg;\n```\n\n## Candid Type System\n\nThis implementation supports the complete Candid type system:\n\n### Text Representation\n\nThe library supports parsing and generating text representations of Candid values:\n\n-   **Parsing**: Convert text like `\"record { age = 30; name = \\\"Alice\\\" }\"` to a `Value`\n-   **Compact format**: Single-line text representation for all values\n-   **Indented format**: Multi-line formatted output for complex structures\n-   **Custom mapping**: Map hash-based field tags to meaningful names\n-   **Override support**: Custom text rendering for specific value types\n\nSupported text syntax includes:\n\n-   Primitive values: `42`, `true`, `\"text\"`, `null`\n-   Optional values: `opt 42`\n-   Vectors: `vec { 1; 2; 3 }`\n-   Records: `record { field1 = value1; field2 = value2 }`\n-   Variants: `variant { tag = value }`\n-   Tuples: `record { value1; value2 }` (fields numbered 0, 1, 2, ...)\n-   Principals: `principal \"aaaaa-aa\"`\n-   Services: `service \"aaaaa-aa\"`\n-   Functions: `func \"aaaaa-aa\".methodName`\n-   Comments: `// line comment` and `/* block comment */`\n-   Hex numbers: `0x1a2b`, `0x1a.2bp10`\n-   Type annotations: `(42 : nat)` (parsed but type info is discarded)\n\n### Primitive Types\n\n-   **Integers**: `int`, `int8`, `int16`, `int32`, `int64`\n-   **Natural Numbers**: `nat`, `nat8`, `nat16`, `nat32`, `nat64`\n-   **Floating Point**: `float32`, `float64`\n-   **Text**: UTF-8 encoded strings\n-   **Boolean**: `bool`\n-   **Special**: `null`, `reserved`, `empty`\n-   **Principal**: Internet Computer principal identifiers\n\n### Compound Types\n\n-   **Optional**: `opt T` - nullable values\n-   **Vector**: `vec T` - homogeneous arrays\n-   **Record**: `record { field1: T1; field2: T2 }` - structured data with named fields\n-   **Variant**: `variant { option1: T1; option2: T2 }` - tagged union types\n-   **Function**: `func (args) -\u003e (results)` - function signatures\n-   **Service**: Service type definitions with method signatures\n\n### Type Features\n\n-   **Recursive Types**: Support for self-referential type definitions\n-   **Field Tags**: Both hash-based and name-based field identification\n-   **Type Safety**: Compile-time type checking with runtime validation\n-   **Subtyping**: Compatible with Candid's structural subtyping rules\n\n## Error Handling\n\nThe library uses option types for error handling:\n\n-   `toBytes()` returns `[Nat8]` (never fails with valid input)\n-   `fromBytes()` returns `?[Arg]` (null on invalid input)\n-   Buffer operations are infallible for valid inputs\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedjcase%2Fmotoko_candid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedjcase%2Fmotoko_candid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedjcase%2Fmotoko_candid/lists"}