# Open API 教程与最佳实践 ## 1. 准备工作 ### 1.1 注册 在使用 open API 前,首先需要在金色传说(主网环境:[https://v.mibao.net/](https://v.mibao.net) ,测试网环境:[https://staging.nervina.c](https://staging.nervina.cn)[n](https://staging.nervina.cn) )上进行注册,该帐号将会成为通过 open API 设计和发行秘宝的创作者,相关数据可以通过在金色传说平台登录该帐号进行查看。 - 测试环境请求地址为:[https://goldenlegend.staging.nervina.cn/api/v1/](https://goldenlegend.staging.nervina.cn/api/v1/) - 正式环境请求地址为:[https://goldenlegend.nervina.cn/api/v1/](https://goldenlegend.nervina.cn/api/v1/) - 创作者平台正式环境:[https://v.mibao.net/](https://v.mibao.net) - 创作者平台测试环境:[https://staging.nervina.cn](https://staging.nervina.cn) - 秘宝账户正式环境:[https://mibao.net/explore](https://mibao.net/explore) - 秘宝账户测试环境:[https://wallet.staging.nervina.cn](https://wallet.staging.nervina.cn) ### 1.2 设置公开信息 完成注册后,必须首先设置创作者(issuer)公开信息才可以进行后续操作。这里设置的公开信息会在链上公开存储,上链操作由金色传说平台自动完成,设计和发行秘宝均置后于该步骤,因此注册后必须执行此操作。 ### 1.3 申请 open API 的 key 和 secret open API 的鉴权需要响应的 key-secret,在完成上述两步操作之后,可以发送邮件到 biz@nervina.io 进行申请,内容必须包含要申请 key-secret 的帐号的注册邮箱。 ### 1.4 能量点充值 在金色传说平台设计和分发秘宝都需要消耗能量点(设计秘宝消耗 1 能量点;每分发一个秘宝消耗 1 能量点);在进行创作和发行前需要进行能量点充值。 完成上述操作后,即可通过 open API 在金色传说平台进行操作。 ## 2. 开放 API 签名算法 为了保证安全性,用户在 HTTP 请求中增加 Authorization 的 Header 来包含签名信息,表明这个消息已被授权。 ### 2.1 Authorization 字段计算的方法 ```plain Authorization = 'NFT ' + AccessKeyId + ':' + Signature Signature = base64(hmac-sha1(AccessKeySecret, VERB + '\n' + FULLPATH + '\n' + Content-MD5 + '\n' + Content-Type + '\n' + Date)) ``` * `AccessKeyId` 即为通过邮件申请获取到的 open API 的 key * `AccessKeySecret` 即为通过邮件申请获取到的 open API 的 secret * `VERB` 表示请求的 method,包括 GET、POST、PUT 等 * `\n` 表示换行符 * `FULLPATH` 表示请求的 endpoint,如果有 query-string,也需要包含在内 * `Content-MD5` 表示请求内容数据的 MD5 值,对消息内容(不包括头部)计算 MD5 值获得 128 比特位数字,然后对该数字进行 base64 编码即可得到。该请求头可用于消息合法性的检查(消息内容是否与发送时一致);当正文为空时,Content-MD5 留空即可。详情可参看 [RFC2616 Content-MD5](https://www.ietf.org/rfc/rfc2616.txt) * `Content-Type` 表示请求内容的类型,如 `application/json` ,也可以为空 * `Date` 表示此次操作的时间,必须为 GMT 格式,如 `Sun, 22 Nov 2015 08:16:38 GMT` 上述 headers 中, `Date` 、 `Content-Type` 和 `Authorization` 为必须包含的字段,且 `Date` 中的时间和金色传说服务器的时间相差不能超过 10 分钟,否则金色传说服务器将拒绝该请求,并返回 401 错误。 在计算签名时, `Content-Type` 和 `Content-MD5` 可以为空字符,但不能省略。 ### 2.2 计算规则 * 签名的字符串必须为 `UTF-8` 格式,含有中文字符的签名字符串必须先进行 `UTF-8` 编码,再进行后续计算。 * 签名的方法为 RFC 2014 中定义的 `HMAC-SHA1` 方法,其中 key 为 `AccessKeySecret` * `Content-Type` 和 `Content-MD5` 在请求中不是必须的,如果为空时,留空即可 ### 2.3 Python 代码示例 Python 版本为 3.9.1。示例中, `AccessKeyId = '44CF9590006BF252F707'` , `AccessKeySecret = 'OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV'` ```plain # 请求 GET /api/v1/token_classes Content-MD5: '' Content-Type: 'application/json' Date: Tue, 06 Jul 2021 00:00:34 GMT # 签名字符串为 'GET\n/api/v1/token_classes\n\napplication/json\nTue, 06 Jul 2021 00:00:34 GMT' ``` Python 代码如下: ```python import base64 import hmac import hashlib from datetime import datetime def get_signature(secret, method, endpoint, content, gmt, content_type='application/json'): if content: content_md5 = base64.b64encode( hashlib.md5(content.encode('utf-8')).digest()).decode('utf-8') else: content_md5 = '' msg = f'{method}\n{endpoint}\n{content_md5}\n{content_type}\n{gmt}' h = hmac.digest(secret.encode('utf-8'), msg.encode('utf-8'), hashlib.sha1) signature = base64.b64encode(h).decode('utf-8') return signature key = '44CF9590006BF252F707' secret = 'OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV' method = 'GET' endpoint = '/api/v1/token_classes' content = '' content_type = 'application/json' gmt = 'Tue, 06 Jul 2021 00:00:34 GMT' signature = get_signature(secret, method, endpoint, content, gmt, content_type) # SXc3VHXXbU08qzYdAm1RvwMWaUw= auth = f'NFT {key}:{signature}' print(auth) # NFT 44CF9590006BF252F707:SXc3VHXXbU08qzYdAm1RvwMWaUw= ``` 得到 `auth` 后,最终的请求为: ```plain GET /api/v1/token_classes Content-MD5: '' Content-Type: 'application/json' Date: Tue, 06 Jul 2021 00:00:34 GMT Authorization: NFT 44CF9590006BF252F707:SXc3VHXXbU08qzYdAm1RvwMWaUw= ``` ### 2.4 常见错误 * 请求的 Header 中缺少 `Content-Type` 、 `Date` 或 `Authorization` ,返回 `Missing Content-Type/Date/Authorization in header` * 请求中使用了错误的 key,返回 `Cannot find access key` * 请求计算签名使用了错误的 secret 或者计算签名有误,返回 `Signature mismatch` ,并返回 `string_to_sign` ,为该请求用来计算签名的 message,方便排查计算签名遇到的问题 * 请求 header 中的 `Authorization` 格式应为 `NFT key:signature` ,格式错误时返回 `Cannot find access key` * 请求中的时间与服务器时间相差超过 10 分钟,返回 `Time expired` ## 3. 术语说明 * `token_class` : 可以理解为秘宝的模具,包含秘宝的基本信息,将会在链上存储;一个秘宝必须先被设计出秘宝模具,通过秘宝模具才能进行铸造和分发。 * `uuid` : 为了方便使用而被创建出来的一种 id,其中包括: * `token_class_uuid` : 关联到对应的 `token_class` * `token_uuid` : 关联到具体的 NFT Token * `tx_uuid` : 关联分发和转让的交易 * `address` : ckb 地址 * `nft_type_args` : NFT Token Cell 的 Type Script 中的 `args` 字段 ## 4. 设计秘宝、铸造和分发秘宝 相关代码可以参考: [https://github.com/nervina-labs/nft_open_api_demo_python](https://github.com/nervina-labs/nft_open_api_demo_python) ### 4.1 设计秘宝 相关文档:[POST /](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[t](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[ok](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[e](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[n](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[_](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[c](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[l](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[as](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[s](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes)[es](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes) 设计秘宝必须包含以下字段: * name: 秘宝的名称,不超过 30 个字符(一旦创建,不可修改) * description: 秘宝的简介,不超过 200 个字符(一旦创建,不可修改) * total: string 类型的非负整数,为 0 时,表示秘宝不限量;大于 0 时,表示秘宝限量的数量(一旦创建,不可修改) * renderer: 秘宝的媒体信息,必须为以 `https://` 开头、媒体信息有效的 url,且不超过 255 字符(秘宝设计完成后,30 天内可修改一次 renderer) * renderer 为图片:支持格式为 png, jpg, jpeg, gif, webp 和 svg 6 种格式,必须以 `https://` 开头,且结尾为 `png | jpg | jpeg | gif | webp | svg`。 * renderer 为视频或 3D:支持格式为 mp4, webm, glb 和 gltf 6 种格式,必须以 `https://` 开头,且结尾为 `mp3 | wav | mp4 | webm | glb | gltf`。当 renderer 为音视频或 3D 时,同时需要传入参数 `cover_image_url` 用于设置 NFT 封面,校验规则与图片格式的 renderer 一致。 * * 设计的秘宝 token class 会上链,上链操作由平台完成。 代码示例: ```python import json import requests key = '' secret = '' method = 'POST' endpoint = '/token_classes' content = { 'name': 'New Token Class Example', 'description': 'Create Token Class by Open API', 'total': '0', 'renderer': 'https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg' } content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=json.dumps(content)) ``` 返回: ```json { 'name': 'New Token Class Example', 'description': 'Create Token Class by Open API', 'issued': '0', 'renderer': 'https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg', 'uuid': 'e6469650-6ac5-4477-a64b-2a97f4168356', 'total': '0', 'tags': [] } ``` 其中, `uuid` 为平台生成的唯一标识 token class id ```python # 如果创建音频或视频格式的秘宝,则 content 如下所示: content = { 'name': 'New Token Class Example', 'description': 'Create Token Class by Open API', 'total': '0', 'renderer': 'https://oss.jinse.cc/production/583b109e-1fc3-42bd-937f-f4935ae80167.mp4', 'cover_image_url': 'https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg' } ``` * 在设计秘宝时提供可选参数`configure`,用于设置 NFT class cell 的 `configure`,金色传说平台默认设置为 `11000000`, 相关信息可参考:[https://talk.nervos.org/t/rfc-multi-purpose-nft-draft-spec/5434](https://talk.nervos.org/t/rfc-multi-purpose-nft-draft-spec/5434)。 `configure` 格式应为类似 `11000000` 的 8 位二进制字符串,否则会返回 `configure field format error` 请求示例: ```python # 可选参数 configure 设置 NFT class cell 的 configure content = { 'name': 'New Token Class with Configure Example', 'description': 'Create Token Class by Open API', 'total': '0', 'configure': '00000000', 'renderer': 'https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg' } ``` #### 4.1.1 创建音频类型秘宝 秘宝创作者平台支持以专辑的形式创建音频类型秘宝,每个音频类型秘宝支持上传设置 10 首单曲。同时创作者可以自定义设置仅持有人可以播放或所有人都可以播放。 创建音频类型秘宝时,除了创建秘宝时需要传入的必要参数外,还需要传入以下参数: * `renderer`: 音频类型的 `renderer` 必须为图片,支持 png, jpg, jpeg, gif, webp 和 svg 6 种格式; * `album_attributes`: 是一个 json object,用于设置音频内容,包括: * `album_attributes.is_private`: `true` 表示仅持有人才可以播放音频内容;`false` 表示所有人可以播放音频内容; * `album_attributes.audios_attributes`: json array,用于设置音频内容,最多支持设置 10 首音频,音频仅支持 `mp3` 格式; 请求示例: ```python= content = { 'name': 'New Album NFT Example', 'description': 'Album NFT', 'total': '0', 'renderer': 'https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg' 'album_attributes': { 'is_private': True, 'audios_attributes': [ { 'name': 'First song', 'audio_url': 'https://oss.jinse.cc/production/d5d35015-0bcf-46c1-bce5-ff3288e2cf6b.mp3' }, { 'name': 'Second song', 'audio_url': 'https://oss.jinse.cc/production/d5d35015-0bcf-46c1-bce5-ff3288e2cf6b.mp3' } ] } } ``` 返回示例: ```json= { "name": "New Album NFT Example", "description": "Album NFT", "issued": "0", "renderer": "https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg", "cover_image_url": null, "uuid": "99d58b7c-23b1-4efd-8582-9539d01deaf8", "total": "0", "renderer_type": "audio", "album": { "is_private": true, "audios": [ { "name": "First song", "audio_url": "https://oss.jinse.cc/production/d5d35015-0bcf-46c1-bce5-ff3288e2cf6b.mp3" }, { "name": "Second song", "audio_url": "https://oss.jinse.cc/production/d5d35015-0bcf-46c1-bce5-ff3288e2cf6b.mp3" } ] } } ``` * 已创建的音频秘宝,`renderer` 及 `album_attributes` 支持修改,但 30 天内仅限修改一次 ### 4.2 查询 token class 相关文档:[GET /token_classes/{uuid}](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/get_token_classes__uuid__) 查询字段: * `uuid` : 在创建 token class 时返回的 uuid 代码示例: ```python import requests key = '' secret = '' method = 'GET' uuid = '7b1eb753-77a8-46ec-ad8a-d78bc204b8d3' endpoint = f'/token_classes/{uuid}' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}'} requests.request(method, url, headers=headers) ``` 返回: ```json { "name": "New token class", "description": "New token class description", "issued": "0", "renderer": "https://oss.jinse.cc/production/8ee71e29-3b10-4d15-b68c-380c7840c653.jpeg", "uuid": "7b1eb753-77a8-46ec-ad8a-d78bc204b8d3", "total": "0", "verified_info": { "is_verified": true, "verified_title": "金色传说官方认证", "verified_source": "official" }, "tags": [] } ``` ### 4.3 查询金色传说账户下所有的 token class 相关文档:[GET /token_classes](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/get_token_classes) 代码示例: ```python import requests key = '' secret = '' method = 'GET' endpoint = '/token_classes' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers) ``` 返回: ```json { "token_classes": [ { "name": "New token class", "description": "New token class description", "issued": "0", "renderer": "https://oss.jinse.cc/production/8ee71e29-3b10-4d15-b68c-380c7840c653.jpeg", "uuid": "7b1eb753-77a8-46ec-ad8a-d78bc204b8d3", "total": "0", "is_banned": false, "verified_info": { "is_verified": true, "verified_title": "金色传说官方认证", "verified_source": "official" }, "tags": [] }, ... ], "meta": { "next_cursor": "1314", "has_banned_items": true } } ``` ### 4.4 铸造并分发秘宝 相关文档:[POST /token_classes/{token_class_uuid}/tokens](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/post_token_classes__token_class_uuid__tokens) 查询字段: * `token_class_uuid` : 设计秘宝时返回的 token class uuid 请求字段: * `addresses` : 一个由 ckb 地址组成的列表 代码示例: ```python import json import requests key = '' secret = '' method = 'POST' uuid = 'e6469650-6ac5-4477-a64b-2a97f4168356' address = 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqycw877rwy0uuwspsh9cteaf8kqp8nzjl0dxfp' endpoint = f'/token_classes/{uuid}/tokens' content = { 'addresses': [ address, address ] } content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=json.dumps(content)) ``` 返回: ```json [ { 'uuid': 'e7c87177-2461-4ea1-8b8f-35d8b2ca34ea', 'oid': '3eefc8278f217311c88c446731b703d7', 'version': 0, 'characteristic': '0000000000000000', 'issuer_id': 1, 'token_class_id': 626, 'state': 'pending', 'configure': 192, 'n_issuer_id': '0xf90f9c38b0ea0815156bbc340c910d0a21ee57cf', 'n_state': 0, 'created_at': '2021-07-06T01:57:48.140Z', 'updated_at': '2021-07-06T01:57:48.140Z' }, { 'uuid': 'a46916b6-b180-4020-ab2b-59377fe3d9e9', 'oid': '3eefc8278f217311c88c446731b703d7', 'version': 0, 'characteristic': '0000000000000000', 'issuer_id': 1, 'token_class_id': 626, 'state': 'pending', 'configure': 192, 'n_issuer_id': '0xf90f9c38b0ea0815156bbc340c910d0a21ee57cf', 'n_state': 0, 'created_at': '2021-07-06T01:57:48.140Z', 'updated_at': '2021-07-06T01:57:48.140Z' } ] ``` 其中, `uuid` 是具体的 NFT token id - 铸造并分发秘宝时可以通过传入可选参数 `characteristic` 来为 token 设置不同的 `characteristic`,默认为 `0000000000000000`,`characteristic` 相关信息可以参考:https://talk.nervos.org/t/rfc-multi-purpose-nft-draft-spec/5434 。`characteristic` 参数格式应为类似 `'0123456789abcdef'` 的十六位十六进制字符串,否则会返回 `the characteristic of token is invalid` 错误。如果在一次请求中分发了多个 token,则每个 token 的 `characteristic` 均为传入参数设置的值。请求示例如下: ```python= content = { 'addresses': [ address, address ], 'characteristic': '0123456789abcdef' } requests.request(method, url, headers=headers, data=json.dumps(content)) ``` 每个 token 的 `characteristic` 可以在分发的返回或 token 相关查询接口的返回中获取。 ### 4.5 查询 NFT token 相关文档:[GET /tokens/{token_uuid}](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/get_tokens__uuid_) 查询参数: * `token_uuid` : 铸造并分发秘宝时返回的 token uuid 代码示例: ```python import requests key = '' secret = '' method = 'GET' token_uuid = '992179fc-515e-42d7-b731-79ae23c92f6c' endpoint = f'/tokens/{token_uuid}' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers) ``` 返回: ```json { "name": "First NFT 009😄", "description": "first nft 009\nasdfasdfas", "issued": "6", "total": "11", "bg_image_url": "https://img.ggg.com/xxxxxx.jpg", "issuer_info": { "name": "Alice", "avatar_url": null, "uuid": "6c7d8931-d3ef-4fbb-ae70-0db1c9353f81" }, "tx_state": "committed", "class_uuid": "6bf5ae4f-c941-460a-bdc7-1e67115ee740", "from_address": "6c7d8931-d3ef-4fbb-ae70-0db1c9353f81", "to_address": "ckt1qyqt8lh56d05m9ylnme0rks5phx75pk7ppjqkjmndc", "is_class_banned": false, "is_issuer_banned": false, "n_token_id": 5, "verified_info": { "is_verified": null, "verified_title": "", "verified_source": "" }, "class_likes": 0, "class_liked": false, "uuid": "992179fc-515e-42d7-b731-79ae23c92f6c", "created_at": "2021-07-22T08:46:22.589Z", "distribution_method": "send", "tx_hash": "0x01c308910bc4840c1cde69d15365a9f3b8c4756b955255843c5c7d5046b27f47", "tx_uuid": "71e5d500-f0b1-4d47-8206-54ec09a09897" } ``` ### 4.6 根据 token id 查询持有者地址 相关文档:[GET /tokens/{token_uuid}/address](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/default/get_tokens__uuid__address) 查询参数: * `token_uuid` : 铸造并分发后返回的 token uuid 代码示例: ```python import requests key = '' secret = '' method = 'GET' token_uuid = 'fd9b685b-a37f-45e5-81cf-0043433460b8' endpoint = f'/tokens/{token_uuid}/address' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回: ```json { 'address': 'ckt1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xws7nx9v0ycll73vnzpsc0nvm3rh8jkc5g2a7xm59' } ``` ## 5. NFT 持有者相关查询 ### 5.1 查询地址所持有的 NFT 相关文档:[GET /indexer/holder_tokens/{address}](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/indexer/get_indexer_holder_tokens__address_) 查询参数: * `address` : 要查询的 ckb 地址 代码示例: ```python import requests key = '' secret = '' method = 'GET' address = 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqycw877rwy0uuwspsh9cteaf8kqp8nzjl0dxfp' endpoint = f'/indexer/holder_tokens/{address}' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回: ```json { 'holder_address': 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqycw877rwy0uuwspsh9cteaf8kqp8nzjl0dxfp', 'meta': { 'total_count': 9, 'max_page': 1, 'current_page': 1 }, 'token_list': [ { 'token_uuid': 'a46916b6-b180-4020-ab2b-59377fe3d9e9', 'n_token_id': 1, 'class_uuid': 'e6469650-6ac5-4477-a64b-2a97f4168356', 'class_name': 'New Token Class Example', 'class_bg_image_url': 'https://examples.jpg', 'class_description': 'Create Token Class by Open API', 'class_total': '0', 'class_issued': '2', 'is_class_banned': false, 'tx_state': 'committed', 'issuer_name': '名字很长的创作者名字很长的创作者名很长的创作者名字很长的创', 'issuer_avatar_url': 'https://images.pexels.com/photos/3396664/peels-photo-3396664.jpeg', 'issuer_uuid': 'f0d044c2-9ddb-4406-87b3-b281fbb27a76', 'is_issuer_banned': false, 'from_address': 'f0d044c2-9ddb-4406-87b3-b281fbb27a76', 'to_address': 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqycw877rwy0uuwspsh9cteaf8kqp8nzjl0dxfp', 'token_outpoint': { 'tx_hash': '0x24ec444afe9d44e664214f659109d4ff852dc72c065f0f12dc921a65df6e4b13', 'index': 2 }, 'verified_info': { 'is_verified': true, 'verified_title': '知名Rapper,Hiphop文化推广者', 'verified_source': 'official' }, 'class_likes': 0 } ] } ``` ### 5.2 查询 nft type args 对应的 token 相关文档:[GET /indexer/tokens/{nft_type_args}](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/indexer/get_indexer_tokens__nft_type_args_) 查询参数: * `nft_type_args` : 在对应的 CKB Cell 的 Type Script 中的 args 代码示例: ```python import requests key = '' secret = '' method = 'GET' nft_type_args = '0xf90f9c38b0ea0815156bbc340c910d0a21ee57cf0000002900000000' endpoint = f'/indexer/tokens/{nft_type_args}' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回: ```json { 'token': { 'id': 3271, 'uuid': 'db144551-e65f-4dc4-9037-fc017734ef7b', 'issuer_id': 1, 'token_class_id': 620, 'n_issuer_id': '0xf90f9c38b0ea0815156bbc340c910d0a21ee57cf', 'n_token_id': 0, 'version': 0, 'configure': 192, 'characteristic': '0000000000000000', 'n_state': 0, 'state': 'committed', 'created_at': '2021-07-05T11:40:02.071Z', 'updated_at': '2021-07-05T11:40:02.130Z', 'oid': '84cbe247c0792718523fecec799b662b', 'is_committed': True, 'verified_info': { 'is_verified': true, 'verified_title': '知名Rapper,Hiphop文化推广者', 'verified_source': 'official' }, }, 'cell': { 'block_number': 2024829, 'out_point': { 'tx_hash': '0x511af426c9117d5040c05e95538ba3525d806d97f74a0688cbb835023dd30c7e', 'index': 1 }, 'output': { 'capacity': 13400000000, 'lock': { 'code_hash': '0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63', 'args': '0x9b67065717f46e887d8b9647571db949beec9933', 'hash_type': 'type' }, 'type': { 'code_hash': '0xb1837b5ad01a88558731953062d1f5cb547adf89ece01e8934a9f0aeed2d959f', 'args': '0xf90f9c38b0ea0815156bbc340c910d0a21ee57cf0000002900000000', 'hash_type': 'type' } }, 'output_data': '0x000000000000000000c000', 'tx_index': 1 } } ``` ### 5.3 查询 token class 对应的 token 相关文档:GET /indexer/token_classes/{token_class_uuid}/tokens 查询参数: * `holder_address` : 查询某个地址是否持有该 token class 中的 token * `limit` : 可选参数,分页参数,单次查询返回 token 列表的数量,最大为 20, * `page` : 可选参数,分页参数,指定查询返回的 token 范围 代码实例: ```python import requests key = '' secret = '' method = 'GET' endpoint = '/indexer/token_classes/8e558035-a5d6-4722-a27b-18f705c09c29/tokens' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers) ``` 返回: ```json { "tokens": [ { "name": "WEBP", "description": "", "issued": "1", "total": "0", "bg_image_url": "https://oss.jinse.cc/production/6e839348-95ea-4e8c-b309-a7b9cf184da2.webp?tid=0", "issuer_info": { "name": "名字很长的创作者名字很长的创作者名很长的创作者名字很长的创", "avatar_url": "", "uuid": "f0d044c2-9ddb-4406-87b3-b281fbb27a76", "issuer_follows": 3, "issuer_followed": false }, "tx_state": "committed", "class_uuid": "8e558035-a5d6-4722-a27b-18f705c09c29", "from_address": "f0d044c2-9ddb-4406-87b3-b281fbb27a76", "to_address": "ckt1qsfy5cxd0x0pl09xvsvkmert8alsajm38qfnmjh2fzfu2804kq47v8j0qjulvfk56l2wtnc6merump3ydfz2ytk0ruz", "is_class_banned": false, "n_token_id": 0, "verified_info": { "is_verified": true, "verified_title": "我是第一个被官方认证的", "verified_source": "official" }, "uuid": "aca48571-8ea2-43c3-9ea6-a768d997544c", "created_at": "2021-09-13T06:34:28.346Z", "distribution_method": "send", "tx_hash": "0xae39ce28f3756f887685acace5f5c4eed5ffd5e0227dcd3e7df70fdc7f660481", "tx_uuid": "7f2d1e2f-1807-41d9-b41f-c6ff7a325628", "renderer_type": "image", "renderer": "https://oss.jinse.cc/production/6e839348-95ea-4e8c-b309-a7b9cf184da2.webp?tid=0" } ], "meta": { "total_count": 1, "max_page": 1, "current_page": 1 } } ``` ## 6. 转让交易 创作者完成铸造和分发秘宝之后,持有者可以通过自己的密钥签名发起转让交易。 此处要求应用的开发人员自己使用 CKB 的 SDK 来生成地址,并在自己的系统中存储公私钥(金色传说平台不涉及持有人的密钥管理),并使用密钥在转账交易中进行签名。 ### 6.1 生成转让交易 相关文档:[GET /tx/token_transfers/new](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/unsigned_tx_generators/get_tx_token_transfers_new) 查询参数: * `from_address` : token 持有者的地址,同时也是需要对生成交易签名的私钥持有者 * `to_address` : 转让交易的目标地址,一旦转让,交易无法撤回 * `token_uuid` : 要转让 token 的 uuid * `nft_type_args` : token 对应 cell 的 type script args **注意:**`token_uuid`**和**`nft_type_args`**两者必须满足有且仅有一个** 代码示例: ```python import requests key = '' secret = '' method = 'GET' from_address = 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdx83pmv9wj80kf0w5zfym9am9eply253tuu8v5lsn' to_address = 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqycw877rwy0uuwspsh9cteaf8kqp8nzjl0dxfp' # nft_type_args = 'cf12d78e-96b9-40d1-b6ca-1d5ff1d87287' # endpoint = f'/api/v1/tx/token_transfers/new?from_address={from_address}&to_address={to_address}&nft_type_args={nft_type_args}' token_uuid = 'a46916b6-b180-4020-ab2b-59377fe3d9e9' endpoint = f'/tx/token_transfers/new?from_address={from_address}&to_address={to_address}&token_uuid={token_uuid}' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回: ```json { "unsigned_tx": { "version": "0x0", "cell_deps": [ { "out_point": { "tx_hash": "0xbd262c87a84c08ea3bc141700cf55c1a285009de0e22c247a8d9597b4fc491e6", "index": "0x2" }, "dep_type": "code" }, { "out_point": { "tx_hash": "0xd346695aa3293a84e9f985448668e9692892c959e7e83d6d8042e59c08b8cf5c", "index": "0x0" }, "dep_type": "code" }, { "out_point": { "tx_hash": "0x03dd2a5594ed2d79196b396c83534e050ba0ad07fa5c1cd61a7094f9fb60a592", "index": "0x0" }, "dep_type": "code" }, { "out_point": { "tx_hash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37", "index": "0x0" }, "dep_type": "dep_group" } ], "header_deps": [], "inputs": [ { "previous_output": { "tx_hash": "0x0e9cad38a05005dff037c5c1df917efe5be53409976405a330ed15e14f939d3b", "index": "0x0" }, "since": "0x0", "capacity": "0x31eb3ba4e", "lock": { "code_hash": "0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63", "args": "0xc43b615d23bec97ba8249365eecb90fc8aa457ce", "hash_type": "type" }, "type": { "code_hash": "0xb1837b5ad01a88558731953062d1f5cb547adf89ece01e8934a9f0aeed2d959f", "args": "0xf90f9c38b0ea0815156bbc340c910d0a21ee57cf0000002300000000", "hash_type": "type" } } ], "outputs": [ { "capacity": "0x31eb3b450", "lock": { "code_hash": "0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63", "args": "0x009871fde1b88fe71d00c2e5c2f3d49ec009e629", "hash_type": "type" }, "type": { "code_hash": "0xb1837b5ad01a88558731953062d1f5cb547adf89ece01e8934a9f0aeed2d959f", "args": "0xf90f9c38b0ea0815156bbc340c910d0a21ee57cf0000002300000000", "hash_type": "type" } } ], "outputs_data": [ "0x000000000000000000c000" ], "witnesses": ["0x"] } } ``` ### 6.2 对签名后的交易发送上链 相关文档:[POST /tx/token_transfers](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/unsigned_tx_generators/post_tx_token_transfers) 查询参数: * `from_address` : NFT token 持有者地址,也是需要对交易签名的私钥持有者 * `to_address` : 转让交易的目标地址 * `token_uuid` : token 在金色传说平台对应的唯一 uuid(可选参数) * `nft_type_args` : token 对应 cell 的 type script args(可选参数) * `signed_tx` : 签名完成的交易字符串 **注意:**`token_uuid`**和**`nft_type_args`**两个参数中至少需要一个,同时传入时会接受**`token_uuid`**进行后续操作** 代码示例: ```python import requests key = '' secret = '' method = 'POST' endpoint = '/tx/token_transfers' content = { 'from_address': 'ckt1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xws7nx9v0ycll73vnzpsc0nvm3rh8jkc5g2a7xm59', 'to_address': 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqycw877rwy0uuwspsh9cteaf8kqp8nzjl0dxfp', 'nft_type_args': '0xf90f9c38b0ea0815156bbc340c910d0a21ee57cf0000003300000002', 'token_uuid': 'f40588f2-7ed3-468c-afbf-ef92f2ecf99a', 'signed_tx': '{"version":"0x0","cell_deps":[{"out_point":{"tx_hash":"0xbd262c87a84c08ea3bc141700cf55c1a285009de0e22c247a8d9597b4fc491e6","index":"0x2"},"dep_type":"code"},{"out_point":{"tx_hash":"0xd346695aa3293a84e9f985448668e9692892c959e7e83d6d8042e59c08b8cf5c","index":"0x0"},"dep_type":"code"},{"out_point":{"tx_hash":"0x03dd2a5594ed2d79196b396c83534e050ba0ad07fa5c1cd61a7094f9fb60a592","index":"0x0"},"dep_type":"code"},{"out_point":{"tx_hash":"0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37","index":"0x0"},"dep_type":"dep_group"}],"header_deps":[],"inputs":[{"previous_output":{"tx_hash":"0x52419c2bf7131908052e7fbed374ca82f3b9a23c091bcfcb487c4ac1c4c90717","index":"0x1"},"since":"0x0"}],"outputs":[{"capacity":"0x31eb3c002","type":{"code_hash":"0xb1837b5ad01a88558731953062d1f5cb547adf89ece01e8934a9f0aeed2d959f","args":"0xf90f9c38b0ea0815156bbc340c910d0a21ee57cf0000003300000002","hash_type":"type"},"lock":{"code_hash":"0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63","args":"0x009871fde1b88fe71d00c2e5c2f3d49ec009e629","hash_type":"type"}}],"outputs_data":["0x000000000000000000c000"],"witnesses":["0x5500000010000000550000005500000041000000c3e23abd5f3fb6350d138c7e25933c1a7e150d6aa75f8c118e55233101052af079ba116a515180252b73e2cbe02a2924118ed710c7e5c5e80c7e5b80258be45800"]}' } content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回: ```json { "tx_hash": "0x8ea665214de0a494f8df0125cbf492e96b2ec1df2cae8569bfe1d27759a3925b", "uuid": "396d73ca-f0b2-44ef-93a0-ea0d40e6fdc0" } ``` 返回的 `tx_hash` 为该交易在链上的交易哈希,可在浏览器对应查看; `uuid` 即为该交易在金色传说平台对应的交易 id,通过其他接口可查询交易状态。 ### 6.3 查询交易状态 相关文档:[GET /tx/token_transfers/{uuid}](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/unsigned_tx_generators/get_tx_token_transfers__uuid_) 查询参数: * `uuid` : 交易的 tx uuid 代码示例: ```python import requests key = '' secret = '' method = 'GET' tx_uuid = '75fddac0-7cda-4d9f-a770-56ee8ab6489c' endpoint = f'/tx/token_transfers/{tx_uuid}' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回: ```json { "from_address": "a2406443-4fbc-4058-ace2-ea2de19bace3", "to_address": "ckt1qsfy5cxd0x0pl09xvsvkmert8alsajm38qfnmjh2fzfu2804kq47v67zaqf9v82ypv7maevz5h70va4eevpggc8gjyw", "tx_type": "issue", "on_chain_timestamp": 1625492143, "tx_state": "committed", "issuer_name": "issuer_name", "issuer_avatar_url": "https://goldenlegend.oss-cn-hangzhou.aliyuncs.com/production/1620901052284.gif", "class_bg_image_url": "https://oss.jinse.cc/production/c8eb25b0-457c-4ac0-98b8-4308ac465893.png", "class_name": "Test", "class_total": "5", "class_uuid": "c2b0c5fb-6145-485d-995e-dd265b17471d", "n_token_id": 1 } ``` ## 7. Multi-Issuers 创作者默认对应一个 issuer,该 issuer 的信息即为用户在秘宝创作平台设置的公开信息。 Open API 允许一个帐号创建多个 issuer,不同 issuer 之间相互独立,即每个 issuer 有独立的 key-secret,可以单独设置创作者信息,以及设计和发行秘宝。 对拥有 multi-Issuers 权限的帐号,我们可以称该帐号对应的 API key 为 master_key,由其创建的 issuer 对应的 API key 为 issuer_key。对于原有接口,只有 master_key 可以正常请求使用;issuer_key 仅有权限进行设计秘宝、铸造和发行秘宝及其对应的查询请求。 注:如需使用该功能,需要单独申请 multi-Issuers 功能权限。用户可以发送邮件到 biz@nervina.io 进行申请,内容必须包含要申请开通 multi-Issuers 权限的帐号邮箱地址。 ### 7.1 Issuers 管理 master_key 可以对其创建的所有 issuers 进行管理,issuer_key 无此权限。 #### 7.1.1 获取全部 issuers 列表 master_key 可以查询该帐号下所有的 issuer 信息。 API 文档:[GET /issuers](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/issuers/get_issuers) 代码示例: ```python import requests key = '' secret = '' method = 'GET' endpoint = f'/issuers' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回示例: ```python { "issuers": [ { "avatar_url": "https://images.pexels.com/photos/3396664/peels-photo-3396664.jpeg", "name": "issuer_name", "description": "issuer_description", "website": "", "email": "", "weibo": "", "uuid": "f0d044c2-9ddb-4406-87b3-b281fbb27a76" } ] } ``` #### 7.1.2 创建新的 issuer master_key 可以创建新的 issuer。 API 文档:[POST /issuers](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/issuers/post_issuers) 必要参数: * `name` : 新创建的 issuer name,最多 30 个字符 可选参数: * `avatar_url` :创作者头像图片链接,最多 255 个字符,必须以 https:// 开头,以 png, jpg, jpeg, gif, webp, svg 这六种文件格式之一为后缀结尾 * `website` :创作者官网地址,最多 200 个字符 * `description` : 创作者简介,最多 200 个字符 * `email` :创作者邮箱地址,最多 100 个字符 * `weibo` :创作者社交媒体链接,最多 200 个字符 代码示例: ```python import requests key = '' secret = '' method = 'POST' endpoint = f'/issuers' content = ' { "avatar_url": "https://www.avatar.jpg", "name": "new issuer name", "website": "https://www.website.com", "description": "new issuer description", "email": "issuer@nervina.io", "weibo": "https://weibo.com/xxx" } ' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回示例: ```python { "avatar_url": "https://www.avatar.jpg", "name": "new issuer name", "description": "new issuer description", "website": "https://www.website.com", "email": "issuer@nervina.io", "weibo": "https://weibo.com/xxx", "uuid": "5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a", "api_token": { "access_key": "Nkx9gUnpaBwD7gCl", "secret": "7226c58ef55f5539fb0fe3afa24978c87581365763abc4f020dbf3a5a379b7de", "refresh_token": "53a74a524a769180bc4cf60e863d385ef29d93795cfb0ddbd86970f736baebda" } } ``` 返回信息中的 `uuid` 为该 issuer 对应的 uuid,`api_token.access_key` , `api_token.secret` 即为该 issuer 进行设计和发行秘宝时的 key-secret, `api_token.refresh_token` 为 issuer 更新 key-secret 时需要的参数(后边相关 API 会详细介绍)。 `api_token` 相关信息仅在创建时返回一次,请谨慎保管。 #### 7.1.3 更新 issuer 信息 master_key 可以更新已有 issuer 的相关信息。 API 文档:[PUT /issuers/{issuer_uuid}](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/issuers/put_issuers__uuid_) 必要参数: * `name` : issuer name,最多 30 个字符 可选参数: * `avatar_url` :创作者头像图片链接,最多 255 个字符,必须以 https:// 开头,以 png, jpg, jpeg, gif, webp, svg 这六种文件格式之一为后缀结尾 * `website` :创作者官网地址,最多 200 个字符 * `description` : 创作者简介,最多 200 个字符 * `email` :创作者邮箱地址,最多 100 个字符 * `weibo` :创作者社交媒体链接,最多 200 个字符 代码示例: ```python import requests key = '' secret = '' method = 'PUT' endpoint = f'/issuers/5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a' content = ' { "avatar_url": "https://www.avatar.jpg", "name": "update new issuer name", "website": "https://www.website.com", "description": "new issuer description", "email": "issuer@nervina.io", "weibo": "https://weibo.com/xxx" } ' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回示例: ```json { "avatar_url": "https://www.avatar.jpg", "name": "update new issuer name", "description": "new issuer description", "website": "https://www.website.com", "email": "issuer@nervina.io", "weibo": "https://weibo.com/xxx", "uuid": "5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a" } ``` #### 7.1.4 获取指定 issuer 信息 master_key 可以获取指定 issuer 信息。 API 文档:[GET /issuers/{issuer_uuid}](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/issuers/get_issuers__uuid_) 代码实例: ```python import requests key = '' secret = '' method = 'GET' endpoint = f'/issuers/5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回实例: ```python { "issuer": { "avatar_url": "https://www.avatar.jpg", "name": "new issuer name", "description": "new issuer description", "website": "https://www.website.com", "email": "issuer@nervina.io", "weibo": "https://weibo.com/xxx", "uuid": "5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a" } } ``` #### 7.1.5 更新指定 issuer 的 key/secret/refresh_token master 可以更新所有 issuer 的 key-secret;issuer 可以更新自己的 key-secret。 API 文档:[POST /issuers/{issuer_uuid}/api_tokens](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/api_key/post_issuers__issuer_uuid__api_tokens) 参数: * `refresh_token` : 使用 master 请求 refresh token 时,该参数可以忽略;使用 issuer 请求时,该参数为必须参数 代码实例: ```python import requests key = '' secret = '' method = 'POST' endpoint = f'/issuers/5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a/refresh_token' content = ' { "refresh_token": "53a74a524a769180bc4cf60e863d385ef29d93795cfb0ddbd86970f736baebda" } ' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=content) ``` 返回实例: ```json { "api_token": { "access_key": "SgxjJTJZ3MDlFwny", "secret": "cc31c0e8fd263b116dfb9cbe3787458cd42cf3e054cdbe522b284c0b77a4d9c9", "refresh_token": "ba5dbcf681f006243e79aa7abaf820c003335fad9b90c4b1a76039a9b3de70d4" } } ``` 返回中的 `access_key` , `secret` 和 `refresh_token` 为新的 key-secret ,仅返回一次,请妥善保管。 ### 7.2 设计和发行秘宝相关接口 issuer 可以独立设计、更新秘宝。 #### 7.2.1 设计秘宝 相关文档:[POST /issuers/{issuer_uuid}/token_classes](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/token_class/post_issuers__uuid__token_classes) 设计秘宝必须包含以下字段: * name: 秘宝的名称,不超过 30 个字符(一旦创建,不可修改) * description: 秘宝的简介,不超过 200 个字符(一旦创建,不可修改) * total: 非负整数,为 0 时,表示秘宝不限量;大于 0 时,表示秘宝限量的数量 * renderer: 秘宝的媒体信息,必须为以 `https://` 开头、媒体信息有效的 url,且不超过 255 字符(秘宝设计完成后,30 天内可修改一次 renderer) * renderer 为图片:支持格式为 png, jpg, jpeg, gif, webp 和 svg 6 种格式,必须以 `https://` 开头,且结尾为 `png | jpg | jpeg | gif | webp | svg`。 * renderer 为视频:支持格式为 mp4 和 webm 2 种格式,必须以 `https://` 开头,且结尾为 `mp4 | webm`。当 renderer 为音视频时,同时需要传入参数 `cover_image_url` 用于设置 NFT 封面,校验规则与图片格式的 renderer 一致。 设计的秘宝 token class 会上链,上链操作由平台完成。 代码示例: ```python import json import requests key = '' secret = '' method = 'POST' endpoint = '/token_classes/5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a/token_classes' content = { 'name': 'New Token Class Example', 'description': 'Create Token Class by Open API', 'total': '0', 'renderer': 'https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg' } content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=json.dumps(content)) ``` 返回: ```json { 'name': 'New Token Class Example', 'description': 'Create Token Class by Open API', 'issued': '0', 'renderer': 'https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg', 'uuid': 'e6469650-6ac5-4477-a64b-2a97f4168356', ' total': '0', 'tags': [] } ``` 其中, `uuid` 为平台生成的唯一标识 token class id ```python # 如果创建音频或视频格式的秘宝,则 content 如下所示: content = { 'name': 'New Token Class Example', 'description': 'Create Token Class by Open API', 'total': '0', 'renderer': 'https://oss.jinse.cc/production/583b109e-1fc3-42bd-937f-f4935ae80167.mp4', 'cover_image_url': 'https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg' } ``` * 在设计秘宝时提供可选参数`configure`,用于设置 NFT class cell 的 `configure`,金色传说平台默认设置为 `11000000`, 相关信息可参考:[https://talk.nervos.org/t/rfc-multi-purpose-nft-draft-spec/5434](https://talk.nervos.org/t/rfc-multi-purpose-nft-draft-spec/5434)。 `configure` 格式应为类似 `11000000` 的 8 位二进制字符串,否则会返回 `configure field format error` 请求示例: ```python # 可选参数 configure 设置 NFT class cell 的 configure content = { 'name': 'New Token Class with Configure Example', 'description': 'Create Token Class by Open API', 'total': '0', 'configure': '00000000', 'renderer': 'https://oss.jinse.cc/production/7ea62f75-bec0-4cdc-b81a-d59d7f40ace1.jpg' } ``` #### 7.2.2 查询指定 issuer 创作的所有 token classes 相关文档:[GET /issuers/{issuer_uuid}/token_classes](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/token_class/get_issuers__uuid__token_classes) 代码示例: ```python import requests key = '' secret = '' method = 'GET' endpoint = '/issuers/5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a/token_classes' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers) ``` 返回: ```json { "token_classes": [ { "name": "issuerA-ikey-c001", "description": "first nft", "issued": "0", "renderer": "https://oss.jinse.cc/production/db8b6440-6e26-4a80-8dc7-cfdd022e0b6d.jpg", "cover_image_url": "", "uuid": "43edf4ba-9c77-466e-807f-d35cb4465c6f", "total": "1000", "is_banned": false, "verified_info": { "is_verified": null, "verified_title": "", "verified_source": "" } } ], "meta": { "total_count": 1, "max_page": 1, "current_page": 1 } } ``` #### 7.2.3 修改指定 issuer 设计的秘宝(token class) 相关文档:[PUT /issuers/{issuer_uuid}/token_classes/{token_class_uuid}](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/token_class/put_issuers__issuer_uuid__token_classes__token_class_uuid_) 秘宝设计完成后,仅 renderer 部分可以修改 * renderer: 秘宝的媒体信息,必须为以 `https://` 开头、媒体信息有效的 url,且不超过 255 字符(秘宝设计完成后,30 天内可修改一次 renderer) * renderer 为图片:支持格式为 png, jpg, jpeg, gif, webp 和 svg 6 种格式,必须以 `https://` 开头,且结尾为 `png | jpg | jpeg | gif | webp | svg`。 * renderer 为音频或视频:支持格式为 mp3, wav, mp4 和 webm 4 种格式,必须以 `https://` 开头,且结尾为 `mp3 | wav | mp4 | webm`。当 renderer 为音视频时,同时需要传入参数 `cover_image_url` 用于设置 NFT 封面,校验规则与图片格式的 renderer 一致。 修改秘宝 token class 会上链,上链操作由平台完成。 代码示例: ```python import json import requests key = '' secret = '' method = 'PUT' endpoint = 'issuers/5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a/token_classes/43edf4ba-9c77-466e-807f-d35cb4465c6f' content = { 'renderer': 'https://game-4m-assets.oss-cn-shanghai.aliyuncs.com/AdobeStock_95598553.jpeg' } content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=json.dumps(content)) ``` 返回: ```json { "name": "issuerA-ikey-c001", "description": "first nft", "issued": "0", "renderer": "https://game-4m-assets.oss-cn-shanghai.aliyuncs.com/AdobeStock_95598553.jpeg", "cover_image_url": null, "uuid": "43edf4ba-9c77-466e-807f-d35cb4465c6f", "total": "1000" } ``` #### 7.2.4 铸造并发行秘宝 相关文档:[POST /issuers/{issuer_uuid}/token_classes/{token_class_uuid}/tokens](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/tokens/post_issuers__issuer_uuid__token_classes__token_class_uuid__tokens) 查询字段: * `token_class_uuid` : 设计秘宝时返回的 token class uuid 请求字段: * `addresses` : 一个由 ckb 地址组成的列表 代码示例: ```python import json import requests key = '' secret = '' method = 'POST' address = 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqycw877rwy0uuwspsh9cteaf8kqp8nzjl0dxfp' endpoint = '5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a/token_classes/43edf4ba-9c77-466e-807f-d35cb4465c6f/tokens' content = { 'addresses': [ address ] } content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=json.dumps(content)) ``` 返回: ```json [ { "uuid": "27b03ca5-abbe-4c82-9416-c37c2462d630", "oid": "83d1ab0dd73c408ecf4d4ae7dd88172b", "version": 0, "characteristic": "0000000000000000", "issuer_id": 1760, "token_class_id": 2348, "state": "pending", "configure": 192, "n_issuer_id": "0xb69cb79760a0a1a187af8c3b94643776237d5899", "n_state": 0, "created_at": "2021-10-25T15:36:13.674+08:00", "updated_at": "2021-10-25T15:36:13.674+08:00" } ] ``` 其中, `uuid` 是具体的 NFT token id #### 7.2.5 获取指定 issuer 创建的 token class 对应的全部 tokens 相关文档:[GET /issuers/{issuer_uuid}/token_classes/{token_class_uuid}/tokens](https://app.swaggerhub.com/apis/ShiningRay/NftSaasOpenAPI/0.0.1#/tokens/get_issuers__issuer_uuid__token_classes__token_class_uuid__tokens) 查询字段: * `token_class_uuid` : 设计秘宝时返回的 token class uuid 代码示例: ```python import json import requests key = '' secret = '' method = 'GET' address = 'ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqycw877rwy0uuwspsh9cteaf8kqp8nzjl0dxfp' endpoint = '5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a/token_classes/43edf4ba-9c77-466e-807f-d35cb4465c6f/tokens' content = '' content_type = 'application/json' url = f'https://goldenlegend.staging.nervina.cn/api/v1{endpoint}' signature, gmt = get_signature_and_gmt(secret, method, endpoint, content, content_type) headers = { 'Content-Type': content_type, 'Date': gmt, 'Authorization': f'NFT {key}:{signature}' } requests.request(method, url, headers=headers, data=json.dumps(content)) ``` 返回: ```json { "tokens": [ { "name": "issuerA-ikey-c001", "description": "first nft", "issued": "1", "total": "1000", "bg_image_url": "https://game-4m-assets.oss-cn-shanghai.aliyuncs.com/AdobeStock_95598553.jpeg", "issuer_info": { "name": "update new name", "avatar_url": "", "uuid": "5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a", "issuer_follows": 0, "issuer_followed": false }, "tx_state": "submitting", "class_uuid": "43edf4ba-9c77-466e-807f-d35cb4465c6f", "from_address": "5f4ebad3-38a1-44d2-8e9d-0212a09f9c5a", "to_address": "ckt1q3vvtay34wndv9nckl8hah6fzzcltcqwcrx79apwp2a5lkd07fdxxqycw877rwy0uuwspsh9cteaf8kqp8nzjl0dxfp", "is_class_banned": false, "is_issuer_banned": false, "n_token_id": 0, "verified_info": { "is_verified": null, "verified_title": "", "verified_source": "" }, "class_likes": 0, "class_liked": false, "uuid": "27b03ca5-abbe-4c82-9416-c37c2462d630", "created_at": "2021-10-25T15:36:13.674+08:00", "distribution_method": "send", "tx_hash": "0x89ed628e5fbe2e4585d486a61b531dc7cd479cdeb04b5ed187851af9290e9f82", "tx_uuid": "c4a9fe74-3f14-48d7-a154-ff013062626d", "renderer_type": "image", "renderer": "https://game-4m-assets.oss-cn-shanghai.aliyuncs.com/AdobeStock_95598553.jpeg", "class_card_back_content_exist": false, "class_card_back_content": null, "id": 21711, "is_redeemed": false } ], "meta": { "total_count": 1, "max_page": 1, "current_page": 1 } } ```