|
| 1 | +--- |
| 2 | +title: 4.6. Upgradeable NFT |
| 3 | +sidebar_position: 6 |
| 4 | +slug: /standard/TIP-4.6 |
| 5 | +--- |
| 6 | + |
| 7 | +# Upgradeable NFT (TIP-4.6) |
| 8 | +Requires: [TIP-4.1](1.md) |
| 9 | +Requires: [TIP-6.1](./../TIP-6/1.md) |
| 10 | + |
| 11 | +## Abstract |
| 12 | + |
| 13 | +This standard describes the operation of upgradeable NFT contracts. This is based on [TIP-4.1](1.md) and does not describe the functionality proposed there. |
| 14 | + |
| 15 | +The only difference in the minting process is that the Collection deploys an NftPlatform contract rather than an NFT. |
| 16 | + |
| 17 | +Immediately after deployment to the network, the NftPlatform contract calls the `tvm.setcode()`, `tvm.setCurrentCode()`, and `onCodeUpgrade` functions, changing its code to NFT code. |
| 18 | + |
| 19 | +## Motivation |
| 20 | + |
| 21 | +The standard allows the NFT code to be changed in case an error is found in it or there is a need to add new functionality. |
| 22 | + |
| 23 | +## Specification |
| 24 | + |
| 25 | +The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). |
| 26 | + |
| 27 | + |
| 28 | +## Collection |
| 29 | + |
| 30 | +The NFT collection contract serves as a repository for the most up-to-date version of the NFT code, including its current version number. When an individual NFT contract seeks to upgrade its own codebase, it can initiate a request to the NFT collection contract. Upon receiving this request, the collection contract will automatically update the requesting NFT with the latest version of the code. |
| 31 | + |
| 32 | +Code update functions are not standardized. |
| 33 | + |
| 34 | +```solidity |
| 35 | +interface ITIP4_6Collection { |
| 36 | + event UpgradeNftRequested(uint32 oldVersion, uint32 newVersion, address nft, address initiator); |
| 37 | + event NftCodeUpdated(uint32 oldVersion, uint32 newVersion, uint256 oldCodeHash, uint256 newCodeHash); |
| 38 | +
|
| 39 | + function nftVersion() external view responsible returns (uint32); |
| 40 | + |
| 41 | + function platformCode() external view responsible returns (TvmCell); |
| 42 | + |
| 43 | + function platformCodeInfo() external view responsible returns (uint256 codeHash, uint16 codeDepth); |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +### TIP4_6Collection.nftVersion() |
| 48 | +```solidity |
| 49 | +function nftVersion() external view responsible returns (uint32); |
| 50 | +``` |
| 51 | + |
| 52 | +Returns the current version of the NFT. The Collection stores only the latest version, update history is stored in events. |
| 53 | + |
| 54 | +### TIP4_6Collection.platformCode() |
| 55 | +```solidity |
| 56 | +function platformCode() external view responsible returns (TvmCell); |
| 57 | +``` |
| 58 | + |
| 59 | +Returns the NftPlatform code containing the `collection`(`address`) salt. The NftPlatform code is non-upgradeable. |
| 60 | + |
| 61 | +### TIP4_6Collection.platformCodeInfo() |
| 62 | +```solidity |
| 63 | +function platformCodeInfo() external view responsible returns (uint256 codeHash, uint16 codeDepth); |
| 64 | +``` |
| 65 | + |
| 66 | +Returns the hash and depth of the NftPlatform code. These can be used to calculate the NFT address in third-party contracts. The hash is taken from the NftPlatform code containing the `collection`(`address`) salt. |
| 67 | + |
| 68 | +### Events |
| 69 | +```solidity |
| 70 | +event UpgradeNftRequested(uint32 oldVersion, uint32 newVersion, address nft, address initiator); |
| 71 | +event NftCodeUpdated(uint32 oldVersion, uint32 newVersion, uint256 oldCodeHash, uint256 newCodeHash); |
| 72 | +``` |
| 73 | +**UpgradeNftRequested** |
| 74 | + |
| 75 | +You MUST emit it when an update is requested from the NFT and upgrade is available (the NFT version at the time of the upgrade request is less than the current NFT version in the collection). |
| 76 | + |
| 77 | +**NftCodeUpdated** |
| 78 | + |
| 79 | +You MUST emit it when NFT code is updated in a collection. The function of code update is not standardized. |
| 80 | + |
| 81 | +### Mint |
| 82 | + |
| 83 | +The functions implementing minting are not standardized. The process itself differs from the one proposed in [TIP-4.1](1.md) (see Abstract). |
| 84 | + |
| 85 | +### Upgrade |
| 86 | + |
| 87 | +When NFT code is updated in a collection, the latter changes the NFT version and emits the `NftCodeUpdated` event. |
| 88 | + |
| 89 | +## Nft |
| 90 | +```solidity |
| 91 | +interface ITIP4_6Nft { |
| 92 | + event NftUpgraded(uint32 oldVersion, uint32 newVersion, address initiator); |
| 93 | +
|
| 94 | + function requestUpgrade(address sendGasTo) external; |
| 95 | + |
| 96 | + function version() external view responsible returns (uint32); |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +### TIP4_6Nft.requestUpgrade() |
| 101 | +```solidity |
| 102 | +function requestUpgrade(address sendGasTo) external; |
| 103 | +``` |
| 104 | + |
| 105 | +- `sendGasTo (address)` - the address to which the remaining gas will be sent |
| 106 | + |
| 107 | +Calls the Сollection function, checking if there is an upgrade. If a new version is available, the Сollection upgrades the NFT by calling the appropriate function (this function is not included in the standard, implementations may vary). |
| 108 | + |
| 109 | +### TIP4_6Nft.version() |
| 110 | +```solidity |
| 111 | +function version() external view responsible returns (uint32); |
| 112 | +``` |
| 113 | + |
| 114 | +Returns the current version of NFT. |
| 115 | + |
| 116 | +### Events |
| 117 | +```solidity |
| 118 | +event NftUpgraded(uint32 oldVersion, uint32 newVersion, address initiator); |
| 119 | +``` |
| 120 | + |
| 121 | +**NftUpgraded** |
| 122 | + |
| 123 | +You MUST emit it after the NFT upgrade. |
| 124 | + |
| 125 | +### Upgrade |
| 126 | + |
| 127 | +The NFT upgrade scenario is as follows: |
| 128 | + |
| 129 | +1. The user (in the general case, the NFT manager) calls the NFT `requestUpgrade` function. |
| 130 | +2. The NFT requests an upgrade from the collection, passing information about itself (`id`, `version`). |
| 131 | +3. The Collection compares the version it holds with the one passed by the NFT. |
| 132 | +4. If the version in the collection is larger, then an upgrade is available. The collection emits the `UpgradeNftRequested` event, calls the NFT function and passes it the new code, version and other necessary data |
| 133 | + 1. If the versions are equal, the process aborts and remaining gas is returned to the owner. |
| 134 | +5. NFT sets the new code, emits `NftUpgraded` event |
| 135 | + |
| 136 | +## NftPlatform |
| 137 | + |
| 138 | +NftPlatform's static variables MUST contain `_id`(`uint256`) and nothing else. |
| 139 | + |
| 140 | +The code and interface of the contract are not standardized, except for the previously mentioned static `_id`(`static uint256`) (similar to the NFT contract). As described above, when minting, the collection deploys this contract instead of the NFT, then it upgrades its code to **TIP4_6Nft.** |
| 141 | + |
| 142 | +This approach allows the collection to accept messages from NFTs of any version without retaining all the NFT code used. |
| 143 | + |
| 144 | +An example of this contract is shown below. The NftPlatform code is salted with the address of the Collection that deployed it (similar to the NFT contract) so that the codehash is unique for each Collection. |
| 145 | + |
| 146 | +```solidity |
| 147 | +contract NftPlatform { |
| 148 | + uint8 constant value_is_empty = 101; |
| 149 | + uint8 constant sender_is_not_collection = 102; |
| 150 | + uint8 constant value_is_less_than_required = 104; |
| 151 | +
|
| 152 | + uint256 static _id; |
| 153 | +
|
| 154 | + constructor(TvmCell nftCode, TvmCell data, uint128 remainOnNft) public { |
| 155 | + optional(TvmCell) optSalt = tvm.codeSalt(tvm.code()); |
| 156 | + require(optSalt.hasValue(), value_is_empty); |
| 157 | + address collection = optSalt.get().toSlice().decode(address); |
| 158 | + require(msg.sender == collection, sender_is_not_collection); |
| 159 | + require(remainOnNft != 0, value_is_empty); |
| 160 | + require(msg.value > remainOnNft, value_is_less_than_required); |
| 161 | +
|
| 162 | + initialize(nftCode, data); |
| 163 | + } |
| 164 | +
|
| 165 | + function initialize( |
| 166 | + TvmCell nftCode, |
| 167 | + TvmCell data |
| 168 | + ) private { |
| 169 | + tvm.setcode(nftCode); |
| 170 | + tvm.setCurrentCode(nftCode); |
| 171 | +
|
| 172 | + onCodeUpgrade(data); |
| 173 | + } |
| 174 | +
|
| 175 | + function onCodeUpgrade(TvmCell data) private {} |
| 176 | +} |
| 177 | +``` |
0 commit comments