From c09b74ac3243559b40967b2bd73ea6e8d7183c9c Mon Sep 17 00:00:00 2001 From: PHCitizen <75726261+PHCitizen@users.noreply.github.com> Date: Sun, 2 Feb 2025 10:19:42 +0000 Subject: [PATCH 1/7] feat: add BchdGrpcNetworkProvider --- packages/cashscript/package.json | 3 + packages/cashscript/src/index.ts | 1 + .../BchdGrpcNetworkProvider/bchrpc.proto | 1018 +++++++++++++++++ .../network/BchdGrpcNetworkProvider/index.ts | 70 ++ packages/cashscript/src/network/index.ts | 1 + yarn.lock | 49 + 6 files changed, 1142 insertions(+) create mode 100644 packages/cashscript/src/network/BchdGrpcNetworkProvider/bchrpc.proto create mode 100644 packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts diff --git a/packages/cashscript/package.json b/packages/cashscript/package.json index b725ea8b..6138f251 100644 --- a/packages/cashscript/package.json +++ b/packages/cashscript/package.json @@ -30,6 +30,7 @@ "test": "test" }, "scripts": { + "generate:bchd-grpc": "mkdir -p ./src/network/BchdGrpcNetworkProvider/generated && protoc --ts_out ./src/network/BchdGrpcNetworkProvider/generated --proto_path src/network/BchdGrpcNetworkProvider bchrpc.proto", "build": "yarn clean && yarn compile", "build:test": "yarn clean:test && yarn compile:test", "clean": "rm -rf ./dist", @@ -46,6 +47,7 @@ "@bitauth/libauth": "^3.1.0-next.2", "@cashscript/utils": "^0.11.0-next.0", "@mr-zwets/bchn-api-wrapper": "^1.0.1", + "@protobuf-ts/grpcweb-transport": "^2.9.4", "delay": "^6.0.0", "electrum-cash": "^2.0.10", "fast-deep-equal": "^3.1.3", @@ -54,6 +56,7 @@ }, "devDependencies": { "@jest/globals": "^29.7.0", + "@protobuf-ts/plugin": "^2.9.4", "@psf/bch-js": "^6.8.0", "@types/pako": "^2.0.3", "@types/semver": "^7.5.8", diff --git a/packages/cashscript/src/index.ts b/packages/cashscript/src/index.ts index 53c364bb..9d1652b1 100644 --- a/packages/cashscript/src/index.ts +++ b/packages/cashscript/src/index.ts @@ -19,5 +19,6 @@ export { ElectrumNetworkProvider, FullStackNetworkProvider, MockNetworkProvider, + BchdGrpcNetworkProvider, } from './network/index.js'; export { randomUtxo, randomToken, randomNFT } from './utils.js'; diff --git a/packages/cashscript/src/network/BchdGrpcNetworkProvider/bchrpc.proto b/packages/cashscript/src/network/BchdGrpcNetworkProvider/bchrpc.proto new file mode 100644 index 00000000..7984b074 --- /dev/null +++ b/packages/cashscript/src/network/BchdGrpcNetworkProvider/bchrpc.proto @@ -0,0 +1,1018 @@ +syntax = "proto3"; +option go_package="github.com/gcash/bchd/bchrpc/pb"; + +package pb; +option java_package = "cash.bchd.rpc"; + +// bchrpc contains a set of RPCs that can be exposed publicly via +// the command line options. This service could be authenticated or +// unauthenticated. +service bchrpc { + + // GetMempoolInfo returns the state of the current mempool. + rpc GetMempoolInfo(GetMempoolInfoRequest) returns (GetMempoolInfoResponse) {} + + // GetMempool returns information about all transactions currently in the memory pool. + // Offers an option to return full transactions or just transactions hashes. + rpc GetMempool(GetMempoolRequest) returns (GetMempoolResponse) {} + + // GetBlockchainInfo returns data about the blockchain including the most recent + // block hash and height. + rpc GetBlockchainInfo(GetBlockchainInfoRequest) returns (GetBlockchainInfoResponse) {} + + // GetBlockInfo returns metadata and info for a specified block. + rpc GetBlockInfo(GetBlockInfoRequest)returns (GetBlockInfoResponse) {} + + // GetBlock returns detailed data for a block. + rpc GetBlock(GetBlockRequest) returns (GetBlockResponse) {} + + // GetRawBlock returns a block in a serialized format. + rpc GetRawBlock(GetRawBlockRequest) returns (GetRawBlockResponse) {} + + // GetBlockFilter returns the compact filter (cf) of a block as a Golomb-Rice encoded set. + // + // **Requires CfIndex** + rpc GetBlockFilter(GetBlockFilterRequest) returns (GetBlockFilterResponse) {} + + // GetHeaders takes a block locator object and returns a batch of no more than 2000 + // headers. Upon parsing the block locator, if the server concludes there has been a + // fork, it will send headers starting at the fork point, or genesis if no blocks in + // the locator are in the best chain. If the locator is already at the tip no headers + // will be returned. + // see: bchd/bchrpc/documentation/wallet_operation.md + rpc GetHeaders(GetHeadersRequest) returns (GetHeadersResponse) {} + + // GetTransaction returns a transaction given a transaction hash. + // + // **Requires TxIndex** + // **Requires SlpIndex for slp related information ** + rpc GetTransaction(GetTransactionRequest) returns (GetTransactionResponse) {} + + // GetRawTransaction returns a serialized transaction given a transaction hash. + // + // **Requires TxIndex** + rpc GetRawTransaction(GetRawTransactionRequest) returns (GetRawTransactionResponse) {} + + // GetAddressTransactions returns the transactions for the given address. Offers offset, + // limit, and from block options. + // + // **Requires AddressIndex** + // **Requires SlpIndex for slp related information ** + rpc GetAddressTransactions(GetAddressTransactionsRequest) returns (GetAddressTransactionsResponse) {} + + // GetRawAddressTransactions returns the serialized raw transactions for + // the given address. Offers offset, limit, and from block options. + // + // **Requires AddressIndex** + rpc GetRawAddressTransactions(GetRawAddressTransactionsRequest) returns (GetRawAddressTransactionsResponse) {} + + // GetAddressUnspentOutputs returns all the unspent transaction outputs + // for the given address. + // + // **Requires AddressIndex** + // **Requires SlpIndex for slp related information ** + rpc GetAddressUnspentOutputs(GetAddressUnspentOutputsRequest) returns (GetAddressUnspentOutputsResponse) {} + + // GetUnspentOutput takes an unspent output in the utxo set and returns + // the utxo metadata or not found. + // + // **Requires SlpIndex for slp related information ** + rpc GetUnspentOutput(GetUnspentOutputRequest) returns (GetUnspentOutputResponse) {} + + // GetMerkleProof returns a Merkle (SPV) proof for a specific transaction + // in the provided block. + // + // **Requires TxIndex** + rpc GetMerkleProof(GetMerkleProofRequest) returns (GetMerkleProofResponse) {} + + // GetSlpTokenMetadata return slp token metadata for one or more tokens. + // + // **Requires SlpIndex** + rpc GetSlpTokenMetadata(GetSlpTokenMetadataRequest) returns (GetSlpTokenMetadataResponse) {} + + // GetSlpParsedScript returns marshalled object from parsing an slp pubKeyScript + // using goslp package. This endpoint does not require SlpIndex. + rpc GetSlpParsedScript(GetSlpParsedScriptRequest) returns (GetSlpParsedScriptResponse) {} + + // GetSlpTrustedValidation returns slp validity related information for one or more transactions. + // + // **Requires SlpIndex** + rpc GetSlpTrustedValidation(GetSlpTrustedValidationRequest) returns (GetSlpTrustedValidationResponse) {} + + // GraphSearch returns all the transactions needed for a client to validate an SLP graph + // + // **Requires SlpIndex and SlpGraphSearch** + rpc GetSlpGraphSearch (GetSlpGraphSearchRequest) returns (GetSlpGraphSearchResponse) {} + + // CheckSlpTransaction checks the validity of a supposed slp transaction before it is broadcasted. + rpc CheckSlpTransaction(CheckSlpTransactionRequest) returns (CheckSlpTransactionResponse) {} + + // Submit a transaction to all connected peers. + rpc SubmitTransaction(SubmitTransactionRequest) returns (SubmitTransactionResponse) {} + + // SubscribeTransactions creates subscription to all relevant transactions based on + // the subscription filter. + // + // This RPC does not use bidirectional streams and therefore can be used + // with grpc-web. You will need to close and reopen the stream whenever + // you want to update the subscription filter. If you are not using grpc-web + // then SubscribeTransactionStream is more appropriate. + // + // **Requires TxIndex to receive input metadata** + // **Requires SlpIndex to receive slp input/output metadata, or SlpTokenMetadata** + rpc SubscribeTransactions(SubscribeTransactionsRequest) returns (stream TransactionNotification) {} + + // SubscribeTransactionStream subscribes to relevant transactions based on + // the subscription requests. The parameters to filter transactions on can + // be updated by sending new SubscribeTransactionsRequest objects on the stream. + // + // NOTE: Because this RPC is using bi-directional streaming it cannot be used with + // grpc-web. + // + // **Requires TxIndex to receive input metadata** + rpc SubscribeTransactionStream(stream SubscribeTransactionsRequest) returns (stream TransactionNotification) {} + + // SubscribeBlocks creates a subscription for notifications of new blocks being + // connected to the blockchain or blocks being disconnected. + rpc SubscribeBlocks(SubscribeBlocksRequest) returns (stream BlockNotification) {} +} + + +// RPC MESSAGES + +message GetMempoolInfoRequest {} +message GetMempoolInfoResponse { + // The count of transactions in the mempool + uint32 size = 1; + // The size in bytes of all transactions in the mempool + uint32 bytes = 2; +} + +message GetMempoolRequest { + // When `full_transactions` is true, full transaction data is provided + // instead of just transaction hashes. Default is false. + bool full_transactions = 1; +} + +message GetMempoolResponse { + message TransactionData { + // Either one of the two following is provided, depending on the request. + oneof txids_or_txs { + // The transaction hash, little-endian. + bytes transaction_hash = 1; + // The transaction data. + Transaction transaction = 2; + } + } + + // List of unconfirmed transactions. + repeated TransactionData transaction_data = 1; +} + +message GetBlockchainInfoRequest {} +message GetBlockchainInfoResponse { + + // Bitcoin network types + enum BitcoinNet { + + // Live public network with monetary value. + MAINNET = 0; + // An isolated environment for automated testing. + REGTEST = 1; + // A public environment where monetary value is agreed to be zero, + // and some checks for transaction conformity are disabled. + TESTNET3 = 2; + // Private testnets for large scale simulations (or stress testing), + // where a specified list of nodes is used, rather than node discovery. + SIMNET = 3; + // Latest Testnet. + TESTNET4 = 4; + } + + // Which network the node is operating on. + BitcoinNet bitcoin_net = 1; + + // The current number of blocks on the longest chain. + int32 best_height = 2; + // The hash of the best (tip) block in the most-work fully-validated chain, little-endian. + bytes best_block_hash = 3; + // Threshold for adding new blocks. + double difficulty = 4; + // Median time of the last 11 blocks. + int64 median_time = 5; + // When `tx_index` is true, the node has full transaction index enabled. + bool tx_index = 6; + // When `addr_index` is true, the node has address index enabled and may + // be used with call related by address. + bool addr_index =7; + // When `slp_index` is true, the node has the slp index enabled and may + // be used with slp related rpc methods and also causes slp metadata to be added + // in some of the existing rpc methods. + bool slp_index = 8; + // When `slp_graphsearch` is true, the node is able to handle calls to slp graph search + bool slp_graphsearch = 9; +} + +message GetBlockInfoRequest { + oneof hash_or_height { + // The block hash as a byte array or base64 encoded string, little-endian. + bytes hash = 1; + // The block number. + int32 height = 2; + } +} +message GetBlockInfoResponse { + // Marshaled block header data, as well as metadata. + BlockInfo info = 1; +} + +message GetBlockRequest { + oneof hash_or_height { + // The block hash as a byte array or base64 encoded string, little-endian. + bytes hash = 1; + // The block number. + int32 height = 2; + } + // When `full_transactions` is true, full transactions are returned + // instead of just hashes. Default is false. + bool full_transactions = 3; +} +message GetBlockResponse { + // A marshaled block. + Block block = 1; +} + +message GetRawBlockRequest { + oneof hash_or_height { + // The block hash as a byte array or base64 encoded string, little-endian. + bytes hash = 1; + // The block number. + int32 height = 2; + } +} +message GetRawBlockResponse { + // Raw block data (with header) serialized according the the bitcoin block protocol. + bytes block = 1; +} + +message GetBlockFilterRequest { + oneof hash_or_height { + // The block hash as a byte array or base64 encoded string, little-endian. + bytes hash = 1; + // The block number. + int32 height = 2; + } +} + +message GetBlockFilterResponse { + // A compact filter matching input outpoints and public key scripts contained + // in a block (encoded according to BIP158). + bytes filter = 1; +} + +// Request headers using a list of known block hashes. +message GetHeadersRequest { + // A list of block hashes known to the client (most recent first) which + // is exponentially sparser toward the genesis block (0), little-endian. + // Common practice is to include all of the last 10 blocks, and then + // 9 blocks for each order of ten thereafter. + repeated bytes block_locator_hashes = 1; + // hash of the latest desired block header, little-endian; only blocks + // occurring before the stop will be returned. + bytes stop_hash = 2; +} +message GetHeadersResponse { + // List of block headers. + repeated BlockInfo headers = 1; +} + +// Get a transaction from a transaction hash. +message GetTransactionRequest { + // A transaction hash, little-endian. + bytes hash = 1; + + bool include_token_metadata = 2; +} +message GetTransactionResponse { + // A marshaled transaction. + Transaction transaction = 1; + + SlpTokenMetadata token_metadata = 2; +} + +// Get an encoded transaction from a transaction hash. +message GetRawTransactionRequest { + // A transaction hash, little-endian. + bytes hash = 1; +} +message GetRawTransactionResponse { + // Raw transaction in bytes. + bytes transaction = 1; +} + +// Get marshaled transactions related to a specific address. +// +// RECOMMENDED: +// Parameters have been provided to query without creating +// performance issues on the node or client. +// +// - The number of transactions to skip and fetch allow for iterating +// over a large set of transactions, if necessary. +// +// - A starting block parameter (either `hash` or `height`) +// may then be used to filter results to those occurring +// after a certain time. +// +// This approach will reduce network traffic and response processing +// for the client, as well as reduce workload on the node. +message GetAddressTransactionsRequest { + // The address to query transactions, in lowercase cashaddr format. + // The network prefix is optional (i.e. "cashaddress:"). + string address = 1; + + // The number of confirmed transactions to skip, starting with the oldest first. + // Does not affect results of unconfirmed transactions. + uint32 nb_skip = 2; + // Specify the number of transactions to fetch. + uint32 nb_fetch = 3; + + + oneof start_block { + // Recommended. Only get transactions after (or within) a + // starting block identified by hash, little-endian. + bytes hash = 4; + // Recommended. Only get transactions after (or within) a + // starting block identified by block number. + int32 height = 5; + } +} +message GetAddressTransactionsResponse { + // Transactions that have been included in a block. + repeated Transaction confirmed_transactions = 1; + // Transactions in mempool which have not been included in a block. + repeated MempoolTransaction unconfirmed_transactions = 2; +} + +// Get encoded transactions related to a specific address. +// +// RECOMMENDED: +// Parameters have been provided to query without creating +// performance issues on the node or client. +// +// - The number of transactions to skip and fetch allow for iterating +// over a large set of transactions, if necessary. +// +// - A starting block parameter (either `hash` or `height`) +// may then be used to filter results to those occurring +// after a certain time. +// +// This approach will reduce network traffic and response processing +// for the client, as well as reduce workload on the node. +message GetRawAddressTransactionsRequest { + // The address to query transactions, in lowercase cashaddr format. + // The network prefix is optional (i.e. "cashaddress:"). + string address = 1; + + // The number of confirmed transactions to skip, starting with the oldest first. + // Does not affect results of unconfirmed transactions. + uint32 nb_skip = 2; + // Specify the number of transactions to fetch. + uint32 nb_fetch = 3; + + oneof start_block { + // Recommended. Only return transactions after some starting block + // identified by hash, little-endian. + bytes hash = 4; + // Recommended. Only return transactions after some starting block + // identified by block number. + int32 height = 5; + } +} +message GetRawAddressTransactionsResponse { + // Transactions that have been included in a block. + repeated bytes confirmed_transactions = 1; + // Transactions in mempool which have not been included in a block. + repeated bytes unconfirmed_transactions = 2; +} + +message GetAddressUnspentOutputsRequest { + // The address to query transactions, in lowercase cashaddr format. + // The network identifier is optional (i.e. "cashaddress:"). + string address = 1; + // When `include_mempool` is true, unconfirmed transactions from mempool + // are returned. Default is false. + bool include_mempool = 2; + bool include_token_metadata = 3; +} +message GetAddressUnspentOutputsResponse { + // List of unspent outputs. + repeated UnspentOutput outputs = 1; + repeated SlpTokenMetadata token_metadata = 2; +} + +message GetUnspentOutputRequest { + // The hash of the transaction, little-endian. + bytes hash = 1; + // The number of the output, starting from zero. + uint32 index = 2; + // When include_mempool is true, unconfirmed transactions from mempool + // are returned. Default is false. + bool include_mempool = 3; + bool include_token_metadata = 4; +} +message GetUnspentOutputResponse { + // A reference to the related input. + Transaction.Input.Outpoint outpoint = 1; + // Locking script dictating how funds can be spent in the future + bytes pubkey_script = 2; + // Amount in satoshi. + int64 value = 3; + // When is_coinbase is true, the transaction was the first in a block, + // created by a miner, and used to pay the block reward + bool is_coinbase = 4; + // The index number of the block containing the transaction creating the output. + int32 block_height = 5; + + SlpToken slp_token = 6; + SlpTokenMetadata token_metadata = 7; + + CashToken cash_token = 8; +} + +message GetMerkleProofRequest { + // A transaction hash, little-endian. + bytes transaction_hash = 1; +} +message GetMerkleProofResponse { + // Block header information for the corresponding transaction + BlockInfo block = 1; + // A list containing the transaction hash, the adjacent leaf transaction hash + // and the hashes of the highest nodes in the merkle tree not built with the transaction. + // Proof hashes are ordered following transaction order, or left to right on the merkle tree + repeated bytes hashes = 2; + // Binary representing the location of the matching transaction in the full merkle tree, + // starting with the root (`1`) at position/level 0, where `1` corresponds + // to a left branch and `01` is a right branch. + bytes flags = 3; +} + +message SubmitTransactionRequest { + // The encoded transaction. + bytes transaction = 1; + bool skip_slp_validity_check = 2; + repeated SlpRequiredBurn required_slp_burns = 3; +} +message SubmitTransactionResponse { + // Transaction hash, little-endian. + bytes hash = 1; +} + +message CheckSlpTransactionRequest { + bytes transaction = 1; + repeated SlpRequiredBurn required_slp_burns = 2; + + // Using the slp specification as a basis for validity judgement can lead to confusion for new users and + // result in accidental token burns. use_spec_validity_judgement will cause the response's is_valid property + // to be returned according to the slp specification. Therefore, use_spec_validity_judgement is false by + // default in order to avoid accidental token burns. When use_spec_validity_judgement is false we return + // invalid in any case which would result in a burned token, unless the burn is explicitly included as an + // item in required_slp_burns property. + // + // When use_spec_validity_judgement is true, there are three cases where the is_valid response property + // will be returned as valid, instead of invalid, as per the slp specification. + // 1) inputs > outputs + // 2) missing transaction outputs + // 3) burned inputs from other tokens + // + // required_slp_burns is not used when use_spec_validity_judgement is set to true. + // + bool use_spec_validity_judgement = 3; +} + +message CheckSlpTransactionResponse { + bool is_valid = 1; + string invalid_reason = 2; + int32 best_height = 3; +} + +// Request to subscribe or unsubscribe from a stream of transactions. +message SubscribeTransactionsRequest { + // Subscribe to a filter. add items to a filter + TransactionFilter subscribe = 1; + // Unsubscribe to a filter, remove items from a filter + TransactionFilter unsubscribe = 2; + + // When include_mempool is true, new unconfirmed transactions from mempool are + // included apart from the ones confirmed in a block. + bool include_mempool = 3; + + // When include_in_block is true, transactions are included when they are confirmed. + // This notification is sent in addition to any requested mempool notifications. + bool include_in_block = 4; + + // When serialize_tx is true, transactions are serialized using + // bitcoin protocol encoding. Default is false, transaction will be Marshaled + // (see `Transaction`, `MempoolTransaction` and `TransactionNotification`) + bool serialize_tx = 5; +} + +// Options to define data structure to be sent by SubscribeBlock stream: +// +// - BlockInfo (block metadata): `BlockInfo` +// - SubscribeBlocksRequest {} +// +// - Marshaled Block (with transaction hashes): `Block` +// - SubscribeBlocksRequest { +// full_block = true +// } +// - Marshaled Block (with full transaction data): `Block` +// - SubscribeBlocksRequest { +// full_block = true +// full_transactions = true +// } +// - Serialized Block acccording to bitcoin protocol encoding: `bytes` +// - SubscribeBlocksRequest { +// serialize_block = true +// } +message SubscribeBlocksRequest { + // When full_block is true, a complete marshaled block is sent. See `Block`. + // Default is false, block metadata is sent. See `BlockInfo`. + bool full_block = 1; + + // When full_transactions is true, provide full transaction info + // for a marshaled block. + // Default is false, only the transaction hashes are included for + // a marshaled block. See `TransactionData`. + bool full_transactions = 2; + + // When serialize_block is true, blocks are serialized using bitcoin protocol encoding. + // Default is false, block will be Marshaled (see `BlockInfo` and `BlockNotification`) + bool serialize_block = 3; +} + +message GetSlpTokenMetadataRequest { + repeated bytes token_ids = 1; +} + +message GetSlpTokenMetadataResponse { + repeated SlpTokenMetadata token_metadata = 1; +} + +message GetSlpParsedScriptRequest { + bytes slp_opreturn_script = 1; +} + +message GetSlpParsedScriptResponse { + string parsing_error = 1; + bytes token_id = 2; + SlpAction slp_action = 3; + SlpTokenType token_type = 4; + oneof slp_metadata { + SlpV1GenesisMetadata v1_genesis = 5; // NFT1 Group also uses this + SlpV1MintMetadata v1_mint = 6; // NFT1 Group also uses this + SlpV1SendMetadata v1_send = 7; // NFT1 Group also uses this + SlpV1Nft1ChildGenesisMetadata v1_nft1_child_genesis = 8; + SlpV1Nft1ChildSendMetadata v1_nft1_child_send = 9; + } +} + +message GetSlpTrustedValidationRequest { + message Query { + bytes prev_out_hash = 1; + uint32 prev_out_vout = 2; + repeated bytes graphsearch_valid_hashes = 3; + } + repeated Query queries = 1; + bool include_graphsearch_count = 2; +} + +message GetSlpTrustedValidationResponse { + message ValidityResult { + bytes prev_out_hash = 1; + uint32 prev_out_vout = 2; + bytes token_id = 3; + SlpAction slp_action = 4; + SlpTokenType token_type = 5; + oneof validity_result_type { + uint64 v1_token_amount = 6 [jstype = JS_STRING]; + bool v1_mint_baton = 7; + } + bytes slp_txn_opreturn = 8; + uint32 graphsearch_txn_count = 9; + } + + repeated ValidityResult results = 1; +} + +message GetSlpGraphSearchRequest { + bytes hash = 1; + repeated bytes valid_hashes = 2; +} + +message GetSlpGraphSearchResponse { + repeated bytes txdata = 1; +} + +// NOTIFICATIONS + +message BlockNotification { + // State of the block in relation to the chain. + enum Type { + CONNECTED = 0; + DISCONNECTED = 1; + } + + // Whether the block is connected to the chain. + Type type = 1; + oneof block { + // Marshaled block header data, as well as metadata stored by the node. + BlockInfo block_info = 2; + // A Block. + Block marshaled_block = 3; + // Binary block, serialized using bitcoin protocol encoding. + bytes serialized_block = 4; + } +} + +message TransactionNotification { + // State of the transaction acceptance. + enum Type { + // A transaction in mempool. + UNCONFIRMED = 0; + // A transaction in a block. + CONFIRMED = 1; + } + + // Whether or not the transaction has been included in a block. + Type type = 1; + oneof transaction { + // A transaction included in a block. + Transaction confirmed_transaction = 2; + // A transaction in mempool. + MempoolTransaction unconfirmed_transaction = 3; + // Binary transaction, serialized using bitcoin protocol encoding. + bytes serialized_transaction = 4; + } +} + + +// DATA MESSAGES + +// Metadata for identifying and validating a block +message BlockInfo { + // Identification. + + // The double sha256 hash of the six header fields in the first 80 bytes + // of the block, when encoded according the bitcoin protocol, little-endian. + // sha256(sha256(encoded_header)) + bytes hash = 1; + // The block number, an incremental index for each block mined. + int32 height = 2; + + // Block header data. + + // A version number to track software/protocol upgrades. + int32 version = 3; + // Hash of the previous block, little-endian. + bytes previous_block = 4; + // The root of the Merkle Tree built from all transactions in the block, little-endian. + bytes merkle_root = 5; + // When mining of the block started, expressed in seconds since 1970-01-01. + int64 timestamp = 6; + // Difficulty in Compressed Target Format. + uint32 bits = 7; + // A random value that was generated during block mining which happened to + // result in a computed block hash below the difficulty target at the time. + uint32 nonce = 8; + + // Metadata. + + // Number of blocks in a chain, including the block itself upon creation. + int32 confirmations = 9; + // Difficulty target at time of creation. + double difficulty = 10; + // Hash of the next block in this chain, little-endian. + bytes next_block_hash = 11; + // Size of the block in bytes. + int32 size = 12; + // The median block time of the latest 11 block timestamps. + int64 median_time = 13; +} + +message Block { + message TransactionData { + oneof txids_or_txs { + // Just the transaction hash, little-endian. + bytes transaction_hash = 1; + // A marshaled transaction. + Transaction transaction = 2; + } + } + // Block header data, as well as metadata stored by the node. + BlockInfo info = 1; + // List of transactions or transaction hashes. + repeated TransactionData transaction_data = 2; +} + +message Transaction { + message Input { + message Outpoint { + // The hash of the transaction containing the output to be spent, little-endian + bytes hash = 1; + // The index of specific output on the transaction. + uint32 index = 2; + } + // The number of the input, starting from zero. + uint32 index = 1; + // The related outpoint. + Outpoint outpoint = 2; + // An unlocking script asserting a transaction is permitted to spend + // the Outpoint (UTXO) + bytes signature_script = 3; + // As of BIP-68, the sequence number is interpreted as a relative + // lock-time for the input. + uint32 sequence = 4; + // Amount in satoshi. + int64 value = 5; + // The pubkey_script of the previous output that is being spent. + bytes previous_script = 6; + // The bitcoin addresses associated with this input. + string address = 7; + SlpToken slp_token = 8; + CashToken cash_token = 9; + } + + message Output { + // The number of the output, starting from zero. + uint32 index = 1; + // The number of satoshis to be transferred. + int64 value = 2; + // The public key script used to pay coins. + bytes pubkey_script = 3; + // The bitcoin addresses associated with this output. + string address = 4; + // The type of script. + string script_class = 5; + // The script expressed in Bitcoin Cash Script. + string disassembled_script = 6; + SlpToken slp_token = 7; + CashToken cash_token = 8; + } + + // The double sha256 hash of the encoded transaction, little-endian. + // sha256(sha256(encoded_transaction)) + bytes hash = 1; + // The version of the transaction format. + int32 version = 2; + // List of inputs. + repeated Input inputs = 3; + // List of outputs. + repeated Output outputs = 4; + // The block height or timestamp after which this transaction is allowed. + // If value is greater than 500 million, it is assumed to be an epoch timestamp, + // otherwise it is treated as a block-height. Default is zero, or lock. + uint32 lock_time = 5; + + // Metadata + + // The size of the transaction in bytes. + int32 size = 8; + // When the transaction was included in a block, in epoch time. + int64 timestamp = 9; + // Number of blocks including proof of the transaction, including + // the block it appeared. + int32 confirmations = 10; + // Number of the block containing the transaction. + int32 block_height = 11; + // Hash of the block the transaction was recorded in, little-endian. + bytes block_hash = 12; + + SlpTransactionInfo slp_transaction_info = 13; +} + +message MempoolTransaction { + Transaction transaction = 1; + // The time when the transaction was added too the pool. + int64 added_time = 2; + // The block height when the transaction was added to the pool. + int32 added_height = 3; + // The total fee in satoshi the transaction pays. + int64 fee = 4; + // The fee in satoshi per kilobyte the transaction pays. + int64 fee_per_kb = 5; + // The priority of the transaction when it was added to the pool. + double starting_priority = 6; +} + +message UnspentOutput { + // A reference to the output given by transaction hash and index. + Transaction.Input.Outpoint outpoint = 1; + // The public key script used to pay coins. + bytes pubkey_script = 2; + // The amount in satoshis + int64 value = 3; + // When is_coinbase is true, the output is the first in the block, + // a generation transaction, the result of mining. + bool is_coinbase = 4; + // The block number containing the UXTO. + int32 block_height = 5; + + SlpToken slp_token = 6; + CashToken cash_token = 7; +} + +message TransactionFilter { + // Filter by address(es) + repeated string addresses = 1; + + // Filter by output hash and index. + repeated Transaction.Input.Outpoint outpoints = 2; + + // Filter by data elements contained in pubkey scripts. + repeated bytes data_elements = 3; + + // Subscribed/Unsubscribe to everything. Other filters + // will be ignored. + bool all_transactions = 4; + + // Subscribed/Unsubscribe to everything slp. Other filters + // will be ignored, except this filter will be overriden by all_transactions=true + bool all_slp_transactions = 5; + + // only transactions associated with the included tokenIds + repeated bytes slp_token_ids = 6; +} + +// CashToken info used in transaction inputs / outputs +// +// WARNING: Some languages (e.g., JavaScript) may not properly handle the 'uint64' +// for large amounts. For this reason, an annotation has been added for JS to +// return a string for the amount field instead of casting uint64 to the JS 'number' +// type. Other languages may require similar treatment. +// +message CashToken { + bytes category_id = 1; + uint64 amount = 2 [jstype = JS_STRING]; + bytes commitment = 3; + bytes bitfield = 4; +} + +// SlpToken info used in transaction inputs / outputs +// +// WARNING: Some languages (e.g., JavaScript) may not properly handle the 'uint64' +// for large amounts. For this reason, an annotation has been added for JS to +// return a string for the amount field instead of casting uint64 to the JS 'number' +// type. Other languages may require similar treatment. +// +message SlpToken { + bytes token_id = 1; + uint64 amount = 2 [jstype = JS_STRING]; + bool is_mint_baton = 3; + string address = 4; + uint32 decimals = 5; + SlpAction slp_action = 6; + SlpTokenType token_type = 7; +} + +enum SlpTokenType { + VERSION_NOT_SET = 0; + V1_FUNGIBLE = 1; + V1_NFT1_CHILD = 65; + V1_NFT1_GROUP = 129; +} + +// SlpTransactionInfo is used inside the Transaction message type. +message SlpTransactionInfo { + SlpAction slp_action = 1; + enum ValidityJudgement { + UNKNOWN_OR_INVALID = 0; + VALID = 1; + } + ValidityJudgement validity_judgement = 2; + string parse_error = 3; + bytes token_id = 4; + enum BurnFlags { + BURNED_INPUTS_OUTPUTS_TOO_HIGH = 0; + BURNED_INPUTS_BAD_OPRETURN = 1; + BURNED_INPUTS_OTHER_TOKEN = 2; + BURNED_OUTPUTS_MISSING_BCH_VOUT = 3; + BURNED_INPUTS_GREATER_THAN_OUTPUTS = 4; + } + repeated BurnFlags burn_flags = 5; + oneof tx_metadata { + SlpV1GenesisMetadata v1_genesis = 6; // NFT1 Group also uses this + SlpV1MintMetadata v1_mint = 7; // NFT1 Group also uses this + SlpV1SendMetadata v1_send = 8; // NFT1 Group also uses this + SlpV1Nft1ChildGenesisMetadata v1_nft1_child_genesis = 9; + SlpV1Nft1ChildSendMetadata v1_nft1_child_send = 10; + } +} + +// SlpV1GenesisMetadata is used to marshal type 1 and NFT1 Group GENESIS OP_RETURN scriptPubKey +message SlpV1GenesisMetadata { + bytes name = 1; + bytes ticker = 2; + bytes document_url = 3; + bytes document_hash = 4; + uint32 decimals = 5; + uint32 mint_baton_vout = 6; + uint64 mint_amount = 7 [jstype = JS_STRING]; +} + +// SlpV1MintMetadata is used to marshal type 1 MINT OP_RETURN scriptPubKey +message SlpV1MintMetadata { + uint32 mint_baton_vout = 1; + uint64 mint_amount = 2 [jstype = JS_STRING]; +} + +// SlpV1SendMetadata is used to marshal type 1 and NFT1 Group SEND OP_RETURN scriptPubKey +message SlpV1SendMetadata { + repeated uint64 amounts = 1 [jstype = JS_STRING]; +} + +// SlpV1Nft1ChildGenesisMetadata is used to marshal NFT1 Child GENESIS OP_RETURN scriptPubKey +message SlpV1Nft1ChildGenesisMetadata { + bytes name = 1; + bytes ticker = 2; + bytes document_url = 3; + bytes document_hash = 4; + uint32 decimals = 5; + bytes group_token_id = 6; +} + +// SlpV1Nft1ChildSendMetadata is used to marshal NFT1 Child SEND OP_RETURN scriptPubKey +message SlpV1Nft1ChildSendMetadata { + bytes group_token_id = 1; +} + +// SlpAction is used to allow clients to identify the type of slp transaction from this single field. +// +// NOTE: All enum types except for "NON_SLP" may be annotated with one or more BurnFlags. +// +enum SlpAction { + NON_SLP = 0; + NON_SLP_BURN = 1; + SLP_PARSE_ERROR = 2; + SLP_UNSUPPORTED_VERSION = 3; + SLP_V1_GENESIS = 4; + SLP_V1_MINT = 5; + SLP_V1_SEND = 6; + SLP_V1_NFT1_GROUP_GENESIS = 7; + SLP_V1_NFT1_GROUP_MINT = 8; + SLP_V1_NFT1_GROUP_SEND = 9; + SLP_V1_NFT1_UNIQUE_CHILD_GENESIS = 10; + SLP_V1_NFT1_UNIQUE_CHILD_SEND = 11; +} + +// SlpTokenMetadata is used to marshal metadata about a specific TokenID +message SlpTokenMetadata { + bytes token_id = 1; + SlpTokenType token_type = 2; + oneof type_metadata { + V1Fungible v1_fungible = 3; + V1NFT1Group v1_nft1_group = 4; + V1NFT1Child v1_nft1_child = 5; + } + + // V1Fungible is used to marshal metadata specific to Type 1 token IDs + message V1Fungible { + string token_ticker = 1; + string token_name = 2; + string token_document_url = 3; + bytes token_document_hash = 4; + uint32 decimals = 5; + bytes mint_baton_hash = 6; + uint32 mint_baton_vout = 7; + } + + // V1NFT1Group is used to marshal metadata specific to NFT1 Group token IDs + message V1NFT1Group { + string token_ticker = 1; + string token_name = 2; + string token_document_url = 3; + bytes token_document_hash = 4; + uint32 decimals = 5; + bytes mint_baton_hash = 6; + uint32 mint_baton_vout = 7; + } + + // V1NFT1Child is used to marshal metadata specific to NFT1 Child token IDs + message V1NFT1Child { + string token_ticker = 1; + string token_name = 2; + string token_document_url = 3; + bytes token_document_hash = 4; + bytes group_id = 5; + } +} + +// SlpRequiredBurn is used by clients to allow token burning +message SlpRequiredBurn { + Transaction.Input.Outpoint outpoint = 1; + bytes token_id = 2; + SlpTokenType token_type = 3; + oneof burn_intention { + uint64 amount = 4 [jstype = JS_STRING]; + uint32 mint_baton_vout = 5; + } +} diff --git a/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts b/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts new file mode 100644 index 00000000..9e133c4e --- /dev/null +++ b/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts @@ -0,0 +1,70 @@ +import { binToHex, hexToBin } from '@bitauth/libauth'; +import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport'; +import { Utxo, Network } from '../../interfaces.js'; +import NetworkProvider from '../NetworkProvider.js'; +import { bchrpcClient } from './generated/bchrpc.client.js'; + +export default class BchdGrpcNetworkProvider implements NetworkProvider { + client: bchrpcClient; + + constructor(public network: Network, baseUrl: string) { + const transport = new GrpcWebFetchTransport({ baseUrl }); + this.client = new bchrpcClient(transport); + } + + async getUtxos(address: string): Promise { + const { response } = await this.client.getAddressUnspentOutputs({ + address, + includeMempool: true, + includeTokenMetadata: false, + }); + + const utxos = response.outputs.map( + (utxo) => + ({ + // TODO: undefined? + txid: binToHex(utxo.outpoint!.hash.reverse()), + vout: utxo.outpoint!.index, + satoshis: utxo.value, + token: utxo.cashToken + ? { + category: binToHex(utxo.cashToken.categoryId.reverse()), + amount: BigInt(utxo.cashToken.amount), + nft: utxo.cashToken.commitment + ? { + // TODO: parse bitfield + capability: utxo.cashToken.bitfield as any, + commitment: binToHex(utxo.cashToken.commitment), + } + : undefined, + } + : undefined, + } satisfies Utxo) + ); + + return utxos; + } + + async getBlockHeight(): Promise { + const { response } = await this.client.getBlockchainInfo({}); + return response.bestHeight; + } + + async getRawTransaction(txid: string): Promise { + const { response } = await this.client.getRawTransaction({ + hash: hexToBin(txid).reverse(), + }); + + return binToHex(response.transaction); + } + + async sendRawTransaction(txHex: string): Promise { + const { response } = await this.client.submitTransaction({ + transaction: hexToBin(txHex), + requiredSlpBurns: [], + skipSlpValidityCheck: true, + }); + + return binToHex(response.hash); + } +} diff --git a/packages/cashscript/src/network/index.ts b/packages/cashscript/src/network/index.ts index 8d31ddde..38061724 100644 --- a/packages/cashscript/src/network/index.ts +++ b/packages/cashscript/src/network/index.ts @@ -3,3 +3,4 @@ export { default as BitcoinRpcNetworkProvider } from './BitcoinRpcNetworkProvide export { default as ElectrumNetworkProvider } from './ElectrumNetworkProvider.js'; export { default as FullStackNetworkProvider } from './FullStackNetworkProvider.js'; export { default as MockNetworkProvider } from './MockNetworkProvider.js'; +export { default as BchdGrpcNetworkProvider } from './BchdGrpcNetworkProvider/index.js'; diff --git a/yarn.lock b/yarn.lock index 15fa8ffb..1db06aaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2533,6 +2533,50 @@ dependencies: "@types/node" ">= 8" +"@protobuf-ts/grpcweb-transport@^2.9.4": + version "2.9.4" + resolved "https://registry.yarnpkg.com/@protobuf-ts/grpcweb-transport/-/grpcweb-transport-2.9.4.tgz#e98d543d2838e95d5cec1a22f23a02de88393401" + integrity sha512-6aQgwPTgX6FkqWqmNts3uk8T/C5coJoH7U87zgaZY/Wo2EVa9SId5bXTM8uo4WR+CN8j9W4c9ij1yG13Hc3xUw== + dependencies: + "@protobuf-ts/runtime" "^2.9.4" + "@protobuf-ts/runtime-rpc" "^2.9.4" + +"@protobuf-ts/plugin-framework@^2.9.4": + version "2.9.4" + resolved "https://registry.yarnpkg.com/@protobuf-ts/plugin-framework/-/plugin-framework-2.9.4.tgz#d7a617dedda4a12c568fdc1db5aa67d5e4da2406" + integrity sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A== + dependencies: + "@protobuf-ts/runtime" "^2.9.4" + typescript "^3.9" + +"@protobuf-ts/plugin@^2.9.4": + version "2.9.4" + resolved "https://registry.yarnpkg.com/@protobuf-ts/plugin/-/plugin-2.9.4.tgz#4e593e59013aaad313e7abbabe6e61964ef0ca28" + integrity sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw== + dependencies: + "@protobuf-ts/plugin-framework" "^2.9.4" + "@protobuf-ts/protoc" "^2.9.4" + "@protobuf-ts/runtime" "^2.9.4" + "@protobuf-ts/runtime-rpc" "^2.9.4" + typescript "^3.9" + +"@protobuf-ts/protoc@^2.9.4": + version "2.9.4" + resolved "https://registry.yarnpkg.com/@protobuf-ts/protoc/-/protoc-2.9.4.tgz#a92262ee64d252998540238701d2140f4ffec081" + integrity sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ== + +"@protobuf-ts/runtime-rpc@^2.9.4": + version "2.9.4" + resolved "https://registry.yarnpkg.com/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.9.4.tgz#d6ab2316c0ba67ce5a08863bb23203a837ff2a3b" + integrity sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA== + dependencies: + "@protobuf-ts/runtime" "^2.9.4" + +"@protobuf-ts/runtime@^2.9.4": + version "2.9.4" + resolved "https://registry.yarnpkg.com/@protobuf-ts/runtime/-/runtime-2.9.4.tgz#db8a78b1c409e26d258ca39464f4757d804add8f" + integrity sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg== + "@psf/bch-js@^6.8.0": version "6.8.0" resolved "https://registry.yarnpkg.com/@psf/bch-js/-/bch-js-6.8.0.tgz#c7b4c77a52c82795df4f249c78ad11d55ffc6af4" @@ -10733,6 +10777,11 @@ typeforce@^1.18.0: resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== +typescript@^3.9: + version "3.9.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== + typescript@^5.7.3: version "5.7.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" From 34addd52f535b759b51e9b7e679ab1b9bc92f413 Mon Sep 17 00:00:00 2001 From: PHCitizen <75726261+PHCitizen@users.noreply.github.com> Date: Sun, 2 Feb 2025 10:20:00 +0000 Subject: [PATCH 2/7] ignore generated folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1945b137..c041cbfe 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ npm-debug.log **/dist/ **/dist-test/ **/node_modules/ +packages/cashscript/src/network/BchdGrpcNetworkProvider/generated **/.DS_Store /**/.project From 18be7b00080ebc38cc44b44c142a79d4ff28532c Mon Sep 17 00:00:00 2001 From: PHCitizen <75726261+PHCitizen@users.noreply.github.com> Date: Sun, 2 Feb 2025 10:24:31 +0000 Subject: [PATCH 3/7] add `node:` for deno --- packages/utils/src/artifact.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/artifact.ts b/packages/utils/src/artifact.ts index ab3b99ac..17c45a00 100644 --- a/packages/utils/src/artifact.ts +++ b/packages/utils/src/artifact.ts @@ -1,4 +1,4 @@ -import fs, { PathLike } from 'fs'; +import fs, { PathLike } from 'node:fs'; export interface AbiInput { name: string; From 79251c397ce5bf9d0da334068271f982277048ca Mon Sep 17 00:00:00 2001 From: PHCitizen <75726261+PHCitizen@users.noreply.github.com> Date: Sun, 2 Feb 2025 12:02:25 +0000 Subject: [PATCH 4/7] parse nft capability --- .../network/BchdGrpcNetworkProvider/index.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts b/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts index 9e133c4e..bd040884 100644 --- a/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts +++ b/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts @@ -1,9 +1,11 @@ import { binToHex, hexToBin } from '@bitauth/libauth'; import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport'; -import { Utxo, Network } from '../../interfaces.js'; +import { Utxo, Network, TokenDetails } from '../../interfaces.js'; import NetworkProvider from '../NetworkProvider.js'; import { bchrpcClient } from './generated/bchrpc.client.js'; +type NFTCapabilities = NonNullable["capability"]; + export default class BchdGrpcNetworkProvider implements NetworkProvider { client: bchrpcClient; @@ -12,6 +14,19 @@ export default class BchdGrpcNetworkProvider implements NetworkProvider { this.client = new bchrpcClient(transport); } + private parseNFTCapabilities(capabilities: number): NFTCapabilities { + switch (capabilities & 0b1111) { + case 0: + return 'none'; + case 1: + return 'mutable'; + case 2: + return 'minting'; + default: + throw new Error(`Invalid NFT capabilities: ${capabilities}`); + } + } + async getUtxos(address: string): Promise { const { response } = await this.client.getAddressUnspentOutputs({ address, @@ -32,8 +47,7 @@ export default class BchdGrpcNetworkProvider implements NetworkProvider { amount: BigInt(utxo.cashToken.amount), nft: utxo.cashToken.commitment ? { - // TODO: parse bitfield - capability: utxo.cashToken.bitfield as any, + capability: this.parseNFTCapabilities(utxo.cashToken.bitfield[0]), commitment: binToHex(utxo.cashToken.commitment), } : undefined, From 9a493961472f29d5f6d2409204af172dc0b5f5d5 Mon Sep 17 00:00:00 2001 From: PHCitizen <75726261+PHCitizen@users.noreply.github.com> Date: Sun, 2 Feb 2025 23:50:56 +0000 Subject: [PATCH 5/7] refactor: rename to bitfield --- .../src/network/BchdGrpcNetworkProvider/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts b/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts index bd040884..6a161449 100644 --- a/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts +++ b/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts @@ -14,8 +14,9 @@ export default class BchdGrpcNetworkProvider implements NetworkProvider { this.client = new bchrpcClient(transport); } - private parseNFTCapabilities(capabilities: number): NFTCapabilities { - switch (capabilities & 0b1111) { + private parseNFTCapabilities(bitfield: number): NFTCapabilities { + const nftCapabilities = bitfield & 0b1111; + switch (nftCapabilities) { case 0: return 'none'; case 1: @@ -23,7 +24,7 @@ export default class BchdGrpcNetworkProvider implements NetworkProvider { case 2: return 'minting'; default: - throw new Error(`Invalid NFT capabilities: ${capabilities}`); + throw new Error(`Invalid NFT capabilities: ${nftCapabilities}`); } } From 096c08aa1f37e6857ba6e7674397fc6ec5e6fe16 Mon Sep 17 00:00:00 2001 From: PHCitizen <75726261+PHCitizen@users.noreply.github.com> Date: Sun, 23 Feb 2025 15:40:08 +0000 Subject: [PATCH 6/7] fix: generated import not using .js extension --- packages/cashscript/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cashscript/package.json b/packages/cashscript/package.json index 6138f251..917c078a 100644 --- a/packages/cashscript/package.json +++ b/packages/cashscript/package.json @@ -30,7 +30,7 @@ "test": "test" }, "scripts": { - "generate:bchd-grpc": "mkdir -p ./src/network/BchdGrpcNetworkProvider/generated && protoc --ts_out ./src/network/BchdGrpcNetworkProvider/generated --proto_path src/network/BchdGrpcNetworkProvider bchrpc.proto", + "generate:bchd-grpc": "cd ./src/network/BchdGrpcNetworkProvider && mkdir -p generated && protoc --ts_out ./generated --proto_path . bchrpc.proto && sed -i 's/bchrpc\";/bchrpc.js\";/' ./generated/bchrpc.client.ts", "build": "yarn clean && yarn compile", "build:test": "yarn clean:test && yarn compile:test", "clean": "rm -rf ./dist", From bcd50c9eac052252f8999af2d4ff82105708e057 Mon Sep 17 00:00:00 2001 From: PHCitizen <75726261+PHCitizen@users.noreply.github.com> Date: Sun, 2 Mar 2025 09:14:23 +0000 Subject: [PATCH 7/7] not undefined --- packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts b/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts index 6a161449..190b58be 100644 --- a/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts +++ b/packages/cashscript/src/network/BchdGrpcNetworkProvider/index.ts @@ -38,7 +38,6 @@ export default class BchdGrpcNetworkProvider implements NetworkProvider { const utxos = response.outputs.map( (utxo) => ({ - // TODO: undefined? txid: binToHex(utxo.outpoint!.hash.reverse()), vout: utxo.outpoint!.index, satoshis: utxo.value,