KDA 钱包开发详细教程

一. KDA 简介

“KDA链”指的是Kadena(KDA)区块链平台。Kadena是一个基于区块链技术的高性能智能合约平台,旨在提供安全、高效和可扩展的区块链解决方案。Kadena项目由Stuart Popejoy和Will Martino于2016年创立,他们曾在摩根大通工作,并参与了JPMorgan的区块链项目Juno的开发。

1.主要特性和功能

  • Pact智能合约语言: Kadena使用一种名为Pact的智能合约语言,它强调安全性和易用性。Pact支持正式验证(Formal Verification),这意味着开发者可以数学上证明合约的正确性,从而减少漏洞和错误。
  • Chainweb并行共识机制: Kadena采用一种独特的共识机制称为Chainweb,这是一种并行化的工作量证明(PoW)共识协议。通过将多个独立的链并行运行并定期交叉链接,Chainweb能够实现高吞吐量和可扩展性。
  • 可扩展性和高性能: Kadena设计旨在解决传统区块链在扩展性方面的瓶颈。通过Chainweb架构,它能够在保持去中心化和安全性的同时,实现大规模扩展和高交易处理速度。
  • 混合区块链架构: Kadena提供公共链和私有链的解决方案,允许企业在私有链上运行内部应用,同时利用公共链进行更广泛的交互和验证。
  • 可编程性和互操作性: Pact智能合约支持模块化编程和互操作性,开发者可以方便地创建和连接不同的区块链应用。

2.核心组件

  • 公共链(Public Chain): Kadena的公共链是一个去中心化的区块链网络,适用于广泛的公共和商业应用。
  • 私有链(Private Chain): Kadena的私有链解决方案为企业提供了一个安全、可控的区块链环境,适合处理敏感数据和内部业务流程。
  • KDA代币: KDA 是 Kadena区块链的原生加密货币,用于支付交易费用、激励矿工以及参与平台治理。

3.Kadena(KDA)的 Chainweb 共识机制

Kadena(KDA)采用一种独特的共识算法称为 Chainweb,这是一个并行化的工作量证明(Proof of Work, PoW)共识协议。Chainweb设计的目标是提高区块链的吞吐量和可扩展性,同时保持去中心化和安全性。

  • 并行链(Parallel Chains): Chainweb 架构由多条并行运行的链组成,每条链都有自己的区块和哈希链。
  • 交叉链接(Cross-Chain Links): 这些并行链定期通过交叉链接进行连接和同步。每个区块不仅包含自己的前区块的哈希,还包含来自其他并行链的区块哈希。这些交叉链接提供了一种机制,使得整个网络能够协调一致地进行共识。
  • 工作量证明(PoW): 和传统的 PoW 一样,矿工通过解决复杂的数学问题来挖掘区块,并获得奖励。Chainweb在此基础上,通过多个链并行工作,使得网络整体上能够处理更多的交易。

3.1.运行机制

  • 独立挖矿: 每条链上的区块独立挖矿。矿工选择一条链并尝试挖掘下一个区块,这个过程与传统的PoW相似。
  • 区块验证: 每个新生成的区块除了包含自己的交易记录和前区块的哈希,还包括与其交叉链接的其他链的最新区块的哈希。这样,验证一个区块不仅需要验证其自身的PoW,还需要验证其交叉链接的区块。
  • 全网共识: 通过交叉链接,Chainweb 架构保证了所有并行链之间的共识。即使某一条链出现分叉,其他链的状态也能通过交叉链接进行验证和同步,从而维持整个网络的一致性。

3.2.优势

  • 高吞吐量: 由于多个链并行运行,Chainweb 架构能够显著提高交易处理速度,增强区块链的吞吐量。
  • 高可扩展性: 新链可以根据需要添加到网络中,从而进一步提高系统的处理能力。这种设计使得 Kadena 能够扩展以满足更大的交易需求。
  • 安全性: 通过交叉链接,Chainweb 提供了额外的安全层。攻击者需要同时攻击多个并行链,增加了攻击的难度和成本。
  • 去中心化: Chainweb 保持了 PoW 机制的去中心化特性,避免了集中化风险。

3.3.比较与传统 PoW

  • 传统 PoW: 比特币等传统 PoW 网络依赖单条链进行共识,处理能力受限于单链的区块生成速度和大小。
  • Chainweb: 通过并行链和交叉链接,Chainweb 突破了单链的限制,能够并行处理大量交易,同时保持去中心化和安全性。

二. KDA 离线地址生成

export function createKdaAddress (seedHex: string, addressIndex: string) {
    const { key } = derivePath("m/44'/626'/0'/0'" + addressIndex + "'", seedHex);
    const publicKey = getPublicKey(new Uint8Array(key), false).toString('hex');
    const hdWallet = {
        privateKey: key.toString('hex') + publicKey,
        publicKey,
        address: "k:" + publicKey
    };
    return JSON.stringify(hdWallet);
}
export function pubKeyToAddress(params: any): string {
    const { pubKey } = params;
    return "k:" + pubKey
}

三. KDA 离线签名

export async function signTransaction(params) {
    const {
        txObj: { toPub, from, to, amount, chainId, targetChainId, gasPrice, gasLimit, decimal, spv, pactId },
        network,
        privs
    } = params;
    const decimals = new BigNumber(10).pow(Number(decimal));
    const amountBig = new BigNumber(amount).times(decimals);
    if (amountBig.toString().indexOf(".") !== -1 || amountBig.comparedTo(new BigNumber(MAX_AMOUNT_SAFE)) > 0 || amountBig.comparedTo(new BigNumber(0)) <= 0) {
        throw new Error(`参数错误: 转账数额无效(${amountBig.toString()})`);
    }
    if (chains.indexOf(chainId.toString()) === -1) {
        throw new Error(`chainId 错误, 请选择 0 到 19`);
    }
    if (chains.indexOf(targetChainId.toString()) === -1) {
        throw new Error(`targetChainId 错误, 请选择 0 到 19`);
    }
    if (!privs || privs.length === 0) {
        throw new Error('must have private key');
    }
    let keypair = nacl.sign.keyPair.fromSecretKey(new Uint8Array(Buffer.from(privs[0].key, 'hex')));
    const sendPublickey = Buffer.from(keypair.publicKey).toString("hex");
    const privateKey = privs?.[0]?.key.length === 64 ? privs?.[0]?.key : privs?.[0]?.key.slice(0, 64);
    const networkId = network === "main_net" ? 'mainnet01' : 'testnet04';
    if (chainId === targetChainId) {
        let method = 'transferOffline';
        if (toPub && toPub.length === 64) {
            method = 'transferCreateOffline';
        }
        const params = {
            SignObj: {
                sender: from,
                senderPubKey: sendPublickey,
                senderSecretKey: privateKey,
                receiver: to,
                receiverPubKey: toPub,
                amount: amount,
                chainId: chainId.toString(),
                targetChainId: targetChainId.toString(),
                networkId: networkId,
                gasPrice: Number(gasPrice),
                gasLimit: gasLimit,
                spv: "",
                pactId: "",
            },
            method: method,
        }
        return JSON.stringify(CreateOfflineSignTx(params))
    } else {
        const params = {
            SignObj: {
                sender: from,
                senderPubKey: sendPublickey,
                senderSecretKey: privateKey,
                receiver: to,
                receiverPubKey: toPub,
                amount: amount,
                chainId: chainId.toString(),
                targetChainId: targetChainId.toString(),
                networkId: networkId,
                gasPrice: Number(gasPrice),
                gasLimit: gasLimit,
                spv: spv,
                pactId: pactId,
            },
            method: 'crossTransferOffline',
        }
        return JSON.stringify(CreateOfflineSignTx(params))
    }
}

注意:若代码不完整,付费学员可以联系 The Web3 社区寻求完整代码

四. KDA 钱包对接相关接口

1.获取账户余额

  • 接口作用:获取账户余额
  • 接口参数构造 python 代码示范
# -*- coding: utf-8 -*-
import json
from hashlib import blake2b
from base64 import b64encode
from datetime import datetime
import calendar

def hashbin(s):
    if not isinstance(s, bytes):
        s = s.encode('utf8')
    i = blake2b(digest_size=32)
    i.update(s)
    return i.digest()

def hash_blake(s):
    return b64encode(hashbin(s))


def str_to_timestamp(s):
    s = s[:19]
    dt = datetime.strptime(s, '%Y-%m-%d %H:%M:%S').timetuple()
    return int(calendar.timegm(dt))


def assemble_balance_payload(account):
    payload = {
        "exec": {
            "data": None,
            "code": "(coin.get-balance \"{}\")".format(account)
        }
    }
    signers = []

    nonce = datetime.utcnow()
    targe_nonce = str(nonce.strftime('%Y-%m-%d %H:%M:%S.%f UTC'))
    creation_time = str_to_timestamp(targe_nonce)
    meta = {
        "creationTime": creation_time,
        "ttl": 1800,
        "gasLimit": 1000,
        "chainId": "",
        "gasPrice": 1.0e-2,
        "sender": ""
    }

    cmd = {
        "networkId": "mainnet01",
        "payload": payload,
        "signers": signers,
        "meta": meta,
        "nonce": targe_nonce
    }
    cmd_str = json.dumps(cmd, separators=(',', ':'))
    hash_str = hash_blake(cmd_str)
    tx_hash = hash_str.decode().replace('+', '-').replace('/', '_').replace('=', '')

    raw_transaction = {
        "hash": tx_hash,
    }
    raw_transaction["sigs"] = []
    raw_transaction["cmd"] = json.dumps(cmd, separators=(',', ':'))
    return json.dumps(raw_transaction, separators=(',', ':'))


print(assemble_balance_payload("k:259e184f5c7cecf261063dd298e250b2303cf896a3d6705eedd35cc8b97cee9b"))
  • 请求示范
curl --location 'https://api.chainweb.com/chainweb/0.0/mainnet01/chain/2/pact/api/v1/local' \
--header 'Content-Type: application/json' \
--data '{"hash":"2I4-ePSHkVU6L9YZqK6nbN-ez02gCkdtXTwzZllwpAM","sigs":[],"cmd":"{\"networkId\":\"mainnet01\",\"payload\":{\"exec\":{\"data\":null,\"code\":\"(coin.get-balance \\\"k:259e184f5c7cecf261063dd298e250b2303cf896a3d6705eedd35cc8b97cee9b\\\")\"}},\"signers\":[],\"meta\":{\"creationTime\":1717726460,\"ttl\":1800,\"gasLimit\":1000,\"chainId\":\"\",\"gasPrice\":0.01,\"sender\":\"\"},\"nonce\":\"2024-06-07 02:14:20.222256 UTC\"}"}
'
  • 返回值
{
    "gas": 20,
    "result": {
        "status": "success",
        "data": 1.459869
    },
    "reqKey": "2I4-ePSHkVU6L9YZqK6nbN-ez02gCkdtXTwzZllwpAM",
    "logs": "wsATyGqckuIvlm89hhd2j4t6RMkCrcwJe_oeCYr7Th8",
    "metaData": {
        "publicMeta": {
            "creationTime": 1717726460,
            "ttl": 1800,
            "gasLimit": 1000,
            "chainId": "",
            "gasPrice": 1.0e-2,
            "sender": ""
        },
        "blockTime": 1717103631588214,
        "prevBlockHash": "bE0BbAubTPj9bMbODafRRlvYvuo-r-0WAVzlakDMHbM",
        "blockHeight": 4821734
    },
    "continuation": null,
    "txId": null
}
  • data 为账户余额

2.获取 SPV

  • 接口作用:获取跨链交易需要使用的 SPV
  • 接口参数: requestKey:源链交易 Hash targetChainId 目标链 ID
  • 请求示范
curl --location 'https://api.chainweb.com/chainweb/0.0/mainnet01/chain/2/pact/spv' \
--header 'Content-Type: application/json' \
--data '{
  "requestKey": "POfJqi3iIEoX8vo8CWvAxtJUQvMA3OfyRWYTDSFuU4Q",
  "targetChainId": "1"
}'
  • 返回值
"eyJjaGFpbiI6MSwib2JqZWN0IjoiQUFBQUVBQUFBQUFBQUFBQkFMLVBJNHJHaEp3OXF2MkN2bUdhX0stcmkzU2tmQ3NCLXlfbW5QeGl0aEdlQU9RaExvVl84MkRiUEphV3JNLU5YVGJwSXgtWVBnLXM5WlpzTlk3enp2X1dBVjhtRkcyc1RyVVN5bGVzVmlqWUxVVmFWSE1wbE1zRlhPRHRyTGNYOVdxbkFjSDBXOTREU1RyUVd0MEZ2LXh5ZDVOWWRTUHQ4M21vNjdvOGdHaTVoQXBpQUJubGVSMFFtcG5BSzl0YkJERHl3emNhVE9MUmNMWmE2N25TNUJlbUt4THdBU1ZmOU55ZFlDVGZncXNhZUYzdTliVk1uMDN3ajljbGtsOFF0NU1PY0ZueUFBVXQtTXNGOWdpNDI5TmFVVDBzMm9wWDFWdGlUQV8yX0dfLUI1VzJDQUpkQUFmclBwUm5FbDJiSkh6VWc5SFJiS3RyNnZzOUkxYnpkWHdyWGc2elJjTGJBYkhMNkhEVEJOcDREUVJsWEZ4UGwwb3A3TlBFYVJKc1Z5NXdNSkV3Sm5TY0FCOTV5cWFNeDZnXzNQNlRGZW9zYlZEZmtQU2F1elptVGZMc2pfWVlsU3lsQU5KMFBjVmFvaUxZd3ZwbG1md1hHdWxkZUpHMUJiZjV5Z0xIUExLRFF1bGpBT0ItdGhMVHM1TlpvckVJMGMwR3ZXQ01uaVVrMmJWbEI5dmJBTXlBLWlLOEFMdmdSZlA2ejJaVVRpczVBd2hUX1ZQYnUzNGRLckJEVVNYX0tueWg2ZzRuQURtZnQwMGtBUUpMT3luSzZEZWJoLXNjck9NaFYyX25qN3cyMU5SWnRYVXJBTVJoNlBDdXd2TmQyVzZFY2prcHZMMkdzMjFMQlBkM1NNMEt1bE1EcDRUVUFMbnRUcnNHNjJDaWI3X1RteG1WbDNPczRxMnBjeER3UzA3YVZIOWtwandCIiwic3ViamVjdCI6eyJpbnB1dCI6IkFCUjdJbWRoY3lJNk5qQXdMQ0p5WlhOMWJIUWlPbnNpYzNSaGRIVnpJam9pWm1GcGJIVnlaU0lzSW1WeWNtOXlJanA3SW1OaGJHeFRkR0ZqYXlJNlcxMHNJblI1Y0dVaU9pSkhZWE5GY25KdmNpSXNJbTFsYzNOaFoyVWlPaUpIWVhNZ2JHbHRhWFFnS0RZd01Da2daWGhqWldWa1pXUTZJRFl4T0NJc0ltbHVabThpT2lJaWZYMHNJbkpsY1V0bGVTSTZJbEJQWmtweGFUTnBTVVZ2V0RoMmJ6aERWM1pCZUhSS1ZWRjJUVUV6VDJaNVVsZFpWRVJUUm5WVk5GRWlMQ0pzYjJkeklqb2lVMng0TVZsSWVIaDBiMWxTVVUxUVFVUTNPRFp0UTBNMmVFSnVkM05FTjNsUU0xY3RZMHhsV0V4cU1DSXNJbVYyWlc1MGN5STZXM3NpY0dGeVlXMXpJanBiSW1zNk1qVTVaVEU0TkdZMVl6ZGpaV05tTWpZeE1EWXpaR1F5T1RobE1qVXdZakl6TUROalpqZzVObUV6WkRZM01EVmxaV1JrTXpWall6aGlPVGRqWldVNVlpSXNJams1WTJJM01EQTRaRGRrTnpCak9UUm1NVE00WTJNek5qWmhPREkxWmpCa09XTTRNMkU0WVRKbU5HSmhPREpqT0Raak5qWTJaVEJoWWpabVpXTm1NMkVpTERZdU1HVXRNbDBzSW01aGJXVWlPaUpVVWtGT1UwWkZVaUlzSW0xdlpIVnNaU0k2ZXlKdVlXMWxjM0JoWTJVaU9tNTFiR3dzSW01aGJXVWlPaUpqYjJsdUluMHNJbTF2WkhWc1pVaGhjMmdpT2lKcmJFWnJja3htY0hsTVZ5MU5NM2hxVmxCVFpIRllSVTFuZUZCUVNtbGlVblJmUkRaeGFVSjNjelp6SW4xZExDSnRaWFJoUkdGMFlTSTZiblZzYkN3aVkyOXVkR2x1ZFdGMGFXOXVJanB1ZFd4c0xDSjBlRWxrSWpwdWRXeHNmUSJ9LCJhbGdvcml0aG0iOiJTSEE1MTJ0XzI1NiJ9"
  • 返回值即为 spv, 用户签名跨链交易发送到目标链上

3.获取各条链的最新块高

  • 接口作用:获取 10 条链的块高
  • 请求参数:无
  • 请求示范
curl --location 'https://api.chainweb.com/chainweb/0.0/mainnet01/cut'
  • 返回值
{
    "hashes": {
        "17": {
            "height": 4841223,
            "hash": "8pQ-3FO29fK-rY8ba6L7xRzOPXSYwsW0Aeva_vsarM0"
        },
        "16": {
            "height": 4841224,
            "hash": "CWk5WWEkO8UTKjRzSuGUd9aRDYLTrGAfQnQ_MpJKrdA"
        },
        "19": {
            "height": 4841224,
            "hash": "rX8tLNp5lfhjBgNw3-HJGBu5NNtP8wSwZm3DLSlKE1Y"
        },
        "18": {
            "height": 4841224,
            "hash": "oLbJGcbKjM9HTVrKRpXwnQ8ievPyQjjYVe6JHQMf9U8"
        },
        "5": {
            "height": 4841223,
            "hash": "jThvZbClD2ye686iEjFY2oiFokbkbKOS8wRnE0LAyDg"
        },
        "4": {
            "height": 4841223,
            "hash": "ENJFCjL_1Yp0Iu9J3nd-qQpn9bRRei_jVIQ6hQewUqQ"
        },
        "7": {
            "height": 4841223,
            "hash": "B07c5QthGdvoLqE9Y59YmVrzslwViDZwF-jGz_5O2Ws"
        },
        "6": {
            "height": 4841224,
            "hash": "2uEikksdlPehPMfSXnfWmGMvIVnr3VvAHNt2DMqMFCc"
        },
        "1": {
            "height": 4841224,
            "hash": "g9gP4wqPPylvOCKEsdaOwnj0asA9VFkQF-zFuT2dZBY"
        },
        "0": {
            "height": 4841222,
            "hash": "RmsCuFPPPPs8NQ7u7LMR4Q_LUVklz35JILGp00PMeNg"
        },
        "3": {
            "height": 4841224,
            "hash": "G1YGper1Nwuv7orbZC2WaLopaPt1g3Qzn3kgeXE--P8"
        },
        "2": {
            "height": 4841223,
            "hash": "nsXjqewBt1fyMUovlqZ0e7PIRu8eoqvR08QDsynJf_g"
        },
        "13": {
            "height": 4841223,
            "hash": "3kVa-MelRan6U6ykEytgTpMHDPQz3zSPa5N1HepEJxQ"
        },
        "12": {
            "height": 4841224,
            "hash": "q-h33GYgEUVpFLAmBvf3eMF_iu4pmeEjHkoYMI27Nus"
        },
        "15": {
            "height": 4841223,
            "hash": "x3GOSps1hUopLPazZsrAHamvvkyw_aHJEBXGoxo7Dnk"
        },
        "14": {
            "height": 4841222,
            "hash": "Nq34vAcyzUh528914_g64gWx5crV8Py-NhResg9S-2U"
        },
        "9": {
            "height": 4841223,
            "hash": "uNZgb8dTzYrDZisAkLkoe2M8aD40E6pnhh8KbekZCOI"
        },
        "8": {
            "height": 4841223,
            "hash": "QJyADFef-9mk9wHCprRVDaw1JAaD70-BIWDw0gUbozU"
        },
        "11": {
            "height": 4841224,
            "hash": "e_utLD8zc1kGaLWDcVOIqbD_HIszEGzTa4i89jNarB4"
        },
        "10": {
            "height": 4841223,
            "hash": "DWjkLhp2Ql3rDuHA0r5yd6ji7jHp1ZZAMzErPA-ve7g"
        }
    },
    "origin": null,
    "weight": "5rRNkAj_c03zfRcAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "height": 96824466,
    "instance": "mainnet01",
    "id": "X7QnBOavHldNR3GPlJgQ3DZq5C_PyIjIhlnX212jPPE"
}
  • height:最新块高
  • hash:区块 Hash
  • height 和 hash 前面的数字是链的标号

4.根据块高获取块信息

  • 接口作用:根据块高获取块信息
  • 参数: minheight:起始区块 maxheight:结束区块
  • 请求示范
curl --location 'https://api.chainweb.com/chainweb/0.0/mainnet01/chain/15/header?minheight=4841250&maxheight=4841250'
  • 返回值
{
    "limit": 2,
    "items": [
        "AAAAAAAAAADN4aO8OhoGAOBzcvf5wbn8rH8ZCxqeb4TKlOzccJXCnFMKe5aZgSnjAwAAAAAAGw4ykoZXsXlRGOkKc4fMD_0nB7K_J-wNfkI00WJyQBwOAAAAQ44Ls8M9WyUK1eRNhKcrbCaT1mDvhiMO-vtn8TLz0OAQAAAAz4vkS4PDBpMYWuUeSll4xLaH_xOBWNheawlHmeJfFvQENyJen40iRX2wyr2O_0dbSp4vqECmLFUOAAAAAAAAALtu3jpxRJ8BxPj0a_uT3vfIJc1oXF6c2YLfz0vQVRGQDwAAAIYiESH39Z_5qCwBAAAAAAAAAAAAAAAAAAAAAAAAAAAAIt9JAAAAAAAFAAAAuQz4FzoaBgCON9nm9rGPmKLtKYIyIgQUZnsku_r99c7nyMf5ccHVK-vG8LGXzMwp",
        "AAAAAAAAAADTpay8OhoGAOBzcvf5wbn8rH8ZCxqeb4TKlOzccJXCnFMKe5aZgSnjAwAAAAAAGw4ykoZXsXlRGOkKc4fMD_0nB7K_J-wNfkI00WJyQBwOAAAAQ44Ls8M9WyUK1eRNhKcrbCaT1mDvhiMO-vtn8TLz0OAQAAAAz4vkS4PDBpMYWuUeSll4xLaH_xOBWNheawlHmeJfFvQENyJen40iRX2wyr2O_0dbSp4vqECmLFUOAAAAAAAAANNH3pUUkZL8iQkYg0ar5mUjvmg0hAjPjgW-Fir0kDpBDwAAAIYiESH39Z_5qCwBAAAAAAAAAAAAAAAAAAAAAAAAAAAAIt9JAAAAAAAFAAAAuQz4FzoaBgAPbU8qDExYIu77NSBtjkJdLVYYPKuobJLHS8v-Fo6ZbjkiTqhgmR8c"
    ],
    "next": "exclusive:7vs1IG2OQl0tVhg8q6hsksdLy_4WjpluOSJOqGCZHxw"
}
  • Item 里面的 190 到 222 取出来之后做 base64 编码之后为 Payload Hash

5.根据 payload hash 获取交易详情

  • 接口作用:根据 payload hash 获取交易详情
  • 接口参数:payload hash
  • 请求示范
curl --location 'https://api.chainweb.com/chainweb/0.0/mainnet01/chain/15/payload/u27eOnFEnwHE-PRr-5Pe98glzWhcXpzZgt_PS9BVEZA/outputs'
  • 返回值
{
    "transactions": [
        [
            "eyJoYXNoIjoid2FiVnB5Y2pLSXRWUlRqdTRPMnlHZFFNTTdPVHYyT1VkMVlwQmZCNjVlbyIsInNpZ3MiOlt7InNpZyI6IjA1YWY3NGRlNzIzYjMwMzE2ZWRhNDVkN2JiMGNhZmYxNjc3NmFkZDMzMGI1YzE1NjhiMTcyYjNkODU0NjAzNDRjZDA1MmYwNDg0ZDIzYzE3Mjk5Mjk2OTIyMmYzOTgxNjQwMjU2M2I3MWY5MTE5ZGJjZWJlZTEyM2RhN2M1YjA4In1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpcIm1haW5uZXQwMVwiLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wia3NcIjp7XCJwcmVkXCI6XCJrZXlzLWFsbFwiLFwia2V5c1wiOltcImJhOTcxZDQxYjUzZTY1MWU2NzQ2ZmFlZjljODg4NTYxNmQ2OWFmNjkyMDA1NTdkNDI4MTBjYTRhMWY1NTkxMWNcIl19fSxcImNvZGVcIjpcIihjb2luLmNyZWF0ZS1hY2NvdW50IFxcXCJrOmJhOTcxZDQxYjUzZTY1MWU2NzQ2ZmFlZjljODg4NTYxNmQ2OWFmNjkyMDA1NTdkNDI4MTBjYTRhMWY1NTkxMWNcXFwiIChyZWFkLWtleXNldCBcXFwia3NcXFwiKSlcIn19LFwic2lnbmVyc1wiOlt7XCJjbGlzdFwiOlt7XCJuYW1lXCI6XCJjb2luLkdBU1wiLFwiYXJnc1wiOltdfV0sXCJwdWJLZXlcIjpcIjVhZGIxNjY2MzA3MzI4MGFjZjYzYmMyYTRiZjQ3N2FkMTM5MTczNmRjZDYyMTdiMDk0OTI2ODYyYzcyZDE1YzlcIn1dLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjE3MTc2ODkyMjYsXCJ0dGxcIjoxODAwLFwiZ2FzTGltaXRcIjozMDAwLFwiY2hhaW5JZFwiOlwiMTVcIixcImdhc1ByaWNlXCI6MWUtOCxcInNlbmRlclwiOlwiazo1YWRiMTY2NjMwNzMyODBhY2Y2M2JjMmE0YmY0NzdhZDEzOTE3MzZkY2Q2MjE3YjA5NDkyNjg2MmM3MmQxNWM5XCJ9LFwibm9uY2VcIjpcIlxcXCIyMDI0LTA2LTA2VDE1OjU0OjAxLjI3N1pcXFwiXCJ9In0",
            "eyJnYXMiOjQzMywicmVzdWx0Ijp7InN0YXR1cyI6InN1Y2Nlc3MiLCJkYXRhIjoiV3JpdGUgc3VjY2VlZGVkIn0sInJlcUtleSI6IndhYlZweWNqS0l0VlJUanU0TzJ5R2RRTU03T1R2Mk9VZDFZcEJmQjY1ZW8iLCJsb2dzIjoiR0dnRlRXR3N3Um1tTVNpWk1weGotUkJWUUFHcXRvdGh6SzItM0RQOWF4OCIsImV2ZW50cyI6W3sicGFyYW1zIjpbIms6NWFkYjE2NjYzMDczMjgwYWNmNjNiYzJhNGJmNDc3YWQxMzkxNzM2ZGNkNjIxN2IwOTQ5MjY4NjJjNzJkMTVjOSIsIms6MjUxZWZiMDZmM2I3OThkYmU3YmIzZjU4ZjUzNWI2N2IwYTllZDJkYTlhYTRlMjM2N2JlNGFiYzA3Y2M5MjdmYSIsNC4zM2UtNl0sIm5hbWUiOiJUUkFOU0ZFUiIsIm1vZHVsZSI6eyJuYW1lc3BhY2UiOm51bGwsIm5hbWUiOiJjb2luIn0sIm1vZHVsZUhhc2giOiJrbEZrckxmcHlMVy1NM3hqVlBTZHFYRU1neFBQSmliUnRfRDZxaUJ3czZzIn1dLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjo0NDEwNzI0fQ"
        ],
        [
            "eyJoYXNoIjoiMlMzM2l5T1E3QTNDemxOUmFBLWNoQ0llSk1Cd1o1ZzNzcWlBYV8tV2ctOCIsInNpZ3MiOlt7InNpZyI6IjMyN2ZhMjA4Yzc1YTA4MDMxZDZlY2JlNzdkZTRiMmI4N2NmMDBlNmU2NWZiMDc2OWQ3MGY0ZTlhYTNiNDFhOGI1ZTI0OTJhNjI2NzY0NDE1ODgwOGUzZTViMTNiM2Q1ODkyOGY5MGM5NjU5OGRhNjBiMDU4NmUxYTkwZjY2NjA4In1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpcIm1haW5uZXQwMVwiLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wia3NcIjp7XCJwcmVkXCI6XCJrZXlzLWFsbFwiLFwia2V5c1wiOltcIjk4ZWM5YWQ4YzIzOGEzYmQ1MDk5NGM0OWJiNzUzMDI4NmQyNDIxNGEyNDllZTY0ZjBlNTJiYmVjZWQ3YjMxZjlcIl19fSxcImNvZGVcIjpcIihjb2luLmNyZWF0ZS1hY2NvdW50IFxcXCJrOjk4ZWM5YWQ4YzIzOGEzYmQ1MDk5NGM0OWJiNzUzMDI4NmQyNDIxNGEyNDllZTY0ZjBlNTJiYmVjZWQ3YjMxZjlcXFwiIChyZWFkLWtleXNldCBcXFwia3NcXFwiKSlcIn19LFwic2lnbmVyc1wiOlt7XCJjbGlzdFwiOlt7XCJuYW1lXCI6XCJjb2luLkdBU1wiLFwiYXJnc1wiOltdfV0sXCJwdWJLZXlcIjpcIjVhZGIxNjY2MzA3MzI4MGFjZjYzYmMyYTRiZjQ3N2FkMTM5MTczNmRjZDYyMTdiMDk0OTI2ODYyYzcyZDE1YzlcIn1dLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjE3MTc2ODkzNjEsXCJ0dGxcIjoxODAwLFwiZ2FzTGltaXRcIjozMDAwLFwiY2hhaW5JZFwiOlwiMTVcIixcImdhc1ByaWNlXCI6MWUtOCxcInNlbmRlclwiOlwiazo1YWRiMTY2NjMwNzMyODBhY2Y2M2JjMmE0YmY0NzdhZDEzOTE3MzZkY2Q2MjE3YjA5NDkyNjg2MmM3MmQxNWM5XCJ9LFwibm9uY2VcIjpcIlxcXCIyMDI0LTA2LTA2VDE1OjU2OjE1Ljc4M1pcXFwiXCJ9In0",
            "eyJnYXMiOjQzMywicmVzdWx0Ijp7InN0YXR1cyI6InN1Y2Nlc3MiLCJkYXRhIjoiV3JpdGUgc3VjY2VlZGVkIn0sInJlcUtleSI6IjJTMzNpeU9RN0EzQ3psTlJhQS1jaENJZUpNQndaNWczc3FpQWFfLVdnLTgiLCJsb2dzIjoiZ3R3ZERZaHRvM2NQZDJQLXo1NHR0MGxYTlN4WW93d3lBX210TlJqTGszZyIsImV2ZW50cyI6W3sicGFyYW1zIjpbIms6NWFkYjE2NjYzMDczMjgwYWNmNjNiYzJhNGJmNDc3YWQxMzkxNzM2ZGNkNjIxN2IwOTQ5MjY4NjJjNzJkMTVjOSIsIms6MjUxZWZiMDZmM2I3OThkYmU3YmIzZjU4ZjUzNWI2N2IwYTllZDJkYTlhYTRlMjM2N2JlNGFiYzA3Y2M5MjdmYSIsNC4zM2UtNl0sIm5hbWUiOiJUUkFOU0ZFUiIsIm1vZHVsZSI6eyJuYW1lc3BhY2UiOm51bGwsIm5hbWUiOiJjb2luIn0sIm1vZHVsZUhhc2giOiJrbEZrckxmcHlMVy1NM3hqVlBTZHFYRU1neFBQSmliUnRfRDZxaUJ3czZzIn1dLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjo0NDEwNzI3fQ"
        ]
    ],
    "minerData": "eyJhY2NvdW50IjoiazoyNTFlZmIwNmYzYjc5OGRiZTdiYjNmNThmNTM1YjY3YjBhOWVkMmRhOWFhNGUyMzY3YmU0YWJjMDdjYzkyN2ZhIiwicHJlZGljYXRlIjoia2V5cy1hbGwiLCJwdWJsaWMta2V5cyI6WyIyNTFlZmIwNmYzYjc5OGRiZTdiYjNmNThmNTM1YjY3YjBhOWVkMmRhOWFhNGUyMzY3YmU0YWJjMDdjYzkyN2ZhIl19",
    "transactionsHash": "2sXMEnpo9UDZRQV2ZNfhiwWYiYK98tOqRXPe6QXxkrs",
    "outputsHash": "EwbSZ1XlkLFYTRz3auf9spIrXxruVVpjvMf6-JhttQM",
    "payloadHash": "u27eOnFEnwHE-PRr-5Pe98glzWhcXpzZgt_PS9BVEZA",
    "coinbase": "eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJJalJJVG5rNVgyNUNkV1o1YzJaNGEweEhjRFYyYUUxeFZUZE9lSGRzWTB0alZYZHdOMnh3YlVKTFpVMGkiLCJsb2dzIjoieDhNWWRnZXljSDBmTTRSWXhoenF3M05kWmlyMDFYNHdobWlPRzB0eVo4NCIsImV2ZW50cyI6W3sicGFyYW1zIjpbIiIsIms6MjUxZWZiMDZmM2I3OThkYmU3YmIzZjU4ZjUzNWI2N2IwYTllZDJkYTlhYTRlMjM2N2JlNGFiYzA3Y2M5MjdmYSIsMC45ODMwMjY1XSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6ImtsRmtyTGZweUxXLU0zeGpWUFNkcVhFTWd4UFBKaWJSdF9ENnFpQndzNnMifV0sIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjQ0MTA3MjJ9"
}
  • 返回的接口交易解析伪代码如下
let timeStamp;
let fromAddrs: any = [];
let toAddrs: any = [];
let amounts: any = [];
const dataBuf = base64url.toBuffer(blockTxResJson.items[i]);
const payloadReq = base64url.encode(dataBuf.slice(190, 222));
const txReqOps = {
    url: config.host + `/chainweb/0.0/mainnet01/chain/${chainId}/payload/${payloadReq}/outputs`,
};
let txResOpsJson: any = await requestApi(txReqOps, true, { get: true });
for(let j =0; j < txResOpsJson.transactions.length; j++) {
    const signCmd = JSON.parse(base64url.decode(txResOpsJson.transactions[j][0]));
    const txInfo = JSON.parse(base64url.decode(txResOpsJson.transactions[j][1]));
    if (txInfo.result.status === "success" && txInfo.reqKey === txHash) {
        const payload = JSON.parse(signCmd.cmd).payload
        timeStamp = JSON.parse(signCmd.cmd).meta.creationTime
        for (let key in payload){
            if(key === "cont") {
                if (payload.cont && payload.cont.proof !== undefined && payload.cont.step !== undefined && payload.cont.step == 1) {
                    if(txInfo.continuation !== null) {
                        const transfer = txInfo.continuation.continuation
                        if (transfer.def === "coin.transfer-crosschain") {
                            fromAddrs.push(transfer.args[0])
                            toAddrs.push(transfer.args[1])
                            amounts.push(transfer.args[4].toString())
                        }
                    }
                }
            }
            if(key === "exec") {
                const payload_code = payload.exec.code
                const m = payload_code.match(/\((.*?) /)
                if (m.indexOf("coin.transfer-crosschain") !== -1 || m.indexOf("coin.transfer-create") !== -1 || m.indexOf("coin.transfer") !== -1) {
                    const signers = JSON.parse(signCmd.cmd).signers
                    if (signers.length === 0 || signers[0].clist.length >= 3) {
                        continue
                    }
                    for(let k = 0; k < signers[0].clist.length; k++) {
                        if (signers[0].clist[k].name === "coin.TRANSFER" || signers[0].clist[k].name === "coin.TRANSFER_XCHAIN") {
                            const transferArgs = signers[0].clist[k].args
                            if(transferArgs.length === 4 || transferArgs.length === 3) {
                                fromAddrs.push(transferArgs[0])
                                toAddrs.push(transferArgs[1])
                                let amount = transferArgs[2]
                                for (let key in transferArgs[2]){
                                    if(key === "decimal") {
                                        amount = transferArgs[2].decimal
                                    }
                                }
                                amounts.push(amount.toString())
                            }
                        }
                    }
                }
            }
        }
    }
}

fromAddress: fromAddrs ,
toAddress: toAddrs ,
amount: amounts,
timestamp: timeStamp,
confirms: lastestBlock - Number(blockHeight),  

6.广播交易到区块链网络

  • 接口作用:发送交易到区块链网络
  • 接口参数:离线签名的交易体
  • 请求示范
curl --location 'https://api.chainweb.com/chainweb/0.0/mainnet01/chain/2/pact/api/v1/send' \
--header 'Content-Type: application/json' \
--data '{"cmds":[{"hash":"uSOYjbztP-C1_zEZk0Gka-RTKyuozwiQWt9IskTQv1I","sigs":[{"sig":"adeb64689c43cbd3c27ff4a692803cf990433554fe4b4d379894c2f9ebc934f1689ee3df6e66416308fe4403893ecb0bf83399b71942044eec521cf01eec9809"}],"cmd":"{\"networkId\":\"mainnet01\",\"payload\":{\"exec\":{\"data\":{},\"code\":\"(coin.transfer \\\"k:259e184f5c7cecf261063dd298e250b2303cf896a3d6705eedd35cc8b97cee9b\\\" \\\"k:6a7bb957fedd2c7e14edb76e66be3c588881f121185aa5d7cb1cb436155c6164\\\" 0.01)\"}},\"signers\":[{\"clist\":[{\"name\":\"coin.TRANSFER\",\"args\":[\"k:259e184f5c7cecf261063dd298e250b2303cf896a3d6705eedd35cc8b97cee9b\",\"k:6a7bb957fedd2c7e14edb76e66be3c588881f121185aa5d7cb1cb436155c6164\",0.01]},{\"name\":\"coin.GAS\",\"args\":[]}],\"pubKey\":\"259e184f5c7cecf261063dd298e250b2303cf896a3d6705eedd35cc8b97cee9b\"}],\"meta\":{\"creationTime\":1717723428,\"ttl\":600,\"gasLimit\":600,\"chainId\":\"2\",\"gasPrice\":0.0001,\"sender\":\"k:259e184f5c7cecf261063dd298e250b2303cf896a3d6705eedd35cc8b97cee9b\"},\"nonce\":\"\\\"2024-06-07T01:24:02.801Z\\\"\"}"}]}
'
  • 返回值
{
    "requestKeys": [
        "uSOYjbztP-C1_zEZk0Gka-RTKyuozwiQWt9IskTQv1I"
    ]
}
  • requestKeys:交易 Hash

五. 和钱包相关的特点总结

1.Gas 经验值

  • STATIC_FEE = dec('0.006')
  • STATIC_GAS_PRICE = dec('0.00003')

2. 跨链交易特点

  • KDA 整个项目由 20 条链组成,链与链之间的资产独立,可以互相跨转

跨链交易流程如下:

  • 源链发送一笔离线签名交易
  • 使用 RequestKey 和目标链 chainID 做为接口参数获取 spv
  • 使用 SPV 和 PactId(RequestKey 做为 PactId )离线签名之后目标链上再发一笔交易

3. 其他特点

  • 算法:Ed25519
  • 代币精度:12

六. 附录

  • Github: https://github.com/kadena-io/
  • 开放 API 节点:https://api.chainweb.com
  • 浏览器:https://explorer.chainweb.com/mainnet
  • 钱包:https://chainweaver.kadena.network/
  • API 文档:https://docs.kadena.io/reference/chainweb-ref
  • 官方 docs: https://docs.kadena.io/

全部评论(0)