Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Contract Metadata Standard #330

Merged
merged 8 commits into from
Mar 29, 2022
103 changes: 103 additions & 0 deletions neps/nep-0330.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
NEP: 330
Title: Contract Metadata
Author: Ben Kurrek <[email protected]>
DiscussionsTo: https://github.com/near/NEPs/discussions/329
Status: Draft
Type: Standards Track
Category: Contract
Created: 27-Feb-2022
---

## Summary

The contract metadata is a standard interface that allows auditing and viewing source code for a deployed smart contract. Implementation of this standard is purely optional but is recommended for developers whose contracts are open source.

## Motivation

There is no trivial way of finding the source code or author of a deployed smart contract. Having a standard that outlines how to view the source code of an arbitrary smart contract creates an environment of openness and collaboration.

The initial discussion can be found [here](https://github.com/near/NEPs/discussions/329).

## Rationale and alternatives

There is a lot of information that can be held about a contract. Ultimately, we wanted to limit it to the least amount fields while still maintaining our goal. This decision was made to not bloat the contracts with unnecessary storage and also to keep the standard simple and understandable.

## Specification

Successful implementations of this standard will introduce a new (`ContractMetadata`) struct that will hold all the necessary information to be queried for. This struct will be kept on the contract level.

The metadata will include two optional fields:
- `version`: a string that references the specific commit hash or version of the code that is currently deployed on-chain. This can be included regardless of whether or not the contract is open-sourced and can also be used for organizational purposes.
- `link`: a string that references the link to the open-source code. This can be anything such as Github or a CID to somewhere on IPFS.

```ts
type ContractMetadata = {
version: string|null, // optional, commit hash being used for the currently deployed wasm. If the contract is not open-sourced, this could also be a numbering system for internal organization / tracking such as "1.0.0" and "2.1.0".
link: string|null, //optional, link to open source code such as a Github repository or a CID to somewhere on IPFS.
}
```

In order to view this information, contracts must include a getter which will return the struct.

```ts
function contract_metadata(): ContractMetadata {}
```

## Reference Implementation

As an example, say there was an NFT contract deployed on-chain which was currently using the commit hash `39f2d2646f2f60e18ab53337501370dc02a5661c` and had its open source code located at `https://github.com/near-examples/nft-tutorial`. This contract would then include a struct which has the following fields:
BenKurrek marked this conversation as resolved.
Show resolved Hide resolved

```ts
type ContractMetadata = {
version: "39f2d2646f2f60e18ab53337501370dc02a5661c"
link: "https://github.com/near-examples/nft-tutorial"
}
```

If someone were to call the view function `contract_metadata`, the contract would return:

```bash
{
version: "39f2d2646f2f60e18ab53337501370dc02a5661c"
link: "https://github.com/near-examples/nft-tutorial"
}
```

An example implementation can be seen below.

```rust
/// Simple Implementation
#[near_bindgen]
pub struct Contract {
pub contract_metadata: ContractMetadata
}

/// Contract metadata structure
pub struct ContractMetadata {
pub version: String,
pub link: String,
}

/// Minimum Viable Interface
pub trait ContractMetadataTrait {
fn contract_metadata(&self) -> ContractMetadata;
}

/// Implementation of the view function
#[near_bindgen]
impl ContractMetadataTrait for Contract {
fn contract_metadata(&self) -> ContractMetadata {
self.contract_metadata.get().unwrap()
}
}
```

## Future possibilities

- By having a standard outlining metadata for an arbitrary contract, any information that pertains on a contract level can be added based on the requests of the developer community.

## Copyright
[copyright]: #copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
93 changes: 93 additions & 0 deletions specs/Standards/ContractMetadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Contract Metadata

## [NEP-330](https://github.com/near/NEPs/discussions/329)

Version `1.0.0`

## Summary

The contract metadata is a standard interface that allows auditing and viewing source code for a deployed smart contract. Implementation of this standard is purely optional but is recommended for developers whose contracts are open source.

## Motivation

There is no trivial way of finding the source code or author of a deployed smart contract. Having a standard that outlines how to view the source code of an arbitrary smart contract creates an environment of openness and collaboration.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the main use cases of having this standard, is if we are able to reproduce the same binary after we have access to the source code. I'm wondering if as part of it we should add a proposal or advise about how we can achieve byte for byte reproducibility.

Having a standard for reproducibility can be useful for external tools to automatically verify the code.

This is hard in general, mostly because there is more than one stack that can be used to generate the contract binary (i.e today we support rust and assembly script). Relevant discussion on how to make this possible on rust: rust-lang/cargo#9506

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A link to a docker image could be sufficient.


The initial discussion can be found [here](https://github.com/near/NEPs/discussions/329).

## Rationale and alternatives

There is a lot of information that can be held about a contract. Ultimately, we wanted to limit it to the least amount fields while still maintaining our goal. This decision was made to not bloat the contracts with unnecessary storage and also to keep the standard simple and understandable.

## Specification

Successful implementations of this standard will introduce a new (`ContractMetadata`) struct that will hold all the necessary information to be queried for. This struct will be kept on the contract level.

The metadata will include two optional fields:
- `version`: a string that references the specific commit hash or version of the code that is currently deployed on-chain. This can be included regardless of whether or not the contract is open-sourced and can also be used for organizational purposes.
- `link`: a string that references the link to the open-source code. This can be anything such as Github or a CID to somewhere on IPFS.

```ts
type ContractMetadata = {
version: string|null, // optional, commit hash being used for the currently deployed wasm. If the contract is not open-sourced, this could also be a numbering system for internal organization / tracking such as "1.0.0" and "2.1.0".
link: string|null, //optional, link to open source code such as a Github repository or a CID to somewhere on IPFS.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BenKurrek @austinabell @MaximusHaximus what are your thoughts of adding a system field so we can get some info on what system helped generate the wasm as well. This way we can try to rebuild the wasm and compare the hashes for auditing purposes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

system: "local"|string,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, you would need architecture and toolchain version at least. Would the purpose of this be human-readable so someone could try to replicate it? You would also need some commit or version since the link doesn't specify repo links to commit that it was built with

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah ideally i want to be able to reference the source code and source dependencies to help with auditing the contract. Here's what polygon does which is pretty cool: https://polygonscan.com/address/0x217cF04C783818E5b15Ae0723b22Ee2415Ab5fe3#code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was assuming that the toolchain version would be kept in the Github repo. I don't really see the need to add a system field but would love other people's opinions

Comment on lines +30 to +32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about adding some sort of organization contact link as well that points to the website of the application, or the developers; potentially with more contact information!

The difference between it and link is that one is to discover the source code, and the other is to discover what is the preferred way to reach developers/community behind this smart contract.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this information could be found by utilizing the link since you can open issues or PRs on Github and follow or view the developers' public profiles. The case where I think this might be useful, is if the link is a CID to somewhere on IPFS since you don't have the same ability to reach out or view the developers as you do using Github.

thoughts? @austinabell @agileurbanite

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would actually go even further and provide three different fields (similar to crates.io): Homepage, Documentation and Repository + version (to pin the exact version and allow reproducing the exact same binary).

Some services like npm only have Homepage and Repository. (Maybe documentation is a bit too much).

}
```

In order to view this information, contracts must include a getter which will return the struct.

```ts
function contract_metadata(): ContractMetadata {}
```

## Reference Implementation

As an example, say there was an NFT contract deployed on-chain which was currently using the commit hash `39f2d2646f2f60e18ab53337501370dc02a5661c` and had its open source code located at `https://github.com/near-examples/nft-tutorial`. This contract would then include a struct which has the following fields:

```ts
type ContractMetadata = {
version: "39f2d2646f2f60e18ab53337501370dc02a5661c"
link: "https://github.com/near-examples/nft-tutorial"
}
```

If someone were to call the view function `contract_metadata`, the contract would return:

```bash
{
version: "39f2d2646f2f60e18ab53337501370dc02a5661c"
link: "https://github.com/near-examples/nft-tutorial"
}
```

An example implementation can be seen below.

```rust
/// Simple Implementation
#[near_bindgen]
pub struct Contract {
pub contract_metadata: ContractMetadata
}

/// Contract metadata structure
pub struct ContractMetadata {
pub version: String,
pub link: String,
}

/// Minimum Viable Interface
pub trait ContractMetadataTrait {
fn contract_metadata(&self) -> ContractMetadata;
}

/// Implementation of the view function
#[near_bindgen]
impl ContractMetadataTrait for Contract {
fn contract_metadata(&self) -> ContractMetadata {
self.contract_metadata.get().unwrap()
}
}
```

## Future possibilities

- By having a standard outlining metadata for an arbitrary contract, any information that pertains on a contract level can be added based on the requests of the developer community.