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' ] }