@tezos-x/octez.js-tzip12 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/dist/lib/composer.js +17 -0
- package/dist/lib/errors.js +43 -0
- package/dist/lib/octez.js-tzip12.js +27 -0
- package/dist/lib/tzip12-contract-abstraction.js +191 -0
- package/dist/lib/tzip12-extension.js +14 -0
- package/dist/lib/version.js +8 -0
- package/dist/octez.js-tzip12.es6.js +281 -0
- package/dist/octez.js-tzip12.es6.js.map +1 -0
- package/dist/octez.js-tzip12.umd.js +290 -0
- package/dist/octez.js-tzip12.umd.js.map +1 -0
- package/dist/types/composer.d.ts +9 -0
- package/dist/types/errors.d.ts +26 -0
- package/dist/types/octez.js-tzip12.d.ts +9 -0
- package/dist/types/tzip12-contract-abstraction.d.ts +42 -0
- package/dist/types/tzip12-extension.d.ts +7 -0
- package/dist/types/version.d.ts +4 -0
- package/package.json +99 -0
- package/signature.json +260 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# octez.js TZIP-012 package
|
|
2
|
+
|
|
3
|
+
*Documentation can be found [here](https://octez.js.io/docs/tzip12)*
|
|
4
|
+
*TypeDoc style documentation is available on-line [here](https://octez.js.io/typedoc/modules/_octez.js_tzip12.html)*
|
|
5
|
+
|
|
6
|
+
**Maintained by [Nomadic Labs](https://www.nomadic-labs.com/).**
|
|
7
|
+
|
|
8
|
+
`@tezos-x/octez.js-tzip12` is an npm package that provides developers with TZIP-12 functionality for octez.js. The package allows retrieving metadata associated with tokens of FA2 contracts.
|
|
9
|
+
|
|
10
|
+
## General Information
|
|
11
|
+
|
|
12
|
+
There are two scenarios to obtain the metadata of a token:
|
|
13
|
+
1. The metadata can be obtained from executing an off-chain view named `token_metadata` present in the contract metadata
|
|
14
|
+
2. or from a big map named `token_metadata` in the contract storage.
|
|
15
|
+
|
|
16
|
+
The `getTokenMetadata` method of the `Tzip12ContractAbstraction` class will find the token metadata with precedence for the off-chain view, if there is one, as specified in the standard. Please refer to the following link for complete documentation on [TZIP-012#Token Metadata](https://gitlab.com/tezos/tzip/-/blob/master/proposals/tzip-12/tzip-12.md#token-metadata).
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
The package can be used to extend the well-known octez.js contract abstraction. The `@tezos-x/octez.js-tzip12` and the `@tezos-x/octez.js` packages need to be installed as follows:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
npm i --save @tezos-x/octez.js-tzip12
|
|
24
|
+
npm i --save @tezos-x/octez.js
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
**Create an instance of the `Tzip12Module` and add it as an extension to the `TezosToolkit`**
|
|
30
|
+
|
|
31
|
+
The constructor of the `Tzip12Module` takes an optional `MetadataProvider` as a parameter. When none is passed, the default `MetadataProvider` of octez.js is instantiated, and the default handlers (`HttpHandler,` `IpfsHandler,` and `TezosStorageHandler`) are used. The `MetadataProvider` can be customized by the user if needed.
|
|
32
|
+
|
|
33
|
+
**Use the `tzip12` function to extend a contract abstraction**
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { TezosToolkit } from '@tezos-x/octez.js';
|
|
37
|
+
import { Tzip12Module, tzip12 } from '@tezos-x/octez.js-tzip12';
|
|
38
|
+
import BigNumber from 'bignumber.js';
|
|
39
|
+
|
|
40
|
+
const Tezos = new TezosToolkit('https://YOUR_PREFERRED_RPC_URL');
|
|
41
|
+
Tezos.addExtension(new Tzip12Module());
|
|
42
|
+
|
|
43
|
+
const contract = await Tezos.contract.at("contractAddress", tzip12)
|
|
44
|
+
|
|
45
|
+
// get the token metadata
|
|
46
|
+
await contract.tzip12().getTokenMetadata(BigNumber(1));
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The `getTokenMetadata` method takes a BigNumber as a parameter that represents the token_id and returns an object matching this interface:
|
|
50
|
+
```
|
|
51
|
+
interface TokenMetadata {
|
|
52
|
+
token_id: BigNumber,
|
|
53
|
+
decimals: number
|
|
54
|
+
name?: string,
|
|
55
|
+
symbol?: string,
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Additional info
|
|
60
|
+
|
|
61
|
+
See the top-level [https://github.com/trilitech/octez.js](https://github.com/trilitech/octez.js) file for details on reporting issues, contributing and versioning.
|
|
62
|
+
|
|
63
|
+
## Disclaimer
|
|
64
|
+
|
|
65
|
+
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tzip12 = tzip12;
|
|
4
|
+
const tzip12_contract_abstraction_1 = require("./tzip12-contract-abstraction");
|
|
5
|
+
const ABSTRACTION_KEY = Symbol('Tzip12ContractAbstractionObjectKey');
|
|
6
|
+
function tzip12(abs, context) {
|
|
7
|
+
return Object.assign(abs, {
|
|
8
|
+
// namespace tzip12
|
|
9
|
+
tzip12() {
|
|
10
|
+
if (!this[ABSTRACTION_KEY]) {
|
|
11
|
+
this[ABSTRACTION_KEY] = new tzip12_contract_abstraction_1.Tzip12ContractAbstraction(this, context);
|
|
12
|
+
}
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
14
|
+
return this[ABSTRACTION_KEY];
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InvalidTokenMetadata = exports.TokenIdNotFound = exports.TokenMetadataNotFound = void 0;
|
|
4
|
+
const octez_js_core_1 = require("@tezos-x/octez.js-core");
|
|
5
|
+
/**
|
|
6
|
+
* @category Error
|
|
7
|
+
* @description Error that indicates the metadata not being found on the contract
|
|
8
|
+
*/
|
|
9
|
+
class TokenMetadataNotFound extends octez_js_core_1.TaquitoError {
|
|
10
|
+
constructor(address) {
|
|
11
|
+
super();
|
|
12
|
+
this.address = address;
|
|
13
|
+
this.name = 'TokenMetadataNotFound';
|
|
14
|
+
this.message = `No token metadata was found for the contract: ${address}`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.TokenMetadataNotFound = TokenMetadataNotFound;
|
|
18
|
+
/**
|
|
19
|
+
* @category Error
|
|
20
|
+
* @description Error that indicates the token ID not being found
|
|
21
|
+
*/
|
|
22
|
+
class TokenIdNotFound extends octez_js_core_1.TaquitoError {
|
|
23
|
+
constructor(tokenId) {
|
|
24
|
+
super(`Could not find token metadata for the token ID: ${tokenId}`);
|
|
25
|
+
this.tokenId = tokenId;
|
|
26
|
+
this.name = 'TokenIdNotFound';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.TokenIdNotFound = TokenIdNotFound;
|
|
30
|
+
/**
|
|
31
|
+
* @category Error
|
|
32
|
+
* @description Error that indicates that the token metadata is invalid (not compliant with the TZIP-12 standard)
|
|
33
|
+
*/
|
|
34
|
+
class InvalidTokenMetadata extends octez_js_core_1.TaquitoError {
|
|
35
|
+
constructor(invalidMetadata) {
|
|
36
|
+
super();
|
|
37
|
+
this.invalidMetadata = invalidMetadata;
|
|
38
|
+
this.name = 'InvalidTokenMetadata';
|
|
39
|
+
this.message =
|
|
40
|
+
'Non-compliance with the TZIP-012 standard. The required property `decimals` is missing.';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.InvalidTokenMetadata = InvalidTokenMetadata;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.VERSION = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* @packageDocumentation
|
|
20
|
+
* @module @tezos-x/octez.js-tzip12
|
|
21
|
+
*/
|
|
22
|
+
__exportStar(require("./tzip12-contract-abstraction"), exports);
|
|
23
|
+
__exportStar(require("./composer"), exports);
|
|
24
|
+
__exportStar(require("./errors"), exports);
|
|
25
|
+
__exportStar(require("./tzip12-extension"), exports);
|
|
26
|
+
var version_1 = require("./version");
|
|
27
|
+
Object.defineProperty(exports, "VERSION", { enumerable: true, get: function () { return version_1.VERSION; } });
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Tzip12ContractAbstraction = void 0;
|
|
13
|
+
const octez_js_michelson_encoder_1 = require("@tezos-x/octez.js-michelson-encoder");
|
|
14
|
+
const octez_js_tzip16_1 = require("@tezos-x/octez.js-tzip16");
|
|
15
|
+
const octez_js_utils_1 = require("@tezos-x/octez.js-utils");
|
|
16
|
+
const errors_1 = require("./errors");
|
|
17
|
+
const tokenMetadataBigMapType = {
|
|
18
|
+
prim: 'big_map',
|
|
19
|
+
args: [
|
|
20
|
+
{ prim: 'nat' },
|
|
21
|
+
{
|
|
22
|
+
prim: 'pair',
|
|
23
|
+
args: [
|
|
24
|
+
{ prim: 'nat', annots: ['%token_id'] },
|
|
25
|
+
{ prim: 'map', args: [{ prim: 'string' }, { prim: 'bytes' }], annots: ['%token_info'] },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
annots: ['%token_metadata'],
|
|
30
|
+
};
|
|
31
|
+
class Tzip12ContractAbstraction {
|
|
32
|
+
constructor(contractAbstraction, context) {
|
|
33
|
+
this.contractAbstraction = contractAbstraction;
|
|
34
|
+
this.context = context;
|
|
35
|
+
this._tzip16ContractAbstraction = new octez_js_tzip16_1.Tzip16ContractAbstraction(this.contractAbstraction, this.context);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* @description Fetches the contract metadata (according to the Tzip-016 standard)
|
|
39
|
+
* @returns An object containing the metadata, the uri, an optional integrity check result and an optional sha256 hash
|
|
40
|
+
* or `Undefined` if the contract has no metadata (non-compliant with Tzip-016)
|
|
41
|
+
*/
|
|
42
|
+
getContractMetadata() {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
try {
|
|
45
|
+
const contractMetadata = yield this._tzip16ContractAbstraction.getMetadata();
|
|
46
|
+
return contractMetadata.metadata;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
// The contract is not compliant with Tzip-016. There is no contract metadata.
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* @description The Tzip-016 "interfaces" field MUST be present in the contract metadata. It should contain "TZIP-012[version-info]"
|
|
55
|
+
* @returns True if "interfaces" field is present and contains "TZIP-012", false otherwise
|
|
56
|
+
*/
|
|
57
|
+
isTzip12Compliant() {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
var _a;
|
|
60
|
+
let isCompliant = false;
|
|
61
|
+
const metadata = yield this.getContractMetadata();
|
|
62
|
+
if (metadata) {
|
|
63
|
+
const tzip12Interface = (_a = metadata.interfaces) === null || _a === void 0 ? void 0 : _a.filter((x) => {
|
|
64
|
+
return x.substring(0, 8) === 'TZIP-012';
|
|
65
|
+
});
|
|
66
|
+
isCompliant = tzip12Interface && tzip12Interface.length !== 0 ? true : false;
|
|
67
|
+
}
|
|
68
|
+
return isCompliant;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @description Fetches the token metadata for a specified token ID.
|
|
73
|
+
* The function first tries to find a `token_metadata` view in the contract metadata and to execute it with the token ID.
|
|
74
|
+
* If there is no view, the function tries to find a `token_metadata` bigmap in the top-level pairs of the storage.
|
|
75
|
+
* @param tokenId The ID of the token for which we want to retrieve token metadata
|
|
76
|
+
* @returns An object of type `TokenMetadata`
|
|
77
|
+
* @throws {@link TokenIdNotFound, TokenMetadataNotFound, InvalidTokenMetadata}
|
|
78
|
+
*/
|
|
79
|
+
getTokenMetadata(tokenId) {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
const tokenMetadata = yield this.retrieveTokenMetadataFromView(tokenId);
|
|
82
|
+
return !tokenMetadata ? this.retrieveTokenMetadataFromBigMap(tokenId) : tokenMetadata;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
retrieveTokenMetadataFromView(tokenId) {
|
|
86
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
87
|
+
if (yield this.getContractMetadata()) {
|
|
88
|
+
const views = yield this._tzip16ContractAbstraction.metadataViews();
|
|
89
|
+
if (views && this.hasTokenMetadataView(views)) {
|
|
90
|
+
return this.executeTokenMetadataView(views['token_metadata'](), tokenId);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
hasTokenMetadataView(views) {
|
|
96
|
+
for (const view of Object.keys(views)) {
|
|
97
|
+
if (view === 'token_metadata') {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
executeTokenMetadataView(tokenMetadataView, tokenId) {
|
|
104
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
105
|
+
const tokenMetadata = yield tokenMetadataView.executeView(tokenId);
|
|
106
|
+
const tokenMap = Object.values(tokenMetadata)[1];
|
|
107
|
+
if (!octez_js_michelson_encoder_1.MichelsonMap.isMichelsonMap(tokenMap)) {
|
|
108
|
+
throw new errors_1.TokenMetadataNotFound(this.contractAbstraction.address);
|
|
109
|
+
}
|
|
110
|
+
const metadataFromUri = yield this.fetchTokenMetadataFromUri(tokenMap);
|
|
111
|
+
return this.formatMetadataToken(tokenId, tokenMap, metadataFromUri);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
fetchTokenMetadataFromUri(tokenMetadata) {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
const uri = tokenMetadata.get('');
|
|
117
|
+
if (uri) {
|
|
118
|
+
try {
|
|
119
|
+
const metadataFromUri = yield this.context.metadataProvider.provideMetadata(this.contractAbstraction, (0, octez_js_utils_1.bytesToString)(uri), this.context);
|
|
120
|
+
return metadataFromUri.metadata;
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
if (e.name === 'InvalidUriError') {
|
|
124
|
+
console.warn(`The URI ${(0, octez_js_utils_1.bytesToString)(uri)} is present in the token metadata, but is invalid.`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
throw e;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
formatMetadataToken(tokenId, metadataTokenMap, metadataFromUri) {
|
|
134
|
+
const tokenMetadataDecoded = {
|
|
135
|
+
token_id: tokenId,
|
|
136
|
+
};
|
|
137
|
+
for (const keyTokenMetadata of metadataTokenMap.keys()) {
|
|
138
|
+
if (keyTokenMetadata === 'decimals') {
|
|
139
|
+
Object.assign(tokenMetadataDecoded, {
|
|
140
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
141
|
+
[keyTokenMetadata]: Number((0, octez_js_utils_1.bytesToString)(metadataTokenMap.get(keyTokenMetadata))),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else if (!(keyTokenMetadata === '')) {
|
|
145
|
+
Object.assign(tokenMetadataDecoded, {
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
147
|
+
[keyTokenMetadata]: (0, octez_js_utils_1.bytesToString)(metadataTokenMap.get(keyTokenMetadata)),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// if an URI is present, add the fetched properties to the object
|
|
152
|
+
// if a property is in the URI and the map, prevalence is accorded to value from the URI
|
|
153
|
+
if (metadataFromUri) {
|
|
154
|
+
for (const property in metadataFromUri) {
|
|
155
|
+
Object.assign(tokenMetadataDecoded, { [property]: metadataFromUri[property] });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (!('decimals' in tokenMetadataDecoded)) {
|
|
159
|
+
throw new errors_1.InvalidTokenMetadata(tokenMetadataDecoded);
|
|
160
|
+
}
|
|
161
|
+
return tokenMetadataDecoded;
|
|
162
|
+
}
|
|
163
|
+
retrieveTokenMetadataFromBigMap(tokenId) {
|
|
164
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
165
|
+
const bigmapTokenMetadataId = yield this.findTokenMetadataBigMap();
|
|
166
|
+
let pairNatMap;
|
|
167
|
+
try {
|
|
168
|
+
pairNatMap = yield this.context.contract.getBigMapKeyByID(bigmapTokenMetadataId['int'].toString(), tokenId.toString(), new octez_js_michelson_encoder_1.Schema(tokenMetadataBigMapType));
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
throw new errors_1.TokenIdNotFound(tokenId);
|
|
172
|
+
}
|
|
173
|
+
const michelsonMap = pairNatMap['token_info'];
|
|
174
|
+
if (!octez_js_michelson_encoder_1.MichelsonMap.isMichelsonMap(michelsonMap)) {
|
|
175
|
+
throw new errors_1.TokenIdNotFound(tokenId);
|
|
176
|
+
}
|
|
177
|
+
const metadataFromUri = yield this.fetchTokenMetadataFromUri(michelsonMap);
|
|
178
|
+
return this.formatMetadataToken(tokenId, michelsonMap, metadataFromUri);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
findTokenMetadataBigMap() {
|
|
182
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
183
|
+
const tokenMetadataBigMapId = this.contractAbstraction.schema.FindFirstInTopLevelPair(yield this.context.readProvider.getStorage(this.contractAbstraction.address, 'head'), tokenMetadataBigMapType);
|
|
184
|
+
if (!tokenMetadataBigMapId || !tokenMetadataBigMapId.int) {
|
|
185
|
+
throw new errors_1.TokenMetadataNotFound(this.contractAbstraction.address);
|
|
186
|
+
}
|
|
187
|
+
return { int: tokenMetadataBigMapId.int };
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.Tzip12ContractAbstraction = Tzip12ContractAbstraction;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Tzip12Module = void 0;
|
|
4
|
+
const octez_js_tzip16_1 = require("@tezos-x/octez.js-tzip16");
|
|
5
|
+
// The same default metadataProvider is used for tzip16 and tzip12
|
|
6
|
+
class Tzip12Module {
|
|
7
|
+
constructor(metadataProvider) {
|
|
8
|
+
this._metadataProvider = metadataProvider ? metadataProvider : new octez_js_tzip16_1.MetadataProvider(octez_js_tzip16_1.DEFAULT_HANDLERS);
|
|
9
|
+
}
|
|
10
|
+
configureContext(context) {
|
|
11
|
+
Object.assign(context, { metadataProvider: this._metadataProvider });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.Tzip12Module = Tzip12Module;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VERSION = void 0;
|
|
4
|
+
// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT!
|
|
5
|
+
exports.VERSION = {
|
|
6
|
+
"commitHash": "f798e97ba998acc2cc5310278d812448591bd312",
|
|
7
|
+
"version": "24.0.2"
|
|
8
|
+
};
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { MichelsonMap, Schema } from '@tezos-x/octez.js-michelson-encoder';
|
|
2
|
+
import { Tzip16ContractAbstraction, MetadataProvider, DEFAULT_HANDLERS } from '@tezos-x/octez.js-tzip16';
|
|
3
|
+
import { bytesToString } from '@tezos-x/octez.js-utils';
|
|
4
|
+
import { TaquitoError } from '@tezos-x/octez.js-core';
|
|
5
|
+
|
|
6
|
+
/******************************************************************************
|
|
7
|
+
Copyright (c) Microsoft Corporation.
|
|
8
|
+
|
|
9
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
10
|
+
purpose with or without fee is hereby granted.
|
|
11
|
+
|
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
13
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
14
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
15
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
16
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
17
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
18
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
19
|
+
***************************************************************************** */
|
|
20
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
24
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
25
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
26
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
27
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
28
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
29
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
34
|
+
var e = new Error(message);
|
|
35
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @category Error
|
|
40
|
+
* @description Error that indicates the metadata not being found on the contract
|
|
41
|
+
*/
|
|
42
|
+
class TokenMetadataNotFound extends TaquitoError {
|
|
43
|
+
constructor(address) {
|
|
44
|
+
super();
|
|
45
|
+
this.address = address;
|
|
46
|
+
this.name = 'TokenMetadataNotFound';
|
|
47
|
+
this.message = `No token metadata was found for the contract: ${address}`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @category Error
|
|
52
|
+
* @description Error that indicates the token ID not being found
|
|
53
|
+
*/
|
|
54
|
+
class TokenIdNotFound extends TaquitoError {
|
|
55
|
+
constructor(tokenId) {
|
|
56
|
+
super(`Could not find token metadata for the token ID: ${tokenId}`);
|
|
57
|
+
this.tokenId = tokenId;
|
|
58
|
+
this.name = 'TokenIdNotFound';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* @category Error
|
|
63
|
+
* @description Error that indicates that the token metadata is invalid (not compliant with the TZIP-12 standard)
|
|
64
|
+
*/
|
|
65
|
+
class InvalidTokenMetadata extends TaquitoError {
|
|
66
|
+
constructor(invalidMetadata) {
|
|
67
|
+
super();
|
|
68
|
+
this.invalidMetadata = invalidMetadata;
|
|
69
|
+
this.name = 'InvalidTokenMetadata';
|
|
70
|
+
this.message =
|
|
71
|
+
'Non-compliance with the TZIP-012 standard. The required property `decimals` is missing.';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const tokenMetadataBigMapType = {
|
|
76
|
+
prim: 'big_map',
|
|
77
|
+
args: [
|
|
78
|
+
{ prim: 'nat' },
|
|
79
|
+
{
|
|
80
|
+
prim: 'pair',
|
|
81
|
+
args: [
|
|
82
|
+
{ prim: 'nat', annots: ['%token_id'] },
|
|
83
|
+
{ prim: 'map', args: [{ prim: 'string' }, { prim: 'bytes' }], annots: ['%token_info'] },
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
annots: ['%token_metadata'],
|
|
88
|
+
};
|
|
89
|
+
class Tzip12ContractAbstraction {
|
|
90
|
+
constructor(contractAbstraction, context) {
|
|
91
|
+
this.contractAbstraction = contractAbstraction;
|
|
92
|
+
this.context = context;
|
|
93
|
+
this._tzip16ContractAbstraction = new Tzip16ContractAbstraction(this.contractAbstraction, this.context);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* @description Fetches the contract metadata (according to the Tzip-016 standard)
|
|
97
|
+
* @returns An object containing the metadata, the uri, an optional integrity check result and an optional sha256 hash
|
|
98
|
+
* or `Undefined` if the contract has no metadata (non-compliant with Tzip-016)
|
|
99
|
+
*/
|
|
100
|
+
getContractMetadata() {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
try {
|
|
103
|
+
const contractMetadata = yield this._tzip16ContractAbstraction.getMetadata();
|
|
104
|
+
return contractMetadata.metadata;
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
// The contract is not compliant with Tzip-016. There is no contract metadata.
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* @description The Tzip-016 "interfaces" field MUST be present in the contract metadata. It should contain "TZIP-012[version-info]"
|
|
113
|
+
* @returns True if "interfaces" field is present and contains "TZIP-012", false otherwise
|
|
114
|
+
*/
|
|
115
|
+
isTzip12Compliant() {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
var _a;
|
|
118
|
+
let isCompliant = false;
|
|
119
|
+
const metadata = yield this.getContractMetadata();
|
|
120
|
+
if (metadata) {
|
|
121
|
+
const tzip12Interface = (_a = metadata.interfaces) === null || _a === void 0 ? void 0 : _a.filter((x) => {
|
|
122
|
+
return x.substring(0, 8) === 'TZIP-012';
|
|
123
|
+
});
|
|
124
|
+
isCompliant = tzip12Interface && tzip12Interface.length !== 0 ? true : false;
|
|
125
|
+
}
|
|
126
|
+
return isCompliant;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* @description Fetches the token metadata for a specified token ID.
|
|
131
|
+
* The function first tries to find a `token_metadata` view in the contract metadata and to execute it with the token ID.
|
|
132
|
+
* If there is no view, the function tries to find a `token_metadata` bigmap in the top-level pairs of the storage.
|
|
133
|
+
* @param tokenId The ID of the token for which we want to retrieve token metadata
|
|
134
|
+
* @returns An object of type `TokenMetadata`
|
|
135
|
+
* @throws {@link TokenIdNotFound, TokenMetadataNotFound, InvalidTokenMetadata}
|
|
136
|
+
*/
|
|
137
|
+
getTokenMetadata(tokenId) {
|
|
138
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
139
|
+
const tokenMetadata = yield this.retrieveTokenMetadataFromView(tokenId);
|
|
140
|
+
return !tokenMetadata ? this.retrieveTokenMetadataFromBigMap(tokenId) : tokenMetadata;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
retrieveTokenMetadataFromView(tokenId) {
|
|
144
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
145
|
+
if (yield this.getContractMetadata()) {
|
|
146
|
+
const views = yield this._tzip16ContractAbstraction.metadataViews();
|
|
147
|
+
if (views && this.hasTokenMetadataView(views)) {
|
|
148
|
+
return this.executeTokenMetadataView(views['token_metadata'](), tokenId);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
hasTokenMetadataView(views) {
|
|
154
|
+
for (const view of Object.keys(views)) {
|
|
155
|
+
if (view === 'token_metadata') {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
executeTokenMetadataView(tokenMetadataView, tokenId) {
|
|
162
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
163
|
+
const tokenMetadata = yield tokenMetadataView.executeView(tokenId);
|
|
164
|
+
const tokenMap = Object.values(tokenMetadata)[1];
|
|
165
|
+
if (!MichelsonMap.isMichelsonMap(tokenMap)) {
|
|
166
|
+
throw new TokenMetadataNotFound(this.contractAbstraction.address);
|
|
167
|
+
}
|
|
168
|
+
const metadataFromUri = yield this.fetchTokenMetadataFromUri(tokenMap);
|
|
169
|
+
return this.formatMetadataToken(tokenId, tokenMap, metadataFromUri);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
fetchTokenMetadataFromUri(tokenMetadata) {
|
|
173
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
174
|
+
const uri = tokenMetadata.get('');
|
|
175
|
+
if (uri) {
|
|
176
|
+
try {
|
|
177
|
+
const metadataFromUri = yield this.context.metadataProvider.provideMetadata(this.contractAbstraction, bytesToString(uri), this.context);
|
|
178
|
+
return metadataFromUri.metadata;
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
if (e.name === 'InvalidUriError') {
|
|
182
|
+
console.warn(`The URI ${bytesToString(uri)} is present in the token metadata, but is invalid.`);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
throw e;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
formatMetadataToken(tokenId, metadataTokenMap, metadataFromUri) {
|
|
192
|
+
const tokenMetadataDecoded = {
|
|
193
|
+
token_id: tokenId,
|
|
194
|
+
};
|
|
195
|
+
for (const keyTokenMetadata of metadataTokenMap.keys()) {
|
|
196
|
+
if (keyTokenMetadata === 'decimals') {
|
|
197
|
+
Object.assign(tokenMetadataDecoded, {
|
|
198
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
199
|
+
[keyTokenMetadata]: Number(bytesToString(metadataTokenMap.get(keyTokenMetadata))),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
else if (!(keyTokenMetadata === '')) {
|
|
203
|
+
Object.assign(tokenMetadataDecoded, {
|
|
204
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
205
|
+
[keyTokenMetadata]: bytesToString(metadataTokenMap.get(keyTokenMetadata)),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// if an URI is present, add the fetched properties to the object
|
|
210
|
+
// if a property is in the URI and the map, prevalence is accorded to value from the URI
|
|
211
|
+
if (metadataFromUri) {
|
|
212
|
+
for (const property in metadataFromUri) {
|
|
213
|
+
Object.assign(tokenMetadataDecoded, { [property]: metadataFromUri[property] });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!('decimals' in tokenMetadataDecoded)) {
|
|
217
|
+
throw new InvalidTokenMetadata(tokenMetadataDecoded);
|
|
218
|
+
}
|
|
219
|
+
return tokenMetadataDecoded;
|
|
220
|
+
}
|
|
221
|
+
retrieveTokenMetadataFromBigMap(tokenId) {
|
|
222
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
223
|
+
const bigmapTokenMetadataId = yield this.findTokenMetadataBigMap();
|
|
224
|
+
let pairNatMap;
|
|
225
|
+
try {
|
|
226
|
+
pairNatMap = yield this.context.contract.getBigMapKeyByID(bigmapTokenMetadataId['int'].toString(), tokenId.toString(), new Schema(tokenMetadataBigMapType));
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
throw new TokenIdNotFound(tokenId);
|
|
230
|
+
}
|
|
231
|
+
const michelsonMap = pairNatMap['token_info'];
|
|
232
|
+
if (!MichelsonMap.isMichelsonMap(michelsonMap)) {
|
|
233
|
+
throw new TokenIdNotFound(tokenId);
|
|
234
|
+
}
|
|
235
|
+
const metadataFromUri = yield this.fetchTokenMetadataFromUri(michelsonMap);
|
|
236
|
+
return this.formatMetadataToken(tokenId, michelsonMap, metadataFromUri);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
findTokenMetadataBigMap() {
|
|
240
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
241
|
+
const tokenMetadataBigMapId = this.contractAbstraction.schema.FindFirstInTopLevelPair(yield this.context.readProvider.getStorage(this.contractAbstraction.address, 'head'), tokenMetadataBigMapType);
|
|
242
|
+
if (!tokenMetadataBigMapId || !tokenMetadataBigMapId.int) {
|
|
243
|
+
throw new TokenMetadataNotFound(this.contractAbstraction.address);
|
|
244
|
+
}
|
|
245
|
+
return { int: tokenMetadataBigMapId.int };
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const ABSTRACTION_KEY = Symbol('Tzip12ContractAbstractionObjectKey');
|
|
251
|
+
function tzip12(abs, context) {
|
|
252
|
+
return Object.assign(abs, {
|
|
253
|
+
// namespace tzip12
|
|
254
|
+
tzip12() {
|
|
255
|
+
if (!this[ABSTRACTION_KEY]) {
|
|
256
|
+
this[ABSTRACTION_KEY] = new Tzip12ContractAbstraction(this, context);
|
|
257
|
+
}
|
|
258
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
259
|
+
return this[ABSTRACTION_KEY];
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// The same default metadataProvider is used for tzip16 and tzip12
|
|
265
|
+
class Tzip12Module {
|
|
266
|
+
constructor(metadataProvider) {
|
|
267
|
+
this._metadataProvider = metadataProvider ? metadataProvider : new MetadataProvider(DEFAULT_HANDLERS);
|
|
268
|
+
}
|
|
269
|
+
configureContext(context) {
|
|
270
|
+
Object.assign(context, { metadataProvider: this._metadataProvider });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT!
|
|
275
|
+
const VERSION = {
|
|
276
|
+
"commitHash": "f798e97ba998acc2cc5310278d812448591bd312",
|
|
277
|
+
"version": "24.0.2"
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
export { InvalidTokenMetadata, TokenIdNotFound, TokenMetadataNotFound, Tzip12ContractAbstraction, Tzip12Module, VERSION, tzip12 };
|
|
281
|
+
//# sourceMappingURL=octez.js-tzip12.es6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"octez.js-tzip12.es6.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|