Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

feat(ContractSpec): add JsonSchema method for corresponding JSON schema #171

Closed
wants to merge 5 commits into from

Conversation

willemneal
Copy link
Contributor

@willemneal willemneal commented Nov 13, 2023

This will allow tooling like react forms to use the JSON schema to generate UI that is valid for the ContractSpec's encoding of args.

This is also useful validating inputs before encoding JS to XDR.

For reference here is the kitchen sink example from our tests:
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "U64": {
      "type": "string",
      "pattern": "^[0-9]+$",
      "minLength": 1,
      "maxLength": 20
    },
    "I64": {
      "type": "string",
      "pattern": "^-?^[0-9]+$",
      "minLength": 1,
      "maxLength": 21
    },
    "U32": {
      "type": "integer",
      "minimum": 0,
      "maximum": 4294967295
    },
    "I32": {
      "type": "integer",
      "minimum": -2147483648,
      "maximum": 2147483647
    },
    "U128": {
      "type": "string",
      "pattern": "^[0-9]+$",
      "minLength": 1,
      "maxLength": 39
    },
    "I128": {
      "type": "string",
      "pattern": "^-?[0-9]+$",
      "minLength": 1,
      "maxLength": 40
    },
    "U256": {
      "type": "string",
      "pattern": "^[0-9]+$",
      "minLength": 1,
      "maxLength": 78
    },
    "I256": {
      "type": "string",
      "pattern": "^-?[0-9]+$",
      "minLength": 1,
      "maxLength": 79
    },
    "Address": {
      "type": "string",
      "description": "Address can be a public key or contract id"
    },
    "ScString": {
      "type": "string",
      "description": "ScString is a string"
    },
    "ScSymbol": {
      "type": "string",
      "description": "ScString is a string"
    },
    "DataUrl": {
      "type": "string",
      "format": "data-url"
    },
    "Test": {
      "description": "This is from the rust doc above the struct Test",
      "properties": {
        "a": {
          "$ref": "#/definitions/U32"
        },
        "b": {
          "type": "boolean"
        },
        "c": {
          "$ref": "#/definitions/ScSymbol"
        },
        "additionalProperties": false
      },
      "required": [
        "a",
        "b",
        "c"
      ],
      "type": "object"
    },
    "SimpleEnum": {
      "oneOf": [
        {
          "tag": "First"
        },
        {
          "tag": "Second"
        },
        {
          "tag": "Third"
        }
      ]
    },
    "RoyalCard": {
      "oneOf": [
        {
          "description": "",
          "title": "Jack",
          "enum": [
            11
          ],
          "type": "number"
        },
        {
          "description": "",
          "title": "Queen",
          "enum": [
            12
          ],
          "type": "number"
        },
        {
          "description": "",
          "title": "King",
          "enum": [
            13
          ],
          "type": "number"
        }
      ]
    },
    "TupleStruct": {
      "description": "",
      "properties": {
        "0": {
          "$ref": "#/definitions/Test"
        },
        "1": {
          "$ref": "#/definitions/SimpleEnum"
        },
        "additionalProperties": false
      },
      "required": [
        "0",
        "1"
      ],
      "type": "object"
    },
    "ComplexEnum": {
      "oneOf": [
        {
          "tag": "Struct",
          "values": [
            {
              "$ref": "#/definitions/Test"
            }
          ]
        },
        {
          "tag": "Tuple",
          "values": [
            {
              "$ref": "#/definitions/TupleStruct"
            }
          ]
        },
        {
          "tag": "Enum",
          "values": [
            {
              "$ref": "#/definitions/SimpleEnum"
            }
          ]
        },
        {
          "tag": "Asset",
          "values": [
            {
              "$ref": "#/definitions/Address"
            },
            {
              "$ref": "#/definitions/I128"
            }
          ]
        },
        {
          "tag": "Void"
        }
      ]
    },
    "hello": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "hello": {
              "$ref": "#/definitions/ScSymbol"
            }
          },
          "type": "object",
          "required": [
            "hello"
          ]
        }
      }
    },
    "woid": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {},
          "type": "object"
        }
      }
    },
    "val": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {},
          "type": "object"
        }
      }
    },
    "u32_fail_on_even": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "u32_": {
              "$ref": "#/definitions/U32"
            }
          },
          "type": "object",
          "required": [
            "u32_"
          ]
        }
      }
    },
    "u32_": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "u32_": {
              "$ref": "#/definitions/U32"
            }
          },
          "type": "object",
          "required": [
            "u32_"
          ]
        }
      }
    },
    "i32_": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "i32_": {
              "$ref": "#/definitions/I32"
            }
          },
          "type": "object",
          "required": [
            "i32_"
          ]
        }
      }
    },
    "i64_": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "i64_": {
              "$ref": "#/definitions/I64"
            }
          },
          "type": "object",
          "required": [
            "i64_"
          ]
        }
      }
    },
    "strukt_hel": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "strukt": {
              "$ref": "#/definitions/Test"
            }
          },
          "type": "object",
          "required": [
            "strukt"
          ]
        }
      },
      "description": "Example contract method which takes a struct"
    },
    "strukt": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "strukt": {
              "$ref": "#/definitions/Test"
            }
          },
          "type": "object",
          "required": [
            "strukt"
          ]
        }
      }
    },
    "simple": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "simple": {
              "$ref": "#/definitions/SimpleEnum"
            }
          },
          "type": "object",
          "required": [
            "simple"
          ]
        }
      }
    },
    "complex": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "complex": {
              "$ref": "#/definitions/ComplexEnum"
            }
          },
          "type": "object",
          "required": [
            "complex"
          ]
        }
      }
    },
    "addresse": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "addresse": {
              "$ref": "#/definitions/Address"
            }
          },
          "type": "object",
          "required": [
            "addresse"
          ]
        }
      }
    },
    "bytes": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "bytes": {
              "$ref": "#/definitions/DataUrl"
            }
          },
          "type": "object",
          "required": [
            "bytes"
          ]
        }
      }
    },
    "bytes_n": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "bytes_n": {
              "$ref": "#/definitions/DataUrl",
              "maxLength": 9
            }
          },
          "type": "object",
          "required": [
            "bytes_n"
          ]
        }
      }
    },
    "card": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "card": {
              "$ref": "#/definitions/RoyalCard"
            }
          },
          "type": "object",
          "required": [
            "card"
          ]
        }
      }
    },
    "boolean": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "boolean": {
              "type": "boolean"
            }
          },
          "type": "object",
          "required": [
            "boolean"
          ]
        }
      }
    },
    "not": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "boolean": {
              "type": "boolean"
            }
          },
          "type": "object",
          "required": [
            "boolean"
          ]
        }
      },
      "description": "Negates a boolean value"
    },
    "i128": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "i128": {
              "$ref": "#/definitions/I128"
            }
          },
          "type": "object",
          "required": [
            "i128"
          ]
        }
      }
    },
    "u128": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "u128": {
              "$ref": "#/definitions/U128"
            }
          },
          "type": "object",
          "required": [
            "u128"
          ]
        }
      }
    },
    "multi_args": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "a": {
              "$ref": "#/definitions/U32"
            },
            "b": {
              "type": "boolean"
            }
          },
          "type": "object",
          "required": [
            "a",
            "b"
          ]
        }
      }
    },
    "map": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "map": {
              "type": "object",
              "patternProperties": {
                "^[a-zA-Z0-9]+$": {
                  "type": "boolean"
                }
              },
              "additionalProperties": false
            }
          },
          "type": "object",
          "required": [
            "map"
          ]
        }
      }
    },
    "vec": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "vec": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/U32"
              }
            }
          },
          "type": "object",
          "required": [
            "vec"
          ]
        }
      }
    },
    "tuple": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "tuple": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/ScSymbol"
                },
                {
                  "$ref": "#/definitions/U32"
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "type": "object",
          "required": [
            "tuple"
          ]
        }
      }
    },
    "option": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "option": {
              "$ref": "#/definitions/U32"
            }
          },
          "type": "object"
        }
      },
      "description": "Example of an optional argument"
    },
    "u256": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "u256": {
              "$ref": "#/definitions/U256"
            }
          },
          "type": "object",
          "required": [
            "u256"
          ]
        }
      }
    },
    "i256": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "i256": {
              "$ref": "#/definitions/I256"
            }
          },
          "type": "object",
          "required": [
            "i256"
          ]
        }
      }
    },
    "string": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "string": {
              "$ref": "#/definitions/ScString"
            }
          },
          "type": "object",
          "required": [
            "string"
          ]
        }
      }
    },
    "tuple_strukt": {
      "additionalProperties": false,
      "contractMethod": "view",
      "properties": {
        "args": {
          "additionalProperties": false,
          "properties": {
            "tuple_strukt": {
              "$ref": "#/definitions/TupleStruct"
            }
          },
          "type": "object",
          "required": [
            "tuple_strukt"
          ]
        }
      }
    }
  }
}

Co-authored-by: George <Shaptic@users.noreply.github.com>
@Shaptic
Copy link
Contributor

Shaptic commented Nov 30, 2023

@willemneal is this RFR? I haven't given it a proper look because it's in Draft. Also, can you target the stable branch? We should release this alongside the other, final, stable Protocol 20 changes.

@willemneal
Copy link
Contributor Author

I want it to be, but should I move it to js-stellar-sdk? If so which branch should I target there? Is it compatible with testnet?

@willemneal willemneal changed the base branch from main to stable December 1, 2023 18:11
@Shaptic
Copy link
Contributor

Shaptic commented Dec 5, 2023

@willemneal sorry for the delay. Yeah SDK might be better just so that we don't need to port it over later. There's a stable branch to target, see here: stellar/js-stellar-sdk#886

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants