From dd1851eea510ab939e2bab6d7d8d0bbbcf23e09a Mon Sep 17 00:00:00 2001
From: Fuxing Loh <4266087+fuxingloh@users.noreply.github.com>
Date: Fri, 16 Apr 2021 20:37:06 +0800
Subject: [PATCH] rawtx.createRawTransaction, rawtx.signRawTransactionWithKey,
rawtx.testMempoolAccept, rawtx.sendRawTransaction (#127)
* completed 4 rpc in rawtx.ts
* added docs
---
.idea/dictionaries/fuxing.xml | 28 +-
.../__tests__/category/rawtx.test.ts | 278 ++++++++++++++++++
.../jellyfish-api-core/src/category/rawtx.ts | 209 +++++++++++++
packages/jellyfish-api-core/src/index.ts | 3 +
website/docs/jellyfish/api/rawtx.md | 121 ++++++++
website/sidebars.js | 1 +
6 files changed, 639 insertions(+), 1 deletion(-)
create mode 100644 packages/jellyfish-api-core/__tests__/category/rawtx.test.ts
create mode 100644 packages/jellyfish-api-core/src/category/rawtx.ts
create mode 100644 website/docs/jellyfish/api/rawtx.md
diff --git a/.idea/dictionaries/fuxing.xml b/.idea/dictionaries/fuxing.xml
index b308f9ed87..f71599a5ec 100644
--- a/.idea/dictionaries/fuxing.xml
+++ b/.idea/dictionaries/fuxing.xml
@@ -10,10 +10,13 @@
bayfrontmarinaheight
bcrt
bech
+ bestblock
+ bestblockhash
booland
boolor
canonbrother
chainparams
+ chainwork
checklocktimeverify
checkmultisig
checkmultisigverify
@@ -23,6 +26,7 @@
clarkequayheight
codeseparator
createmasternode
+ createrawtransaction
currentblocktx
currentblockweight
dakotacrescentheight
@@ -34,6 +38,7 @@
devnet
dockerode
dummypos
+ dumpprivkey
equalverify
fromaltstack
fullstackninja
@@ -41,6 +46,8 @@
generatetoaddress
getaddressinfo
getbalance
+ getblock
+ getblockchaininfo
getblockcount
getblockhash
getmintinginfo
@@ -48,12 +55,14 @@
getnewaddress
getreceivedbyaddress
gettransaction
+ gettxout
getunconfirmedbalance
greaterthan
greaterthanorequal
ifdup
importprivkey
infima
+ initialblockdownload
invalidopcode
isoperator
jsonrpc
@@ -61,6 +70,7 @@
lessthanorequal
libevent
listaccounts
+ locktime
logtimemicros
lshift
mainnet
@@ -68,9 +78,13 @@
masternodeid
masternodeoperator
masternodestate
+ mediantime
+ mempool
+ merkleroot
mintedblocks
nblocks
networkhashps
+ nextblockhash
nmasternode
nospv
notif
@@ -78,12 +92,14 @@
numequalverify
numnotequal
pooledtx
+ previousblockhash
prevout
prevouts
printtoconsole
priv
pubkey
pushdata
+ rawtx
regtest
rewardaddress
ripemd
@@ -94,26 +110,36 @@
rshift
secp
segwit
+ sendrawtransaction
sendtoaddress
sighash
sighashtype
signmessage
+ signrawtransactionwithkey
+ sigs
+ softforks
+ strippedsize
testcontainers
+ testmempoolaccept
thedoublejay
toaltstack
txid
txindex
+ txinwitness
txnotokens
uacomment
unpkg
utxo
+ utxos
utxostoaccount
varint
verif
+ verificationprogress
verifymessage
vernotif
vout
+ vsize
wpkh
-
\ No newline at end of file
+
diff --git a/packages/jellyfish-api-core/__tests__/category/rawtx.test.ts b/packages/jellyfish-api-core/__tests__/category/rawtx.test.ts
new file mode 100644
index 0000000000..57a8d51549
--- /dev/null
+++ b/packages/jellyfish-api-core/__tests__/category/rawtx.test.ts
@@ -0,0 +1,278 @@
+import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
+import { ContainerAdapterClient } from '../container_adapter_client'
+import { BigNumber } from '../../src'
+import { CreateRawTxOut, SigHashType, SignRawTxWithKeyResult, TestMempoolAcceptResult } from '../../src/category/rawtx'
+
+const container = new MasterNodeRegTestContainer()
+const client = new ContainerAdapterClient(container)
+
+beforeAll(async () => {
+ await container.start()
+ await container.waitForReady()
+ await container.waitForWalletCoinbaseMaturity()
+ await container.waitForWalletBalanceGTE(300)
+})
+
+afterAll(async () => {
+ await container.stop()
+})
+
+// From Address P2WPKH
+const input = {
+ bech32: 'bcrt1qykj5fsrne09yazx4n72ue4fwtpx8u65zac9zhn',
+ privKey: 'cQSsfYvYkK5tx3u1ByK2ywTTc9xJrREc1dd67ZrJqJUEMwgktPWN'
+}
+// To Address P2WPKH
+const output = {
+ bech32: 'bcrt1qf26rj8895uewxcfeuukhng5wqxmmpqp555z5a7',
+ privKey: 'cQbfHFbdJNhg3UGaBczir2m5D4hiFRVRKgoU8GJoxmu2gEhzqHtV'
+}
+
+describe('createRawTransaction', () => {
+ it('should createRawTransaction()', async () => {
+ const { txid } = await container.fundAddress(input.bech32, 10)
+ const inputs = [{ txid: txid, vout: 0 }]
+
+ const outputs: CreateRawTxOut = {}
+ outputs[output.bech32] = new BigNumber('5')
+
+ const unsigned: string = await client.rawtx.createRawTransaction(inputs, outputs)
+
+ // Version
+ expect(unsigned.substr(0, 8)).toBe('04000000')
+ // Vin
+ expect(unsigned.substr(8, 2)).toBe('01')
+ expect(unsigned.substr(10, 64)).toBe(
+ Buffer.from(txid, 'hex').reverse().toString('hex')
+ )
+ expect(unsigned.substr(74, 8)).toBe('00000000')
+ expect(unsigned.substr(82, 2)).toBe('00')
+ expect(unsigned.substr(84, 8)).toBe('ffffffff')
+ // Vout
+ expect(unsigned.substr(92, 2)).toBe('01')
+ expect(unsigned.substr(94, 16)).toBe('0065cd1d00000000')
+ expect(unsigned.substr(110, 2)).toBe('16')
+ expect(unsigned.substr(112, 2)).toBe('00') // OP_0
+ expect(unsigned.substr(114, 2)).toBe('14')
+ expect(unsigned.substr(116, 40)).toBe('4ab4391ce5a732e36139e72d79a28e01b7b08034') // PKH
+ expect(unsigned.substr(156, 2)).toBe('00') // DCT_ID
+ // LockTime
+ expect(unsigned.substr(158, 8)).toBe('00000000')
+
+ expect(unsigned.length).toBe(166)
+ })
+
+ it('should createRawTransaction() with locktime 1000', async () => {
+ const { txid } = await container.fundAddress(input.bech32, 10)
+ const inputs = [{ txid: txid, vout: 0 }]
+
+ const outputs: CreateRawTxOut = {}
+ outputs[output.bech32] = new BigNumber('5')
+
+ const unsigned: string = await client.rawtx.createRawTransaction(inputs, outputs, {
+ locktime: 1000
+ })
+
+ expect(unsigned.substr(0, 84)).toBe(
+ '0400000001' + Buffer.from(txid, 'hex').reverse().toString('hex') + '0000000000'
+ )
+ expect(unsigned.substr(84, 8)).toBe('feffffff')
+ expect(unsigned.substr(92, 66)).toBe('010065cd1d000000001600144ab4391ce5a732e36139e72d79a28e01b7b0803400')
+ expect(unsigned.substr(158, 8)).toBe('e8030000')
+ expect(unsigned.length).toBe(166)
+ })
+
+ it('should createRawTransaction() with replaceable = true', async () => {
+ const { txid } = await container.fundAddress(input.bech32, 10)
+ const inputs = [{ txid: txid, vout: 0 }]
+
+ const outputs: CreateRawTxOut = {}
+ outputs[output.bech32] = new BigNumber('5')
+
+ const unsigned: string = await client.rawtx.createRawTransaction(inputs, outputs, {
+ replaceable: true
+ })
+
+ expect(unsigned.substr(0, 84)).toBe(
+ '0400000001' + Buffer.from(txid, 'hex').reverse().toString('hex') + '0000000000'
+ )
+ expect(unsigned.substr(84, 8)).toBe('fdffffff')
+ expect(unsigned.substr(92, 74)).toBe('010065cd1d000000001600144ab4391ce5a732e36139e72d79a28e01b7b080340000000000')
+ expect(unsigned.length).toBe(166)
+ })
+})
+
+describe('signRawTransactionWithKey', () => {
+ it('should signRawTransactionWithKey() 10.0 to 5.0 with 5.0 as fee', async () => {
+ const { txid, vout } = await container.fundAddress(input.bech32, 10)
+ const inputs = [{ txid: txid, vout: vout }]
+
+ const outputs: CreateRawTxOut = {}
+ outputs[output.bech32] = new BigNumber('5')
+
+ const unsigned = await client.rawtx.createRawTransaction(inputs, outputs)
+ const signed = await client.rawtx.signRawTransactionWithKey(unsigned, [input.privKey])
+
+ expect(signed.complete).toBe(true)
+ expect(signed.hex.substr(0, 14)).toBe('04000000000101')
+ expect(signed.hex.substr(86, 88)).toBe('00ffffffff010065cd1d000000001600144ab4391ce5a732e36139e72d79a28e01b7b0803400024730440220')
+ expect(signed.hex.substr(306, 78)).toBe('012103987aec2e508e124468f0f07a836d185b329026e7aaf75be48cf12be8f18cbe8100000000')
+ expect(signed.hex.length).toBe(384)
+ })
+
+ // TODO(anyone): SignRawTxWithKeyPrevTx is not yet typed tested,
+ // for sake of time. It's out of my scope of work.
+
+ it('should signRawTransactionWithKey() 10.0 to 5.0 with 4.9 as change and 0.1 as fee', async () => {
+ const { txid, vout } = await container.fundAddress(input.bech32, 10)
+ const inputs = [{ txid: txid, vout: vout }]
+
+ const outputs: CreateRawTxOut = {}
+ outputs[output.bech32] = new BigNumber('5')
+ outputs[input.bech32] = new BigNumber('4.9')
+
+ const unsigned = await client.rawtx.createRawTransaction(inputs, outputs)
+ const signed = await client.rawtx.signRawTransactionWithKey(unsigned, [input.privKey])
+
+ expect(signed.complete).toBe(true)
+ expect(signed.hex.substr(0, 14)).toBe('04000000000101')
+ expect(signed.hex.substr(86, 152)).toBe('00ffffffff020065cd1d000000001600144ab4391ce5a732e36139e72d79a28e01b7b080340080ce341d0000000016001425a544c073cbca4e88d59f95ccd52e584c7e6a8200024730440220')
+ expect(signed.hex.substr(370, 78)).toBe('012103987aec2e508e124468f0f07a836d185b329026e7aaf75be48cf12be8f18cbe8100000000')
+ expect(signed.hex.length).toBe(448)
+ })
+
+ describe('signRawTransactionWithKeySigHashType', () => {
+ async function signRawTransactionWithKeySigHashType (type: SigHashType): Promise {
+ const inputs = [
+ await container.fundAddress(input.bech32, 10)
+ ]
+
+ const outputs: CreateRawTxOut = {}
+ outputs[output.bech32] = new BigNumber('5')
+ outputs[input.bech32] = new BigNumber('4.9')
+
+ const unsigned = await client.rawtx.createRawTransaction(inputs, outputs)
+ return await client.rawtx.signRawTransactionWithKey(unsigned, [input.privKey], {
+ sigHashType: type
+ })
+ }
+
+ it('should signRawTransactionWithKey() with SigHashType.ALL', async () => {
+ const signed = await signRawTransactionWithKeySigHashType(SigHashType.ALL)
+
+ expect(signed.complete).toBe(true)
+ expect(signed.hex.length).toBe(448)
+ })
+
+ it('should signRawTransactionWithKey() with SigHashType.NONE', async () => {
+ const signed = await signRawTransactionWithKeySigHashType(SigHashType.NONE)
+
+ expect(signed.complete).toBe(true)
+ expect(signed.hex.length).toBe(448)
+ })
+
+ it('should signRawTransactionWithKey() with SigHashType.SINGLE', async () => {
+ const signed = await signRawTransactionWithKeySigHashType(SigHashType.SINGLE)
+
+ expect(signed.complete).toBe(true)
+ expect(signed.hex.length).toBe(448)
+ })
+
+ it('should signRawTransactionWithKey() with SigHashType.ALL_ANYONECANPAY', async () => {
+ const signed = await signRawTransactionWithKeySigHashType(SigHashType.ALL_ANYONECANPAY)
+
+ expect(signed.complete).toBe(true)
+ expect(signed.hex.length).toBe(448)
+ })
+
+ it('should signRawTransactionWithKey() with SigHashType.NONE_ANYONECANPAY', async () => {
+ const signed = await signRawTransactionWithKeySigHashType(SigHashType.NONE_ANYONECANPAY)
+
+ expect(signed.complete).toBe(true)
+ expect(signed.hex.length).toBe(448)
+ })
+
+ it('should signRawTransactionWithKey() with SigHashType.SINGLE_ANYONECANPAY', async () => {
+ const signed = await signRawTransactionWithKeySigHashType(SigHashType.SINGLE_ANYONECANPAY)
+
+ expect(signed.complete).toBe(true)
+ expect(signed.hex.length).toBe(448)
+ })
+ })
+})
+
+describe('testMempoolAccept', () => {
+ it('testMempoolAccept() should fail with random hex', async () => {
+ const invalid = 'bf94838ced5a8313eb355c3bdd053cdbdbb3f9e0'
+ return await expect(client.rawtx.testMempoolAccept(invalid))
+ .rejects.toThrow('RpcApiError: \'TX decode failed\', code: -22')
+ })
+
+ it('testMempoolAccept() should fail due to missing-inputs', async () => {
+ const signed = '0400000000010193c90783761bf94838ced5a8313eb355c3bdd053cdbdbb3f9e0f3dbc3243609b0000000000ffffffff020065cd1d000000001600144ab4391ce5a732e36139e72d79a28e01b7b080340080ce341d0000000016001425a544c073cbca4e88d59f95ccd52e584c7e6a82000247304402201142c461b7b52323654710b14074928dd8e623d75141f9eb8c2132b7cb2d47c202202883fde993e1ecf0cf3955235522e9fe948b523b568d0e6b427f83c6f1b3efd9012103987aec2e508e124468f0f07a836d185b329026e7aaf75be48cf12be8f18cbe8100000000'
+ const result = await client.rawtx.testMempoolAccept(signed)
+
+ expect(result.txid).toBe('5749ad89256b50786a02d4527621a4fc7fa6acc5a3b289841112628ff3a4990a')
+ expect(result.allowed).toBe(false)
+ expect(result['reject-reason']).toBe('missing-inputs')
+ })
+
+ async function testMempoolAcceptFees (fees?: BigNumber): Promise {
+ const { txid, vout } = await container.fundAddress(input.bech32, 10)
+ const inputs = [{ txid: txid, vout: vout }]
+
+ const outputs: CreateRawTxOut = {}
+ outputs[output.bech32] = new BigNumber('9.5')
+
+ const unsigned = await client.rawtx.createRawTransaction(inputs, outputs)
+ const signed = await client.rawtx.signRawTransactionWithKey(unsigned, [input.privKey])
+ // 32 + 68 + 10 = 110 bytes
+ // 1000/100 * 0.5 = 4.54545
+ return await client.rawtx.testMempoolAccept(signed.hex, fees)
+ }
+
+ it('testMempoolAccept() should succeed with any fees', async () => {
+ const result = await testMempoolAcceptFees()
+ expect(result.allowed).toBe(true)
+ })
+
+ it('testMempoolAccept() should succeed with high fees rate', async () => {
+ const result = await testMempoolAcceptFees(new BigNumber('10.0'))
+ expect(result.allowed).toBe(true)
+ })
+
+ it('testMempoolAccept() should succeed just above expected fees', async () => {
+ const result = await testMempoolAcceptFees(new BigNumber('4.6'))
+ expect(result.allowed).toBe(true)
+ })
+
+ it('testMempoolAccept() should fail with low fee rate', async () => {
+ const result = await testMempoolAcceptFees(new BigNumber('4.5'))
+ expect(result.allowed).toBe(false)
+ expect(result['reject-reason']).toBe('256: absurdly-high-fee')
+ })
+
+ it('testMempoolAccept() should fail with extreme low fee rate', async () => {
+ const result = await testMempoolAcceptFees(new BigNumber('0.01'))
+ expect(result.allowed).toBe(false)
+ expect(result['reject-reason']).toBe('256: absurdly-high-fee')
+ })
+})
+
+describe('sendRawTransaction', () => {
+ it('should sendRawTransaction() and get rawtx and wait confirmations', async () => {
+ const inputs = [
+ await container.fundAddress(input.bech32, 10)
+ ]
+
+ const outputs: CreateRawTxOut = {}
+ outputs[output.bech32] = new BigNumber('9.9')
+
+ const unsigned = await client.rawtx.createRawTransaction(inputs, outputs)
+ const signed = await client.rawtx.signRawTransactionWithKey(unsigned, [input.privKey])
+ const txid = await client.rawtx.sendRawTransaction(signed.hex)
+
+ const tx = await container.call('getrawtransaction', [txid, true])
+ expect(tx.txid).toBe(txid)
+ })
+})
diff --git a/packages/jellyfish-api-core/src/category/rawtx.ts b/packages/jellyfish-api-core/src/category/rawtx.ts
new file mode 100644
index 0000000000..232864857a
--- /dev/null
+++ b/packages/jellyfish-api-core/src/category/rawtx.ts
@@ -0,0 +1,209 @@
+import { BigNumber, ApiClient } from '../.'
+
+export enum SigHashType {
+ ALL = 'ALL',
+ NONE = 'NONE',
+ SINGLE = 'SINGLE',
+ ALL_ANYONECANPAY = 'ALL|ANYONECANPAY',
+ NONE_ANYONECANPAY = 'NONE|ANYONECANPAY',
+ SINGLE_ANYONECANPAY = 'SINGLE|ANYONECANPAY',
+}
+
+/**
+ * RawTransaction RPCs for DeFi Blockchain
+ */
+export class RawTx {
+ private readonly client: ApiClient
+
+ constructor (client: ApiClient) {
+ this.client = client
+ }
+
+ /**
+ * Create a transaction spending the given inputs and creating new outputs that returns a hex-encoded raw transaction.
+ * Note that the transaction's inputs are not signed, and it is not stored in the wallet or transmitted to the network.
+ *
+ * @param {CreateRawTxIn[]} inputs array of inputs
+ * @param {CreateRawTxOut[]} outputs array with outputs
+ * @param {CreateRawTxOptions} options
+ * @param {number} options.locktime Non-0 value also locktime-activates inputs
+ * @param {boolean} options.replaceable Marks this transaction as BIP125-replaceable
+ * @return {Promise} hex string of the transaction
+ */
+ async createRawTransaction (
+ inputs: CreateRawTxIn[],
+ outputs: CreateRawTxOut,
+ options: CreateRawTxOptions = {}
+ ): Promise {
+ const { locktime = 0, replaceable = false } = options
+
+ return await this.client.call('createrawtransaction', [
+ inputs, outputs, locktime, replaceable
+ ], 'number')
+ }
+
+ /**
+ * Sign inputs for raw transaction (serialized, hex-encoded), Providing an array of base58-encoded private keys that
+ * will be the keys used to sign the transaction. An optional array of previous transaction outputs that this
+ * transaction depends on but may not yet be in the blockchain.
+ *
+ * @param {string} rawTx unsigned raw transaction
+ * @param {string[]} privKeys array of base58-encoded private keys for signing (WIF)
+ * @param {SignRawTxWithKeyOptions} options
+ * @param {SigHashType} options.sigHashType the signature hash type to use
+ * @param {SignRawTxWithKeyPrevTx[]} options.prevTxs array of previous dependent transaction outputs
+ * @return {Promise}
+ */
+ async signRawTransactionWithKey (
+ rawTx: string,
+ privKeys: string[],
+ options: SignRawTxWithKeyOptions = {}
+ ): Promise {
+ const { prevTxs = [], sigHashType = SigHashType.ALL } = options
+
+ return await this.client.call('signrawtransactionwithkey', [
+ rawTx, privKeys, prevTxs, sigHashType
+ ], 'number')
+ }
+
+ /**
+ * Returns result of mempool acceptance tests indicating if raw transaction would be accepted by mempool.
+ * This checks if the transaction violates the consensus or policy rules. The fee rate is expressed is DFI/kB,
+ * using the vSize of the transaction.
+ *
+ * @param {string} signedTx signed raw transaction
+ * @param {BigNumber} maxFeeRate Reject transactions whose fee rate is higher than the specified value. in DFI/kB
+ * @return {Promise} transaction mempool accept result
+ * @see sendRawTransaction
+ * @see createRawTransaction
+ * @see signRawTransactionWithKey
+ */
+ async testMempoolAccept (
+ signedTx: string,
+ maxFeeRate: BigNumber = new BigNumber('0')
+ ): Promise {
+ const results: TestMempoolAcceptResult[] = await this.client.call('testmempoolaccept', [
+ [signedTx], maxFeeRate
+ ], 'number')
+ return results[0]
+ }
+
+ /**
+ * Submit a raw transaction (serialized, hex-encoded) to the connected node and network. The transaction will be sent
+ * unconditionally to all peers, so using this for manual rebroadcast may degrade privacy by leaking the transaction's
+ * origin, as nodes will normally not rebroadcast non-wallet transactions already in their mempool.
+ *
+ * @param {string} signedTx signed raw transaction
+ * @param {BigNumber} maxFeeRate Reject transactions whose fee rate is higher than the specified value. in DFI/kB
+ * @return {Promise} transaction hash in hex
+ * @see testMempoolAccept
+ * @see createRawTransaction
+ * @see signRawTransactionWithKey
+ */
+ async sendRawTransaction (
+ signedTx: string, maxFeeRate:
+ BigNumber = new BigNumber('0')
+ ): Promise {
+ return await this.client.call('sendrawtransaction', [
+ signedTx, maxFeeRate
+ ], 'number')
+ }
+}
+
+export interface CreateRawTxOptions {
+ locktime?: number
+ replaceable?: boolean
+}
+
+export interface CreateRawTxIn {
+ txid: string
+ vout: number
+ sequence?: number
+}
+
+export interface CreateRawTxOut {
+ [address: string]: BigNumber
+}
+
+export interface SignRawTxWithKeyOptions {
+ prevTxs?: SignRawTxWithKeyPrevTx[]
+ sigHashType?: SigHashType
+}
+
+export interface SignRawTxWithKeyPrevTx {
+ /**
+ * The transaction id
+ */
+ txid: string
+ /**
+ * The output number
+ */
+ vout: number
+ /**
+ * Pubkey
+ */
+ scriptPubKey: string
+ /**
+ * Required for P2SH or P2WSH
+ */
+ redeemScript?: string
+ /**
+ * Required for P2WSH or P2SH-P2WSH witness script
+ */
+ witnessScript?: string
+ /**
+ * Required for segwit inputs
+ */
+ amount?: BigNumber
+}
+
+export interface SignRawTxWithKeyResult {
+ /**
+ * The hex-encoded raw transaction with signature(s)
+ */
+ hex: string
+ /**
+ * If the transaction has a complete set of signatures
+ */
+ complete: boolean
+ /**
+ * Script verification errors (if there are any)
+ */
+ errors: Array<{
+ /**
+ * The hash of the referenced, previous transaction
+ */
+ txid: string
+ /**
+ * The index of the output to spent and used as input
+ */
+ vout: number
+ /**
+ * The hex-encoded signature script
+ */
+ scriptSig: string
+ /**
+ * Script sequence number
+ */
+ sequence: number
+ /**
+ * Verification or signing error related to the input
+ */
+ error: string
+ }>
+}
+
+export interface TestMempoolAcceptResult {
+ /**
+ * The transaction hash in hex
+ */
+ txid: string
+ /**
+ * If the mempool allows this tx to be inserted
+ */
+ allowed: boolean
+ /**
+ * Rejection string, only present when 'allowed' is false
+ */
+ 'reject-reason'?: string
+}
diff --git a/packages/jellyfish-api-core/src/index.ts b/packages/jellyfish-api-core/src/index.ts
index b5086e61cc..491f363415 100644
--- a/packages/jellyfish-api-core/src/index.ts
+++ b/packages/jellyfish-api-core/src/index.ts
@@ -1,11 +1,13 @@
import { Precision, PrecisionPath } from '@defichain/jellyfish-json'
import { Blockchain } from './category/blockchain'
import { Mining } from './category/mining'
+import { RawTx } from './category/rawtx'
import { Wallet } from './category/wallet'
export * from '@defichain/jellyfish-json'
export * from './category/blockchain'
export * from './category/mining'
+export * as rawtx from './category/rawtx'
export * from './category/wallet'
/**
@@ -14,6 +16,7 @@ export * from './category/wallet'
export abstract class ApiClient {
public readonly blockchain = new Blockchain(this)
public readonly mining = new Mining(this)
+ public readonly rawtx = new RawTx(this)
public readonly wallet = new Wallet(this)
/**
diff --git a/website/docs/jellyfish/api/rawtx.md b/website/docs/jellyfish/api/rawtx.md
new file mode 100644
index 0000000000..504bbe564d
--- /dev/null
+++ b/website/docs/jellyfish/api/rawtx.md
@@ -0,0 +1,121 @@
+---
+id: rawtx
+title: Raw Transaction API
+sidebar_label: RawTx API
+slug: /jellyfish/api/rawtx
+---
+
+```js
+import {Client} from '@defichain/jellyfish'
+const client = new Client()
+
+// Using client.rawtx.
+const something = await client.rawtx.method()
+```
+
+## createRawTransaction
+
+Create a transaction spending the given inputs and creating new outputs that returns a hex-encoded raw transaction.
+Note that the transaction's inputs are not signed, and it is not stored in the wallet or transmitted to the network.
+
+```ts title="client.rawtx.createRawTransaction()"
+interface rawtx {
+ createRawTransaction (
+ inputs: CreateRawTxIn[],
+ outputs: CreateRawTxOut,
+ options: CreateRawTxOptions = {}
+ ): Promise
+}
+
+interface CreateRawTxOptions {
+ locktime?: number
+ replaceable?: boolean
+}
+
+interface CreateRawTxIn {
+ txid: string
+ vout: number
+ sequence?: number
+}
+
+interface CreateRawTxOut {
+ [address: string]: BigNumber
+}
+```
+
+
+## signRawTransactionWithKey
+
+Sign inputs for raw transaction (serialized, hex-encoded), Providing an array of base58-encoded private keys that will
+be the keys used to sign the transaction. An optional array of previous transaction outputs that this transaction
+depends on but may not yet be in the blockchain.
+
+```ts title="client.rawtx.signRawTransactionWithKey()"
+interface rawtx {
+ signRawTransactionWithKey (
+ rawTx: string,
+ privKeys: string[],
+ options: SignRawTxWithKeyOptions = {}
+ ): Promise
+}
+
+interface SignRawTxWithKeyOptions {
+ prevTxs?: SignRawTxWithKeyPrevTx[]
+ sigHashType?: SigHashType
+}
+
+interface SignRawTxWithKeyPrevTx {
+ txid: string
+ vout: number
+ scriptPubKey: string
+ redeemScript?: string
+ witnessScript?: string
+ amount?: BigNumber
+}
+
+enum SigHashType {
+ ALL = 'ALL',
+ NONE = 'NONE',
+ SINGLE = 'SINGLE',
+ ALL_ANYONECANPAY = 'ALL|ANYONECANPAY',
+ NONE_ANYONECANPAY = 'NONE|ANYONECANPAY',
+ SINGLE_ANYONECANPAY = 'SINGLE|ANYONECANPAY',
+}
+```
+
+## testMempoolAccept
+
+Returns result of mempool acceptance tests indicating if raw transaction would be accepted by mempool.
+This checks if the transaction violates the consensus or policy rules. The fee rate is expressed is DFI/kB, using the
+vSize of the transaction.
+
+```ts title="client.rawtx.testMempoolAccept()"
+interface rawtx {
+ testMempoolAccept (
+ signedTx: string,
+ maxFeeRate: BigNumber = new BigNumber('0')
+ ): Promise
+}
+
+interface TestMempoolAcceptResult {
+ txid: string
+ allowed: boolean
+ 'reject-reason'?: string
+}
+
+```
+
+## sendRawTransaction
+
+Submit a raw transaction (serialized, hex-encoded) to the connected node and network. The transaction will be sent
+unconditionally to all peers, so using this for manual rebroadcast may degrade privacy by leaking the transaction's
+origin, as nodes will normally not rebroadcast non-wallet transactions already in their mempool.
+
+```ts title="client.rawtx.sendRawTransaction()"
+interface rawtx {
+ sendRawTransaction (
+ signedTx: string,
+ maxFeeRate: BigNumber = new BigNumber('0')
+ ): Promise
+}
+```
diff --git a/website/sidebars.js b/website/sidebars.js
index d0ee0e9559..f7ac3e13df 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -10,6 +10,7 @@ module.exports = {
items: [
'jellyfish/api/blockchain',
'jellyfish/api/mining',
+ 'jellyfish/api/rawtx',
'jellyfish/api/wallet'
]
}