Create contract manifest during compilation #396

Open
opened 2025-12-28 17:13:08 +00:00 by sami · 8 comments
Owner

Originally created by @fyrchik on GitHub (Jun 9, 2020).

While some of the contract features cannot be determined from the contract source (e.g. IsPayable) most of them can. Method and Event descriptions should be generated automatically.
I think it is also worth supporting Permissions/Groups (via magic comments?), so that manifest can be completely generated from source.

Originally created by @fyrchik on GitHub (Jun 9, 2020). While some of the contract features cannot be determined from the contract source (e.g. `IsPayable`) most of them can. Method and Event descriptions should be generated automatically. I think it is also worth supporting Permissions/Groups (via magic comments?), so that manifest can be completely generated from source.
Author
Owner

@AnnaShaleva commented on GitHub (Jun 26, 2020):

Perhaps, we can define Permissions/Groups right into configuration .yaml file, like it was done before for contract author, email and etc. Usage of this file is required for .manifest.json generation, so why not to add some more configurable fields to it?

@roman-khimov ?

@AnnaShaleva commented on GitHub (Jun 26, 2020): Perhaps, we can define Permissions/Groups right into configuration `.yaml` file, like it was done before for contract author, email and etc. Usage of this file is required for `.manifest.json` generation, so why not to add some more configurable fields to it? @roman-khimov ?
Author
Owner

@gsmachado commented on GitHub (Jan 11, 2021):

I support this. 👍 💯

@roman-khimov as we discussed in the other issue, I would keep generating the YAMLs. However, I would check if the YAML can be automagically generated somehow (as you suggested).

A short-term approach, in my opinion, would be:

  • when the user compiles the contract.go using the neo-go contract compile, it reads comment's tags and not only generates the NEF, but also the YAML file
  • if the user wants to compile the contract without generating the YAML file again (for whatever reason), this behavior can be specified as a param to the neo-go contract compile

The only disadvantage with this approach is that the neo-go would need to parse and validate comment, which can be ugly. But I believe you guys can re-use the tools used in swag.

The best (long-term) approach would be to automagically generate the YAML from analyzing the script. But this is a second step after generating from comments (as I mentioned above).

@gsmachado commented on GitHub (Jan 11, 2021): I support this. 👍 💯 @roman-khimov as we discussed in the other issue, I would keep generating the YAMLs. However, I would check if the YAML can be automagically generated somehow (as you suggested). A short-term approach, in my opinion, would be: - when the user compiles the `contract.go` using the `neo-go contract compile`, it reads comment's tags and not only generates the NEF, but also the YAML file - if the user wants to compile the contract without generating the YAML file again (for whatever reason), this behavior can be specified as a param to the `neo-go contract compile` The only disadvantage with this approach is that the `neo-go` would need to parse and validate comment, which can be ugly. But I believe you guys can re-use the tools used in [swag](https://github.com/swaggo/swag). The best (long-term) approach would be to automagically generate the YAML from analyzing the script. But this is a second step after generating from comments (as I mentioned above).
Author
Owner

@roman-khimov commented on GitHub (Jan 12, 2021):

If we're to get all metadata from the source code we probably no longer need YAMLs then, compiler could just emit NEF and manifest. But

neo-go would need to parse and validate comment, which can be ugly

this still is a problem.

The best (long-term) approach would be to automagically generate the YAML from analyzing the script.

If we're to get back to the list of things specified there:

  • name
    Can't be derived from the code, it's either needs to be specified in the comment or some magic variable (like var _contract_name = "Name" with _contract_name being a reserved identifier)
  • list of supported standards
    Also can't be derived directly.
  • list of safe methods
    #1596
  • events
    Unfortunately, even this is problematic. We can analyze runtime.Notify() argument types, but is the type we have there is the intended one? Do we want to emit "Tranfser" if that's what is written in the source code? How do we map interface{} into event type? What if event name is a variable? So some declaration is still needed even there.
@roman-khimov commented on GitHub (Jan 12, 2021): If we're to get all metadata from the source code we probably no longer need YAMLs then, compiler could just emit NEF and manifest. But > neo-go would need to parse and validate comment, which can be ugly this still is a problem. > The best (long-term) approach would be to automagically generate the YAML from analyzing the script. If we're to get back to the list of things specified there: * name Can't be derived from the code, it's either needs to be specified in the comment or some magic variable (like `var _contract_name = "Name"` with `_contract_name` being a reserved identifier) * list of supported standards Also can't be derived directly. * list of safe methods #1596 * events Unfortunately, even this is problematic. We can analyze `runtime.Notify()` argument types, but is the type we have there is the intended one? Do we want to emit "Tranfser" if that's what is written in the source code? How do we map `interface{}` into event type? What if event name is a variable? So some declaration is still needed even there.
Author
Owner

@lock9 commented on GitHub (Nov 17, 2023):

Hello.

What if there is a method that returns the configuration object?
It doesn't need annotations. The compiler could identify the method by the return type. For example, if it returns the NeoMetadataStruct, you know that this method will generate the configuration.

  • There won't be conflicts with existing applications because this type doesn't exist yet.

Something like this (I'm not a go dev):

type NeoMetadata struct {
    Name             string
    Source           *string 
    SupportedStandards []string
    Trusts           []string
    Permissions      []map[string]interface{} 
    Groups           []map[string]interface{}
    Author           *string 
    Email            *string
    Description      *string
}

func ContractMetadata() *NeoMetadata {
    return &NeoMetadata{
        Name:             "",
        SupportedStandards: []string{},
        Trusts:           []string{},
        Permissions:      []map[string]interface{}{},
        Groups:           []map[string]interface{}{},
    }
}

I'm not sure if this is what I was proposing in the other issue. It's not that I want it to be automatic, I just want it to use a single file instead

@lock9 commented on GitHub (Nov 17, 2023): Hello. What if there is a method that returns the configuration object? It doesn't need annotations. The compiler could identify the method by the return type. For example, if it returns the NeoMetadataStruct, you know that this method will generate the configuration. - There won't be conflicts with existing applications because this type doesn't exist yet. Something like this (I'm not a go dev): ``` type NeoMetadata struct { Name string Source *string SupportedStandards []string Trusts []string Permissions []map[string]interface{} Groups []map[string]interface{} Author *string Email *string Description *string } func ContractMetadata() *NeoMetadata { return &NeoMetadata{ Name: "", SupportedStandards: []string{}, Trusts: []string{}, Permissions: []map[string]interface{}{}, Groups: []map[string]interface{}{}, } } ``` I'm not sure if this is what I was proposing in the other issue. It's not that I want it to be automatic, I just want it to use a single file instead
Author
Owner

@roman-khimov commented on GitHub (Nov 18, 2023):

Magic variables were considered previously, they can help to some extent, but I'm not sure it'd be a big improvement if they just have the same data, it could be a magic

const __manifest = `
name: "NeoFS Object NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"]
events:
  - name: Transfer
    parameters:
      - name: from
        type: Hash160
      - name: to
        type: Hash160
      - name: amount
        type: Integer
      - name: tokenId
        type: ByteArray
permissions:
  - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
    methods: ["update", "destroy"]
  - methods: ["onNEP11Payment"]
overloads:
  balanceOfDivisible: balanceOf
  transferDivisible: transfer
`

then. Is it better than a separate YAML?

At the same time we have some mechanism for event guessing now (implemented as a part of #3008), the hardest part other than that is likely method safeness (permissions can technically be guessed in a way similar to events). But we've also got overloads (https://github.com/nspcc-dev/neo-go/blob/master/docs/compiler.md#Overloads).

@roman-khimov commented on GitHub (Nov 18, 2023): Magic variables were considered previously, they can help to some extent, but I'm not sure it'd be a big improvement if they just have the same data, it could be a magic ```go const __manifest = ` name: "NeoFS Object NFT" sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: ["NEP-11"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"] events: - name: Transfer parameters: - name: from type: Hash160 - name: to type: Hash160 - name: amount type: Integer - name: tokenId type: ByteArray permissions: - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd methods: ["update", "destroy"] - methods: ["onNEP11Payment"] overloads: balanceOfDivisible: balanceOf transferDivisible: transfer ` ``` then. Is it better than a separate YAML? At the same time we have some mechanism for event guessing now (implemented as a part of #3008), the hardest part other than that is likely method safeness (permissions can technically be guessed in a way similar to events). But we've also got overloads (https://github.com/nspcc-dev/neo-go/blob/master/docs/compiler.md#Overloads).
Author
Owner

@lock9 commented on GitHub (Nov 21, 2023):

Magic variables were considered previously, they can help to some extent, but I'm not sure it'd be a big improvement if they just have the same data, it could be a magic

const __manifest = `
name: "NeoFS Object NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"]
events:
  - name: Transfer
    parameters:
      - name: from
        type: Hash160
      - name: to
        type: Hash160
      - name: amount
        type: Integer
      - name: tokenId
        type: ByteArray
permissions:
  - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
    methods: ["update", "destroy"]
  - methods: ["onNEP11Payment"]
overloads:
  balanceOfDivisible: balanceOf
  transferDivisible: transfer
`

then. Is it better than a separate YAML?

At the same time we have some mechanism for event guessing now (implemented as a part of #3008), the hardest part other than that is likely method safeness (permissions can technically be guessed in a way similar to events). But we've also got overloads (https://github.com/nspcc-dev/neo-go/blob/master/docs/compiler.md#Overloads).

For some use cases, it's better than a separate file. However, I can see that it may become an issue once it gets bigger.
It would be better if it were typed 😅

Edit: This could be replaced with a type, like a "NEP11TransferEvent"

  • name: Transfer
    parameters:
    • name: from
      type: Hash160
    • name: to
      type: Hash160
    • name: amount
      type: Integer
    • name: tokenId
      type: ByteArray

Edit 2:, if the user declares it NEP-11 or NEP-xx compliant, the safe methods and events can be 'guessed' (enforced?)

It could be like this:

const __manifest = `
name: "NeoFS Object NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"] // This line tells the compiler to find the methods and events
permissions:
  - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
    methods: ["update", "destroy"]
  - methods: ["onNEP11Payment"] // Maybe this one is not needed
overloads:
  balanceOfDivisible: balanceOf
  transferDivisible: transfer
`
@lock9 commented on GitHub (Nov 21, 2023): > Magic variables were considered previously, they can help to some extent, but I'm not sure it'd be a big improvement if they just have the same data, it could be a magic > > ```go > const __manifest = ` > name: "NeoFS Object NFT" > sourceurl: https://github.com/nspcc-dev/neo-go/ > supportedstandards: ["NEP-11"] > safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"] > events: > - name: Transfer > parameters: > - name: from > type: Hash160 > - name: to > type: Hash160 > - name: amount > type: Integer > - name: tokenId > type: ByteArray > permissions: > - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd > methods: ["update", "destroy"] > - methods: ["onNEP11Payment"] > overloads: > balanceOfDivisible: balanceOf > transferDivisible: transfer > ` > ``` > > then. Is it better than a separate YAML? > > At the same time we have some mechanism for event guessing now (implemented as a part of #3008), the hardest part other than that is likely method safeness (permissions can technically be guessed in a way similar to events). But we've also got overloads (https://github.com/nspcc-dev/neo-go/blob/master/docs/compiler.md#Overloads). For some use cases, it's better than a separate file. However, I can see that it may become an issue once it gets bigger. It would be better if it were typed 😅 Edit: This could be replaced with a type, like a "NEP11TransferEvent" > - name: Transfer > parameters: > - name: from > type: Hash160 > - name: to > type: Hash160 > - name: amount > type: Integer > - name: tokenId > type: ByteArray Edit 2:, if the user declares it NEP-11 or NEP-xx compliant, the safe methods and events can be 'guessed' (enforced?) It could be like this: > ```go > const __manifest = ` > name: "NeoFS Object NFT" > sourceurl: https://github.com/nspcc-dev/neo-go/ > supportedstandards: ["NEP-11"] // This line tells the compiler to find the methods and events > permissions: > - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd > methods: ["update", "destroy"] > - methods: ["onNEP11Payment"] // Maybe this one is not needed > overloads: > balanceOfDivisible: balanceOf > transferDivisible: transfer > ` > ```
Author
Owner

@amlwwalker commented on GitHub (Feb 5, 2024):

this looks cool. Any progress on this @roman-khimov ? be nice to not have to have a seperate yaml file!

@amlwwalker commented on GitHub (Feb 5, 2024): this looks cool. Any progress on this @roman-khimov ? be nice to not have to have a seperate yaml file!
Author
Owner

@roman-khimov commented on GitHub (Feb 5, 2024):

Not on the roadmap at the moment (https://github.com/nspcc-dev/neo-go/milestone/76). Patches are welcome. Can be done step by step, btw, starting with simple attributes (name/url/standards).

@roman-khimov commented on GitHub (Feb 5, 2024): Not on the roadmap at the moment (https://github.com/nspcc-dev/neo-go/milestone/76). Patches are welcome. Can be done step by step, btw, starting with simple attributes (name/url/standards).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nspcc-dev/neo-go#396
No description provided.