计算 API 签名

以下步骤介绍如何计算 API 签名。
  1. 首先,基于您的请求拼接出一个字符串,如下所示: str_to_sign = {METHOD}|{PATH}|{TIMESTAMP}|{PARAMS}|{BODY}
    字段描述示例
    METHODHTTP 方法。GET
    PATHAPI 端点。/v2/transactions/transfer
    TIMESTAMP以毫秒为单位的当前 Unix 时间戳。该值必须与请求头中的随机数相同。1718587017026
    PARAMS查询参数。chain_id=ETH&limit=10
    BODY原始请求包体的字符串格式。{"wallet_type":"Custodial"}
    PARAMS和BODY字段是可选的,如果没有对应参数的话,留空字符串
  2. 使用 hashlib 库对字符串进行两次 SHA-256 哈希,如下所示:
      import hashlib
      content_hash = hashlib.sha256(hashlib.sha256(str_to_sign.encode()).digest()).digest()
    
  3. 使用 API Secret 对字符串进行签名,如下所示:
      from nacl.signing import SigningKey
    
      # 创建 Ed25519 签名密钥。将 `api_secret` 替换为您的 API Secret
      sk = SigningKey(bytes.fromhex(api_secret))
      # 签署哈希后的消息
      signature = sk.sign(content_hash).signature.hex()
    
现在您已经计算出了一个 API 签名。

签名代码示例

以下是各语言的完整签名 Helper 实现。您可以直接复制到项目中使用。 签名流程:
  1. 拼接待签名字符串: {METHOD}|{PATH}|{TIMESTAMP}|{PARAMS}|{BODY}
  2. 双重 SHA256 哈希: sha256(sha256(str_to_sign))
  3. 使用 Ed25519 私钥签名
  4. 将签名结果放入请求头: BIZ-API-KEY, Biz-Api-Nonce, Biz-Api-Signature
import hashlib
import time
import json
import requests
from nacl.signing import SigningKey


class NBTSigner:
    """NBT API 签名工具类"""

    def __init__(self, api_key: str, api_secret: str, base_url: str = "https://apidev.nusd.me"):
        self.api_key = api_key
        self.api_secret = api_secret
        self.base_url = base_url

    def sign(self, method: str, path: str, params: str = "", body: str = "") -> dict:
        """
        生成签名并返回请求头

        Args:
            method: HTTP 方法 (GET/POST)
            path: API 路径 (如 /nps/balance)
            params: 查询参数字符串 (如 wallet_id=xxx)
            body: 请求体 JSON 字符串
        Returns:
            包含签名信息的请求头字典
        """
        timestamp = str(int(time.time() * 1000))
        str_to_sign = f"{method}|{path}|{timestamp}|{params}|{body}"

        # 双重 SHA256
        content_hash = hashlib.sha256(
            hashlib.sha256(str_to_sign.encode()).digest()
        ).digest()

        # Ed25519 签名
        signing_key = SigningKey(bytes.fromhex(self.api_secret))
        signature = signing_key.sign(content_hash).signature.hex()

        return {
            "BIZ-API-KEY": self.api_key,
            "Biz-Api-Nonce": timestamp,
            "Biz-Api-Signature": signature,
            "Content-Type": "application/json",
        }

    def get(self, path: str, params: dict = None) -> dict:
        """发送签名 GET 请求"""
        from urllib.parse import urlencode
        query_string = urlencode(params) if params else ""
        headers = self.sign("GET", path, params=query_string)
        url = f"{self.base_url}{path}"
        if query_string:
            url += f"?{query_string}"
        return requests.get(url, headers=headers).json()

    def post(self, path: str, data: dict = None) -> dict:
        """发送签名 POST 请求"""
        body = json.dumps(data, ensure_ascii=False) if data else ""
        headers = self.sign("POST", path, body=body)
        return requests.post(
            f"{self.base_url}{path}", headers=headers, data=body
        ).json()


# 使用示例
if __name__ == "__main__":
    signer = NBTSigner(
        api_key="your_api_key",
        api_secret="your_api_secret_hex"
    )

    # GET 请求示例
    result = signer.get("/nps/balance", {"wallet_id": "your_wallet_id"})
    print("Balance:", result)

    # POST 请求示例
    result = signer.post("/nps/address", {
        "wallet_id": "your_wallet_id",
        "chain_id": "BASE_ETH",
        "user_token": "user_123"
    })
    print("Address:", result)