Developers

When reading this developer document, the assumption is that your project members already have the corresponding development capabilities, including NFT (Non-Fungible Token) contracts and other business contract development, as well as NFT-related metadata management and development capabilities. 

3.1 Windvane API 

Notice: 

The Windvane API is currently in the research stage. We are still improving overall performance and adding more endpoints. 

API Documentation: Coming very soon. 

3.2 Smart Contracts 

The technical implementation of NFT assets can generally be disassembled into two parts: Smart Contracts and Metadata. 

Smart contracts are executable code stored on the blockchain. NFTs rely on them to maintain their asset status and logical execution. The executable code of smart contracts are open, and their source code will be made public (contract code certification) in most cases, thus their security is very important. For the EVM ecosystem, we generally recommend using Solidity and OpenZeppelin to develop smart contracts. 

 For smart contracts, we provide developers with the following references: 

  1. NFT Contract Standards 

  2. Primary Market Interface 

  3. Secondary Market Interface 

 3.2.1 NFT Contract Standards 

Although the Ethereum ecosystem is quite large, and Solidity can develop a large number of flexible smart contract logic, we still recommend following the ERC721 standard or ERC1155 standard when developing smart contracts. 

Following the standard can effectively reduce the risks of your project and achieve rapid interfacing with our system. 

 3.2.1.1 Asset Contract Standards Proposal 

 The implementation of asset contracts is related to the Windvane platform's support for project contracts, and also affects Windvane's asset display to users, transaction status tracking in processing, business statistics and other functions. 

Windvane technically recommends that project parties implement asset contracts with standard protocols to obtain fast Windvane Primary Market/Secondary Market compatibility. If there is non-standard implementation logic that needs to be interfaced, you need to contact the Windvane technical team in advance to test and debug.  

1) ERC721: 

Every ERC-721 compliant contract must implement the ERC721 and ERC165 APIs: 

pragma solidity ^0.4.20; 

 ///  Note: The ERC-165 identifier for this API is 0x80ac58cd. 

API ERC721 /* is ERC165 */ { 

event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); 

event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); 

  event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); 

    function balanceOf(address _owner) external view returns (uint256); 

    function ownerOf(uint256 _tokenId) external view returns (address); 

    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable; 

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable; 

    function transferFrom(address _from, address _to, uint256 _tokenId) external payable; 

    function approve(address _approved, uint256 _tokenId) external payable; 

    function setApprovalForAll(address _operator, bool _approved) external; 

    function getApproved(uint256 _tokenId) external view returns (address); 

    function isApprovedForAll(address _owner, address _operator) external view returns (bool); 

} 

API ERC165 { 

    function supportsInterface(bytes4 interfaceID) external view returns (bool);  } 

2)ERC1155 

Smart contracts implementing the ERC-1155 standard MUST implement all of the functions in the ERC1155 API. 

Smart contracts implementing the ERC-1155 standard MUST implement the ERC-165 supportsInterface function and MUST return the constant value of True if 0xd9b67a26 is passed through the interfaceID argument. 

pragma solidity ^0.5.9; 

/// Note: The ERC-165 identifier for this API is 0xd9b67a26. 

API ERC1155 /* is ERC165 */ { 

    event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value); 

    event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values); 

    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); 

    event URI(string _value, uint256 indexed _id); 

    function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external; 

    function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external; 

    function balanceOf(address _owner, uint256 _id) external view returns (uint256); 

    function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory); 

    function setApprovalForAll(address _operator, bool _approved) external; 

    function isApprovedForAll(address _owner, address _operator) external view returns (bool); 

} 

3) Recommended Reading for Standard Proposals: 

ERC721: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md

ERC1155: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md

3.2.1.2 Contract Data Interfacing Method 

Windvane has two ways to interface with the project's ERC721/1155 NFT contract status data, including asset accounting and metadata, one is a common contract parsing method to scan and extract asset accounting for ERC721/1155 standard contracts on the supported chains, and the other is a one-to-one interfacing via SubGraph. 

The specific way to use an interface depends on the business partnership agreement of the project and the complexity of the technical solution, but no matter which method is used, contract resolution is a common underwriting solution and will collect all the mapping and changes between the tokenIds and owner addresses. Many functions rely on this accounting data, and here we mainly discuss the impact of contract standards on the access of data by contract resolution. If your project does not match the common processing logic of the Windvane platform, or if you have special data collection and logic needs, prepare your SubGraph in advance of the technical interface. 

In simplified terms, contract resolution will automatically determine your contract standard type through the identification of the following methods and include the accounts: 

  1. Retracing all block data from the beginning of a block height to the latest, obtaining all log records in the block, performing initial screening and discarding obvious garbage data. 

  2. Determining the type of contract and roughing up the data by determining the history of event transfer. 

  3. Data segmentation according to standards such as ERC165, see {ERC165-supportsInterface}. 

  4. Get all asset relationships from the contract and include them. 

For contract asset relationships, where NFT casting and transfer occur within your contract, we perform discovery and retrieval via the event code of the standard event. 

  • erc721TransferEventHash = keccak256(“Transfer(address,address,uint256)”); 

  • erc1155SingleTransferHash = keccak256(“TransferSingle(address,address,address,uint256,uint256)”); 

  • erc1155BatchTransferHash = keccak256(“TransferBatch(address,address,address,uint256[],uint256[])”); 

In this sense, event transfer is the key to the inclusion of the user's asset data, which is related to the user's accounting records. 

Another part of the NFT data is Metadata, which determines how the project's NFTs are presented or used. Generally speaking, NFTs are generally only stored as tokenIds on the blockchain (for data cost reasons, expensive on-chain storage solutions are generally not preferred), and the project owner can choose to store the metadata on a decentralized IPFS-like protocol or a self-hosted centralized service or a CDN. For each tokenId, no matter where it is placed, it needs to at least provide us with a method to get the corresponding metadata storage location (Uniform Resource Identifier - URI) 

In the project contract, the tokenURI (uint256) - (ERC721) or uri(uint256) - (ERC1155) method should be implemented to return us a URI address that satisfies RFC3986. 

The above are the necessary interfaces that Windvane relies on to connect to the ERC721/1155 basic data. 

3.2.2 Primary Market Interface 

The Windvane primary market is mainly used by project parties for initial sales, with the most common scenarios being blind box sales and whitelist mint. When the business partnership is finalized, the project owner needs to prepare the technical alignment of the standards, mainly the contract and metadata. In order to better support the various ways of playing the primary market and to get a better user experience, the primary market listing takes place mainly on Windvane's centralized service, without any additional market contracts for the project parties to interface. 

Due to adhering to the ERC721/1155 standard, the platform supports the automatic synchronization of asset data and metadata, so the interfacing point involved in the primary market mainly lies in the business logic of the primary issuance. This differs from one project to another, with rather flexible implementations, and our technical team supports following the business online communication interfaces. However, in terms of the current business paradigm, the following aspects are involved: 

  1. The contract interaction logic (specifically the mint logic), which involves the methods called by mint, the parameters of the mint interaction, whether there is whitelist verification, whether there is protocol signature verification, and other logic. 

  2. Blind box update logic, here involves how to agree to trigger Windvane to re-fetch the URI from the contract and actively refresh the metadata cache, and whether there is replacement class blind box logic, and whether the update needs to be actively triggered by openapi. 

For some project parties, they wish to collect distribution statistics for Windvane channels, so they will interact with the agreed mint method or protocol signature verification during mint. Protocol signature verification means that Windvane will sign the user's mint protocol content in the centralized service using the web3 account, inform the project party of the signature account address in advance and save it in the contract, and the project party will implement the Recover ECDSA signer of the EIP-1271 in the smart contract and verify it by comparing the signer address. 

Another common scenario is whitelisting sales, which can be implemented in a variety of ways. Windvane supports the following authentication methods. 

  1. User address validation in the form of a hard code or status entry in the project's contract (whitelist set by the project). 

  2. User address is verified by the Merkel tree and the project’s contract only requires the entry of Merkel Root (small amount of data, whitelist set by Windvane). 

  3. Third-party whitelist contracts, internal validation of project-side contracts, no interaction with Windvane. 

For the update logic of the blind box, which is usually implemented by updating the metadata, Windvane supports updating the metadata at different data processing stages to be compatible with the project's open box logic. 

  1. If the NFT contract updates the blind box data by changing the baseURI or the tokenURI corresponding to a tokenId, we can proactively update the data by triggering the contract event at the agreed time described in the update timing convention. 

  2. If the NFT contract does not deal with open-box logic, but updates the blind-box data by changing the original metadata description, we can proactively update the data by agreeing on a time to do so. 

  3. We support openapi for invoked passive refresh of metadata for single or batch or full collection updates if the project has business collaboration requirements. 

 3.2.3 Secondary Market Interface 

The Windvane secondary market will be launched very soon, and the docking information will not be provided for the time being. 

3.2.4 Windvane Secondary Market Contract Example 

The Windvane secondary market will be launched very soon, and the docking information will not be provided for the time being. 

3.3 Metadata Standards 

We know that NFTs can now be based on ERC721 and ERC1155, with a tokenId at the core to differentiate it on the chain, but how can richer metadata be added to ERC721/ERC1155? Or how do we implement association support? 

The solution is to associate a description file called metadata to the tokenId. Providing metadata will allow our trading marketplace, and other applications that parse NFTs, to support richer functionality, such as fields like name, image, description, attributes, etc., in addition to the unique id (tokenId) that already exists on the chain. 

3.3.1 Metadata URI 

How do we obtain the address of the metadata? 

The tokenURI (ERC721) and uri (ERC1155) methods in the project's NFT contract are parsed to determine where the metadata was obtained. If the project returns the relevant metadata in json format from this address, Windvane will parse it and display it automatically according to the standards protocol. 

 /** 

 * @dev Returns a URI for a given token ID 

 */ 

function tokenURI(uint256 _tokenId) public view returns (string) { 

  return Strings.strConcat( 

      baseTokenURI(), 

      Strings.uint2str(_tokenId) 

  ); 

} 

 3.3 Metadata Content Standards 

 1) Standards 

We support the standard ERC721 format definition (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md) and the ERC1155 - Enjin proposal (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md#erc-1155-metadata-uri-json-schema). 

 [换行符]EIP721 Metadata JSON Schema: 

{ 

    "title": "Asset Metadata", 

    "type": "object", 

    "properties": { 

        "name": { 

            "type": "string", 

            "description": "Identifies the asset to which this NFT represents" 

        }, 

        "description": { 

            "type": "string", 

            "description": "Describes the asset that this NFT represents" 

        }, 

        "image": { 

            "type": "string", 

            "description": "A URI pointing to a resource with mime type image/* representing the asset that this NFT represents. Consider making any images at a width between 320 and 1080 pixels and an aspect ratio between 1.91:1 and 4:5 (inclusive)." 

        } 

    } 

} 

Example: 

{ 

  "description": "windvane demo des.", 

  "external_url": "https://your-project.xxx/demo", 

  "image": "https://your-storage/demo.png", 

  "name": "Windvane Demo", 

  "attributes": [ ... ], 

} 

In addition to the standard fields, we also provide compatibility with NFT attribute field formats created in active markets such as Opensea/LooksRare. With these extended metadata, Windvane maintains compatibility with stock NFT projects. We currently support Image\Video\Audio\3D Model as content presentation, and we will provide compatibility with moreOpensea extended data formats in the future. 

 Field Brief Descriptions: 

image

This is the URL to the image of the item. Can be just about any type of image, along with IPFS URLs or paths.

image_data

Raw image data if you want to generate images on the fly (not recommended). Only use this if you're not including the image parameter.

description

A human readable description of the item. Markdown is supported.

name

The name of the item.

attributes

These are the attributes for the item that will show up on the details page.

animation_url

A URL to a multi-media attachment for the item. The file extensions GLTF, GLB, WEBM, MP4, M4V, OGV, and OGG are supported, along with the audio-only extensions MP3, WAV, and OGA.

Attributes contain multiple key-value objects. For example: 

 { 

"attributes": [ 

    { 

      "trait_type": "Base", 

      "value": "Token 123" 

    }, 

    { 

      "trait_type": "Base", 

      "value": 5 

    }, 

  ] 

} 

Date Traits: 

Windvane also supports a date display_type. Pass in a unix timestamp (seconds) as the value. 

{ 

      "display_type": "date", 

      "trait_type": "birthday", 

      "value": 1546360800 

} 

NOTICE: Other types of Trait Types from Opensea will be handled in a similar way to how Basic presents keys. 

Enjin Metadata: 

Windvane also supports the Enjin Metadata style. For example: 

{ 

    "properties": { 

        "base": "Token 100", 

        "rich_property": { 

            "name": token 

            "value": "T101", 

            "display_value": "Tt101", 

        } 

                ... 

    } 

} 

EIP1155 Metadata JSON Schema: 

{ 

    "title": "Token Metadata", 

    "type": "object", 

    "properties": { 

        "name": { 

            "type": "string", 

            "description": "Identifies the asset that this token represents" 

        }, 

        "decimals": { 

            "type": "integer", 

            "description": "The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation." 

        }, 

        "description": { 

            "type": "string", 

            "description": "Describes the asset that this token represents" 

        }, 

        "image": { 

            "type": "string", 

            "description": "A URI pointing to a resource with mime type image/* representing the asset that this token represents. Consider making any images at a width between 320 and 1080 pixels and an aspect ratio between 1.91:1 and 4:5 (inclusive)." 

        }, 

        "properties": { 

            "type": "object", 

            "description": "Arbitrary properties. Values may be strings, numbers, objects, or arrays." 

        } 

    } 

} 

Example: 

{ 

    "name": "Windvane Demo Name", 

    "description": "windvane demo des...", 

    "image": "https:\/\/s3.amazonaws.com\/your-bucket\/images\/{id}.png", 

    "properties": { 

        "simple_property": "example value", 

        "rich_property": { 

            "name": "Name", 

            "value": "123", 

        }, 

        "array_property": { 

            "name": "Name", 

            "value": [1,2,3,4], 

            "class": "emphasis" 

        } 

    } 

} 

If compatibility with multilingual metadata is required, you can use {locale} to redirect the localization description file. 

JSON Schema: 

{ 

    "title": "Token Metadata", 

    "type": "object", 

    "properties": { 

        "name": { 

            "type": "string", 

            "description": "Identifies the asset that this token represents", 

        }, 

        "decimals": { 

            "type": "integer", 

            "description": "The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation." 

        }, 

        "description": { 

            "type": "string", 

            "description": "Describes the asset that this token represents" 

        }, 

        "image": { 

            "type": "string", 

            "description": "A URI pointing to a resource with mime type image/* representing the asset that this token represents. Consider making any images at a width between 320 and 1080 pixels and an aspect ratio between 1.91:1 and 4:5 (inclusive)." 

        }, 

        "properties": { 

            "type": "object", 

            "description": "Arbitrary properties. Values may be strings, numbers, objects, or arrays.", 

        }, 

        "localization": { 

            "type": "object", 

            "required": ["uri", "default", "locales"], 

            "properties": { 

                "uri": { 

                    "type": "string", 

                    "description": "The URI pattern to fetch localized data from. This URI should contain the substring `{locale}` which will be replaced with the appropriate locale value before sending the request." 

                }, 

                "DEFAULT", 

                    "type": "string", 

                    "description": "The locale of the default data within the base JSON" 

                }, 

                "locales": { 

                    "type": "array", 

                    "description": "The list of locales for which data is available. These locales should conform to those defined in the Unicode Common Locale Data Repository (http://cldr.unicode.org/)." 

                } 

            } 

        } 

    } 

} 

Example: 

Base URI: 

{ 

  "name": "Windvane Space", 

  "description": "Each token represents a unique Ad space in the city.", 

  "localization": { 

    "uri": "ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT/{locale}.json", 

    "default": "en", 

    "locales": ["en", "es", "fr"] 

  } 

} 

es.json 

{ 

  "name": "ES - Espacio Publicitario", 

  "description": "Cada token representa un espacio publicitario único en la ciudad." 

} 

fr.json 

{ 

  "name": "FR - Espace Publicitaire", 

  "description": "Chaque jeton représente un espace publicitaire unique dans la ville." 

} 

2) Things to note about attribute values: 

  • If it is a string value, it needs to be enclosed in double quotation marks. 

  • In case of numerical value, it needs to be expressed as an integers or a floating point number. 

  • All strings need to be composed of human-readable characters. 

3) Metadata's network location: 

In theory, there are three locations for the metadata: on-chain storage, decentralized storage and centralized storage. Here we give their matchmaking elements which project parties can choose from according to their needs. 

Network Location

Data Format

Editable?

Cost

Protocol & Service

Rec. Physical Location

Whitelist Restriction

Recommended?

On-chain storage

base64 encoding, saved directly to the tokenURI

No

High

Block

N/A

No

No

Decentralized storage

Decentralized protocol management file

No

Relatively low

IPFS / Arweave

N/A

Yes

Yes

Centralized storage

Centralized protocol management file

Yes

Low

https, recommend using cloudfare & cloudfront CDN

Japan & North America

Yes

Yes

4) NFT metadata updating times: 

At some point, we will synchronize metadata data to our storage to provide a better access experience for the platform's users, and the metadata update will occur at the following update times: 

  • A data pull update is performed upon mint success 

  • A data pull update is performed upon blind box opening 

Update time triggering agreement: 

Updated Time

Trigger Method

Message

Recommended?

mint

Transfer record

0x transfer event

Not recommended, but the system is compatible.

mint

Contract event

Contract code agreement event name 

event MintTo(uint tokenId, address to, string tokenURI)

Yes

blindBoxOpen

Configuration

Agreed time

Not recommended, but the system is compatible.

blindBoxOpen

Contract event

Contract code agreement event name 

event BlindBoxOpen(uint tokenId) tokenId = 0 means all items are open, > 0 means a single tokenId corresponding to the item is open

Yes

  5) NFT agreement-level metadata: 

NOTICE: Parsing Contract level metadata (i.e. contractURI method execution) is currently not supported. 

However, the secondary market phase will be supported in the future, and project parties can pre-position themselves according to this standard. The standard is compatible with Opensea. 

Contract Example: 

contract MyCollectible is ERC721 { 

    function contractURI() public view returns (string memory) { 

        return "https://metadata-url.com/my-metadata"; 

    } 

} 

Contract metadata format: 

{ 

  "name": "Windvane Creatures", 

  "description": "Windvane collection description", 

  "image": "https://your-cdn.io/image.png", 

  "external_link": "https://your-website.io", 

  "seller_fee_basis_points": 100, # Indicates a 1% seller fee. 

  "fee_recipient": "0xABCD337c39cccE66adfeCB2BF99C1DdC54C2D721" # Where seller fees will be paid to. 

} 

These are the technical conventions related to metadata content. 

3.4 Required information for technical interfacing 

If there are multiple links into the project’s metadata object constraints, you need to provide multiple copies (currently does not support the provision of cross-linked interfaces, so please ignore the multi-link section). 

Key

Value Description

blockchain

Project chain

contractName

Project name

contractAddress

Project’s contract address. If a contract has not been deployed yet, the contract source code prevails.

contractCode

Project contract source code.

contractABI

Project contract ABI description.

mintMethod

The mint method to which this channel belongs.

mintPrice

The primary market mint price of this channel.

originURI

item metadata source code

signType

Use protocol signature method to interact?

signMsgFormat

Protocol signature source message format.

isBlindBoxSell

Blind box sale?

openBlindBoxTime

Blind box opening time (open at the point of mint or delayed opening at a specific time)

flowAttachment

Contract business flowchart.

secAttachment

Contract security audit report.

3.5 Windvane Code and Issues:  

Github:https://github.com/windvane-io

Last updated