caver-js
Version:
caver-js is a JavaScript API library that allows developers to interact with a Kaia node
946 lines (757 loc) • 70.6 kB
JavaScript
/*
Copyright 2020 The caver-js Authors
This file is part of the caver-js library.
The caver-js library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The caver-js library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the caver-js. If not, see <http://www.gnu.org/licenses/>.
*/
const chai = require('chai')
const sinon = require('sinon')
const sinonChai = require('sinon-chai')
const chaiAsPromised = require('chai-as-promised')
const RLP = require('eth-lib/lib/rlp')
chai.use(chaiAsPromised)
chai.use(sinonChai)
const expect = chai.expect
const { propertiesForUnnecessary } = require('../utils')
const testRPCURL = require('../../testrpc')
const Caver = require('../../../index')
const Keyring = require('../../../packages/caver-wallet/src/keyring/keyringFactory')
const SingleKeyring = require('../../../packages/caver-wallet/src/keyring/singleKeyring')
const TransactionHasher = require('../../../packages/caver-transaction/src/transactionHasher/transactionHasher')
const { generateRoleBasedKeyring, checkSignature, checkFeePayerSignature } = require('../utils')
let caver
let sender
let roleBasedKeyring
const txWithExpectedValues = {}
const sandbox = sinon.createSandbox()
const input =
'0xf8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006'
before(() => {
caver = new Caver(testRPCURL)
sender = caver.wallet.add(caver.wallet.keyring.generate())
roleBasedKeyring = generateRoleBasedKeyring([3, 3, 3])
txWithExpectedValues.tx = {
from: '0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B',
gas: '0x174876e800',
gasPrice: '0x5d21dba00',
chainId: '0x1',
nonce: 17,
input,
signatures: [
[
'0x26',
'0xafe41edc9cce1185ab9065ca7dbfb89ab5c7bde3602a659aa258324124644142',
'0x317848698248ba7cc057b8f0dd19a27b52ef904d29cb72823100f1ed18ba2bb3',
],
],
feePayer: '0x33f524631e573329a550296F595c820D6c65213f',
feePayerSignatures: [
[
'0x25',
'0x309e46db21a1bf7bfdae24d9192aca69516d6a341ecce8971fc69cff481cee76',
'0x4b939bf7384c4f919880307323a5e36d4d6e029bae1887a43332710cdd48f174',
],
],
}
txWithExpectedValues.rlpEncodingForSigning =
'0xf8dbb8d6f8d449118505d21dba0085174876e80094a94f5374fce5edbc8e2a8697c15331677e6ebf0bb8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006018080'
txWithExpectedValues.rlpEncodingForFeePayerSigning =
'0xf8f0b8d6f8d449118505d21dba0085174876e80094a94f5374fce5edbc8e2a8697c15331677e6ebf0bb8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000000040580069433f524631e573329a550296f595c820d6c65213f018080'
txWithExpectedValues.senderTxHash = '0x4f5c00ea8f6346baa7d4400dfefd72efa5ec219561ebcebed7be8a2b79d52bcd'
txWithExpectedValues.transactionHash = '0xecf1ec12937065617f9b3cd07570452bfdb75dc36404c4f37f78995c6dc462af'
txWithExpectedValues.rlpEncoding =
'0x49f90176118505d21dba0085174876e80094a94f5374fce5edbc8e2a8697c15331677e6ebf0bb8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006f845f84326a0afe41edc9cce1185ab9065ca7dbfb89ab5c7bde3602a659aa258324124644142a0317848698248ba7cc057b8f0dd19a27b52ef904d29cb72823100f1ed18ba2bb39433f524631e573329a550296f595c820d6c65213ff845f84325a0309e46db21a1bf7bfdae24d9192aca69516d6a341ecce8971fc69cff481cee76a04b939bf7384c4f919880307323a5e36d4d6e029bae1887a43332710cdd48f174'
})
describe('TxTypeFeeDelegatedChainDataAnchoring', () => {
let transactionObj
let getGasPriceSpy
let getHeaderSpy
let getNonceSpy
let getChainIdSpy
beforeEach(() => {
transactionObj = {
from: sender.address,
gas: '0x15f90',
input,
}
getGasPriceSpy = sandbox.stub(caver.transaction.klaytnCall, 'getGasPrice')
getGasPriceSpy.returns('0x5d21dba00')
getHeaderSpy = sandbox.stub(caver.transaction.klaytnCall, 'getHeaderByNumber')
getHeaderSpy.returns({ baseFeePerGas: '0x5d21dba00' })
getNonceSpy = sandbox.stub(caver.transaction.klaytnCall, 'getTransactionCount')
getNonceSpy.returns('0x3a')
getChainIdSpy = sandbox.stub(caver.transaction.klaytnCall, 'getChainId')
getChainIdSpy.returns('0x7e3')
})
afterEach(() => {
sandbox.restore()
})
context('create feeDelegatedChainDataAnchoring instance', () => {
it('CAVERJS-UNIT-TRANSACTIONFD-442: If feeDelegatedChainDataAnchoring not define from, return error', () => {
delete transactionObj.from
const expectedError = '"from" is missing'
expect(() => caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTIONFD-443: If feeDelegatedChainDataAnchoring not define gas, return error', () => {
delete transactionObj.gas
const expectedError = '"gas" is missing'
expect(() => caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTIONFD-444: If feeDelegatedChainDataAnchoring not define input, return error', () => {
delete transactionObj.input
const expectedError = '"input" is missing'
expect(() => caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTIONFD-445: If feeDelegatedChainDataAnchoring define from property with invalid address, return error', () => {
transactionObj.from = 'invalid'
const expectedError = `Invalid address of from: ${transactionObj.from}`
expect(() => caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTIONFD-446: If feeDelegatedChainDataAnchoring define feePayer property with invalid address, return error', () => {
transactionObj.feePayer = 'invalid'
const expectedError = `Invalid address of fee payer: ${transactionObj.feePayer}`
expect(() => caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTIONFD-447: If feeDelegatedChainDataAnchoring define feePayerSignatures property without feePayer, return error', () => {
transactionObj.feePayerSignatures = [
[
'0x26',
'0xf45cf8d7f88c08e6b6ec0b3b562f34ca94283e4689021987abb6b0772ddfd80a',
'0x298fe2c5aeabb6a518f4cbb5ff39631a5d88be505d3923374f65fdcf63c2955b',
],
]
const expectedError = '"feePayer" is missing: feePayer must be defined with feePayerSignatures.'
expect(() => caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTIONFD-448: If feeDelegatedChainDataAnchoring define unnecessary property, return error', () => {
const unnecessaries = [
propertiesForUnnecessary.to,
propertiesForUnnecessary.value,
propertiesForUnnecessary.codeFormat,
propertiesForUnnecessary.failKey,
propertiesForUnnecessary.feeRatio,
propertiesForUnnecessary.account,
propertiesForUnnecessary.key,
propertiesForUnnecessary.legacyKey,
propertiesForUnnecessary.publicKey,
propertiesForUnnecessary.failKey,
propertiesForUnnecessary.multisig,
propertiesForUnnecessary.roleTransactionKey,
propertiesForUnnecessary.roleAccountUpdateKey,
propertiesForUnnecessary.roleFeePayerKey,
propertiesForUnnecessary.humanReadable,
propertiesForUnnecessary.accessList,
propertiesForUnnecessary.maxPriorityFeePerGas,
propertiesForUnnecessary.maxFeePerGas,
]
for (let i = 0; i < unnecessaries.length; i++) {
if (i > 0) delete transactionObj[unnecessaries[i - 1].name]
transactionObj[unnecessaries[i].name] = unnecessaries[i].value
const expectedError = `"${unnecessaries[i].name}" cannot be used with ${caver.transaction.type.TxTypeFeeDelegatedChainDataAnchoring} transaction`
expect(() => caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)).to.throw(expectedError)
}
})
})
context('feeDelegatedChainDataAnchoring.getRLPEncoding', () => {
it('CAVERJS-UNIT-TRANSACTIONFD-449: Returns RLP-encoded string', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(txWithExpectedValues.tx)
expect(tx.getRLPEncoding()).to.equal(txWithExpectedValues.rlpEncoding)
})
it('CAVERJS-UNIT-TRANSACTIONFD-450: getRLPEncoding should throw error when nonce is undefined', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(txWithExpectedValues.tx)
delete tx._nonce
const expectedError = `nonce is undefined. Define nonce in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getRLPEncoding()).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTIONFD-451: getRLPEncoding should throw error when gasPrice is undefined', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(txWithExpectedValues.tx)
delete tx._gasPrice
const expectedError = `gasPrice is undefined. Define gasPrice in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getRLPEncoding()).to.throw(expectedError)
})
})
context('feeDelegatedChainDataAnchoring.sign', () => {
const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550'
let fillTransactionSpy
let createFromPrivateKeySpy
let senderSignSpy
let appendSignaturesSpy
let hasherSpy
let tx
beforeEach(() => {
tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
fillTransactionSpy = sandbox.spy(tx, 'fillTransaction')
createFromPrivateKeySpy = sandbox.spy(Keyring, 'createFromPrivateKey')
senderSignSpy = sandbox.spy(sender, 'sign')
appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures')
hasherSpy = sandbox.stub(TransactionHasher, 'getHashForSignature')
hasherSpy.returns(txHash)
})
afterEach(() => {
sandbox.restore()
})
function checkFunctionCall(customHasher = false) {
expect(fillTransactionSpy).to.have.been.calledOnce
expect(appendSignaturesSpy).to.have.been.calledOnce
if (!customHasher) expect(hasherSpy).to.have.been.calledWith(tx)
}
it('CAVERJS-UNIT-TRANSACTIONFD-453: input: keyring. should sign transaction.', async () => {
await tx.sign(sender)
checkFunctionCall()
checkSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(senderSignSpy).to.have.been.calledWith(txHash, '0x7e3', 0, undefined)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-454: input: private key string. should sign transaction.', async () => {
const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign')
await tx.sign(sender.key.privateKey)
checkFunctionCall()
checkSignature(tx)
expect(createFromPrivateKeySpy).to.have.been.calledOnce
expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 0, undefined)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-455: input: KlaytnWalletKey. should sign transaction.', async () => {
const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign')
await tx.sign(sender.getKlaytnWalletKey())
checkFunctionCall()
checkSignature(tx)
expect(createFromPrivateKeySpy).to.have.been.calledOnce
expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 0, undefined)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-456: input: keyring, index. should sign transaction with specific index.', async () => {
const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign')
tx.from = roleBasedKeyring.address
await tx.sign(roleBasedKeyring, 1)
checkFunctionCall()
checkSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(roleBasedSignSpy).to.have.been.calledWith(txHash, '0x7e3', 0, 1)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-457: input: keyring, custom hasher. should use custom hasher.', async () => {
const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae'
const customHasher = () => hashForCustomHasher
await tx.sign(sender, customHasher)
checkFunctionCall(true)
checkSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(senderSignSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 0, undefined)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-458: input: keyring, index, custom hasher. should use custom hasher when sign transaction.', async () => {
const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae'
const customHasher = () => hashForCustomHasher
const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign')
tx.from = roleBasedKeyring.address
await tx.sign(roleBasedKeyring, 1, customHasher)
checkFunctionCall(true)
checkSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(roleBasedSignSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 0, 1)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-459: input: keyring. should throw error when from is different.', async () => {
transactionObj.from = roleBasedKeyring.address
tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const expectedError = `The from address of the transaction is different with the address of the keyring to use.`
await expect(tx.sign(sender)).to.be.rejectedWith(expectedError)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-460: input: rolebased keyring, index out of range. should throw error.', async () => {
transactionObj.from = roleBasedKeyring.address
tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const expectedError = `Invalid index(10): index must be less than the length of keys(${roleBasedKeyring.keys[0].length}).`
await expect(tx.sign(roleBasedKeyring, 10)).to.be.rejectedWith(expectedError)
}).timeout(200000)
})
context('feeDelegatedChainDataAnchoring.signAsFeePayer', () => {
const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550'
let fillTransactionSpy
let createFromPrivateKeySpy
let senderSignSpy
let appendSignaturesSpy
let hasherSpy
let tx
beforeEach(() => {
tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
tx.feePayer = sender.address
fillTransactionSpy = sandbox.spy(tx, 'fillTransaction')
createFromPrivateKeySpy = sandbox.spy(Keyring, 'createFromPrivateKey')
senderSignSpy = sandbox.spy(sender, 'sign')
appendSignaturesSpy = sandbox.spy(tx, 'appendFeePayerSignatures')
hasherSpy = sandbox.stub(TransactionHasher, 'getHashForFeePayerSignature')
hasherSpy.returns(txHash)
})
afterEach(() => {
sandbox.restore()
})
function checkFunctionCall(customHasher = false) {
expect(fillTransactionSpy).to.have.been.calledOnce
expect(appendSignaturesSpy).to.have.been.calledOnce
if (!customHasher) expect(hasherSpy).to.have.been.calledWith(tx)
}
it('CAVERJS-UNIT-TRANSACTIONFD-461: input: keyring. If feePayer is not defined, should be set with keyring address.', async () => {
tx.feePayer = '0x'
await tx.signAsFeePayer(sender)
expect(tx.feePayer.toLowerCase()).to.equal(sender.address.toLowerCase())
checkFunctionCall()
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(senderSignSpy).to.have.been.calledWith(txHash, '0x7e3', 2, undefined)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-462: input: keyring. should sign transaction.', async () => {
await tx.signAsFeePayer(sender)
checkFunctionCall()
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(senderSignSpy).to.have.been.calledWith(txHash, '0x7e3', 2, undefined)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-463: input: private key string. should sign transaction.', async () => {
const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign')
await tx.signAsFeePayer(sender.key.privateKey)
checkFunctionCall()
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).to.have.been.calledOnce
expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 2, undefined)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-464: input: KlaytnWalletKey. should sign transaction.', async () => {
const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign')
await tx.signAsFeePayer(sender.getKlaytnWalletKey())
checkFunctionCall()
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).to.have.been.calledOnce
expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 2, undefined)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-465: input: keyring, index. should sign transaction with specific index.', async () => {
const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign')
tx.feePayer = roleBasedKeyring.address
await tx.signAsFeePayer(roleBasedKeyring, 1)
checkFunctionCall()
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(roleBasedSignSpy).to.have.been.calledWith(txHash, '0x7e3', 2, 1)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-466: input: keyring, custom hasher. should use custom hasher.', async () => {
const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae'
const customHasher = () => hashForCustomHasher
const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign')
tx.feePayer = roleBasedKeyring.address
await tx.signAsFeePayer(roleBasedKeyring, customHasher)
checkFunctionCall(true)
checkFeePayerSignature(tx, { expectedLength: roleBasedKeyring.keys[2].length })
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(roleBasedSignSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 2, undefined)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-467: input: keyring, index, custom hasher. should use custom hasher when sign transaction.', async () => {
const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae'
const customHasher = () => hashForCustomHasher
const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign')
tx.feePayer = roleBasedKeyring.address
await tx.signAsFeePayer(roleBasedKeyring, 1, customHasher)
checkFunctionCall(true)
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(roleBasedSignSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 2, 1)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-468: input: keyring. should throw error when feePayer is different.', async () => {
tx.feePayer = roleBasedKeyring.address
const expectedError = `The feePayer address of the transaction is different with the address of the keyring to use.`
await expect(tx.signAsFeePayer(sender)).to.be.rejectedWith(expectedError)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-469: input: rolebased keyring, index out of range. should throw error.', async () => {
transactionObj.from = roleBasedKeyring.address
tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const expectedError = `Invalid index(10): index must be less than the length of keys(${roleBasedKeyring.keys[0].length}).`
await expect(tx.signAsFeePayer(roleBasedKeyring, 10)).to.be.rejectedWith(expectedError)
}).timeout(200000)
})
context('feeDelegatedChainDataAnchoring.sign with multiple keys', () => {
const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550'
let fillTransactionSpy
let createFromPrivateKeySpy
let senderSignWithKeysSpy
let appendSignaturesSpy
let hasherSpy
let tx
beforeEach(() => {
tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
fillTransactionSpy = sandbox.spy(tx, 'fillTransaction')
createFromPrivateKeySpy = sandbox.spy(Keyring, 'createFromPrivateKey')
senderSignWithKeysSpy = sandbox.spy(sender, 'sign')
appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures')
hasherSpy = sandbox.stub(TransactionHasher, 'getHashForSignature')
hasherSpy.returns(txHash)
})
afterEach(() => {
sandbox.restore()
})
function checkFunctionCall(customHasher = false) {
expect(fillTransactionSpy).to.have.been.calledOnce
expect(appendSignaturesSpy).to.have.been.calledOnce
if (!customHasher) expect(hasherSpy).to.have.been.calledWith(tx)
}
it('CAVERJS-UNIT-TRANSACTIONFD-470: input: keyring. should sign transaction.', async () => {
await tx.sign(sender)
checkFunctionCall()
checkSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(senderSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 0)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-471: input: private key string. should sign transaction.', async () => {
const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign')
await tx.sign(sender.key.privateKey)
checkFunctionCall()
checkSignature(tx)
expect(createFromPrivateKeySpy).to.have.been.calledOnce
expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 0)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-472: input: KlaytnWalletKey. should sign transaction.', async () => {
const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign')
await tx.sign(sender.getKlaytnWalletKey())
checkFunctionCall()
checkSignature(tx)
expect(createFromPrivateKeySpy).to.have.been.calledOnce
expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 0)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-473: input: keyring, custom hasher. should use custom hasher when sign transaction.', async () => {
const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae'
const customHasher = () => hashForCustomHasher
await tx.sign(sender, customHasher)
checkFunctionCall(true)
checkSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(senderSignWithKeysSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 0)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-474: input: keyring. should throw error when from is different.', async () => {
transactionObj.from = roleBasedKeyring.address
tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const expectedError = `The from address of the transaction is different with the address of the keyring to use.`
await expect(tx.sign(sender)).to.be.rejectedWith(expectedError)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-475: input: roleBased keyring. should sign with multiple keys and append signatures', async () => {
const roleBasedSignWithKeysSpy = sandbox.spy(roleBasedKeyring, 'sign')
tx.from = roleBasedKeyring.address
await tx.sign(roleBasedKeyring)
checkFunctionCall(true)
checkSignature(tx, { expectedLength: roleBasedKeyring.keys[0].length })
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(roleBasedSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 0)
}).timeout(200000)
})
context('feeDelegatedChainDataAnchoring.signAsFeePayer with multiple keys', () => {
const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550'
let fillTransactionSpy
let createFromPrivateKeySpy
let senderSignWithKeysSpy
let appendSignaturesSpy
let hasherSpy
let tx
beforeEach(() => {
tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
fillTransactionSpy = sandbox.spy(tx, 'fillTransaction')
createFromPrivateKeySpy = sandbox.spy(Keyring, 'createFromPrivateKey')
senderSignWithKeysSpy = sandbox.spy(sender, 'sign')
appendSignaturesSpy = sandbox.spy(tx, 'appendFeePayerSignatures')
hasherSpy = sandbox.stub(TransactionHasher, 'getHashForFeePayerSignature')
hasherSpy.returns(txHash)
})
afterEach(() => {
sandbox.restore()
})
function checkFunctionCall(customHasher = false) {
expect(fillTransactionSpy).to.have.been.calledOnce
expect(appendSignaturesSpy).to.have.been.calledOnce
if (!customHasher) expect(hasherSpy).to.have.been.calledWith(tx)
}
it('CAVERJS-UNIT-TRANSACTIONFD-476: input: keyring. If feePayer is not defined, should be set with keyring address.', async () => {
tx.feePayer = '0x'
await tx.signAsFeePayer(sender)
checkFunctionCall()
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(senderSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 2)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-477: input: keyring. should sign transaction.', async () => {
await tx.signAsFeePayer(sender)
checkFunctionCall()
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(senderSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 2)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-478: input: private key string. should sign transaction.', async () => {
const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign')
await tx.signAsFeePayer(sender.key.privateKey)
checkFunctionCall()
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).to.have.been.calledOnce
expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 2)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-479: input: KlaytnWalletKey. should sign transaction.', async () => {
const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign')
await tx.signAsFeePayer(sender.getKlaytnWalletKey())
checkFunctionCall()
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).to.have.been.calledOnce
expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 2)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-480: input: keyring, custom hasher. should use custom hasher when sign transaction.', async () => {
const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae'
const customHasher = () => hashForCustomHasher
await tx.signAsFeePayer(sender, customHasher)
checkFunctionCall(true)
checkFeePayerSignature(tx)
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(senderSignWithKeysSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 2)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-481: input: keyring. should throw error when feePayer is different.', async () => {
tx.feePayer = roleBasedKeyring.address
const expectedError = `The feePayer address of the transaction is different with the address of the keyring to use.`
await expect(tx.signAsFeePayer(sender)).to.be.rejectedWith(expectedError)
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTIONFD-482: input: roleBased keyring. should sign with multiple keys and append signatures', async () => {
const roleBasedSignWithKeysSpy = sandbox.spy(roleBasedKeyring, 'sign')
tx.feePayer = roleBasedKeyring.address
await tx.signAsFeePayer(roleBasedKeyring)
checkFunctionCall(true)
checkFeePayerSignature(tx, { expectedLength: roleBasedKeyring.keys[2].length })
expect(createFromPrivateKeySpy).not.to.have.been.calledOnce
expect(roleBasedSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 2)
}).timeout(200000)
})
context('feeDelegatedChainDataAnchoring.appendSignatures', () => {
afterEach(() => {
sandbox.restore()
})
it('CAVERJS-UNIT-TRANSACTIONFD-483: If signatures is empty, appendSignatures append signatures in transaction', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const sig = [
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
]
tx.appendSignatures(sig)
checkSignature(tx)
})
it('CAVERJS-UNIT-TRANSACTIONFD-484: If signatures is empty, appendSignatures append signatures with two-dimensional signature array', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const sig = [
[
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
],
]
tx.appendSignatures(sig)
checkSignature(tx)
})
it('CAVERJS-UNIT-TRANSACTIONFD-485: If signatures is not empty, appendSignatures should append signatures', () => {
transactionObj.signatures = [
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
]
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const sig = [
'0x0fea',
'0x7a5011b41cfcb6270af1b5f8aeac8aeabb1edb436f028261b5add564de694700',
'0x23ac51660b8b421bf732ef8148d0d4f19d5e29cb97be6bccb5ae505ebe89eb4a',
]
tx.appendSignatures(sig)
checkSignature(tx, { expectedLength: 2 })
})
it('CAVERJS-UNIT-TRANSACTIONFD-486: appendSignatures should append multiple signatures', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const sig = [
[
'0x0fea',
'0xbde66cceed35a576010966338b7ded961f2c160c96f928e193b47aaf4480aa07',
'0x546eb193ec138523b7fd34c4f12a1a04d0f74470e8f3bbe91ce0b4ec16e7f0d2',
],
[
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
],
]
tx.appendSignatures(sig)
checkSignature(tx, { expectedLength: 2 })
})
})
context('feeDelegatedChainDataAnchoring.appendFeePayerSignatures', () => {
beforeEach(() => {
transactionObj.feePayer = '0x90b3e9a3770481345a7f17f22f16d020bccfd33e'
})
afterEach(() => {
sandbox.restore()
})
it('CAVERJS-UNIT-TRANSACTIONFD-487: If feePayerSignatures is empty, appendFeePayerSignatures append feePayerSignatures in transaction', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const sig = [
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
]
tx.appendFeePayerSignatures(sig)
checkFeePayerSignature(tx)
})
it('CAVERJS-UNIT-TRANSACTIONFD-488: If feePayerSignatures is empty, appendFeePayerSignatures append feePayerSignatures with two-dimensional signature array', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const sig = [
[
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
],
]
tx.appendFeePayerSignatures(sig)
checkFeePayerSignature(tx)
})
it('CAVERJS-UNIT-TRANSACTIONFD-489: If feePayerSignatures is not empty, appendFeePayerSignatures should append feePayerSignatures', () => {
transactionObj.feePayerSignatures = [
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
]
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const sig = [
'0x0fea',
'0x7a5011b41cfcb6270af1b5f8aeac8aeabb1edb436f028261b5add564de694700',
'0x23ac51660b8b421bf732ef8148d0d4f19d5e29cb97be6bccb5ae505ebe89eb4a',
]
tx.appendFeePayerSignatures(sig)
checkFeePayerSignature(tx, { expectedLength: 2 })
})
it('CAVERJS-UNIT-TRANSACTIONFD-490: appendFeePayerSignatures should append multiple feePayerSignatures', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const sig = [
[
'0x0fea',
'0xbde66cceed35a576010966338b7ded961f2c160c96f928e193b47aaf4480aa07',
'0x546eb193ec138523b7fd34c4f12a1a04d0f74470e8f3bbe91ce0b4ec16e7f0d2',
],
[
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
],
]
tx.appendFeePayerSignatures(sig)
checkFeePayerSignature(tx, { expectedLength: 2 })
})
})
context('feeDelegatedChainDataAnchoring.combineSignedRawTransactions', () => {
beforeEach(() => {
transactionObj = {
from: '0xf1f766ded1aae1e06e2ed6c85127dd69891f7b28',
gas: '0x174876e800',
nonce: '0x1',
gasPrice: '0x5d21dba00',
chainId: '0x7e3',
input,
}
})
afterEach(() => {
sandbox.restore()
})
it('CAVERJS-UNIT-TRANSACTIONFD-491: combineSignedRawTransactions combines single signature and sets signatures in transaction', () => {
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures')
const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding')
const rlpEncoded =
'0x49f90136018505d21dba0085174876e80094f1f766ded1aae1e06e2ed6c85127dd69891f7b28b8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006f847f845820feaa0042800bfb7429b6c054fed37b86c473fdea9d4481d5d5b32cc92f34d744983b9a03ce733dfce2efd9f6ffaf70d50a0a211b94d84a8a18f1196e875053896a974be940000000000000000000000000000000000000000c4c3018080'
const combined = tx.combineSignedRawTransactions([rlpEncoded])
const expectedSignatures = [
[
'0x0fea',
'0x042800bfb7429b6c054fed37b86c473fdea9d4481d5d5b32cc92f34d744983b9',
'0x3ce733dfce2efd9f6ffaf70d50a0a211b94d84a8a18f1196e875053896a974be',
],
]
expect(appendSignaturesSpy).to.have.been.calledOnce
expect(getRLPEncodingSpy).to.have.been.calledOnce
expect(combined).to.equal(rlpEncoded)
checkSignature(tx, { expectedSignatures })
})
it('CAVERJS-UNIT-TRANSACTIONFD-492: combineSignedRawTransactions combines multiple signatures and sets signatures in transaction', () => {
transactionObj.signatures = [
[
'0x0fea',
'0x042800bfb7429b6c054fed37b86c473fdea9d4481d5d5b32cc92f34d744983b9',
'0x3ce733dfce2efd9f6ffaf70d50a0a211b94d84a8a18f1196e875053896a974be',
],
]
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const rlpEncodedStrings = [
'0x49f90122018505d21dba0085174876e80094f1f766ded1aae1e06e2ed6c85127dd69891f7b28b8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006f847f845820feaa0a45210f00ff64784e0aac0597b7eb19ea0890144100fd8dc8bb0b2fe003cbe84a07ff706e9a3825be7767f389789927a5633cf4995790a8bfe26d9332300de5db080c4c3018080',
'0x49f90122018505d21dba0085174876e80094f1f766ded1aae1e06e2ed6c85127dd69891f7b28b8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006f847f845820feaa076a91becce2c632980731d97a319216030de7b1b94b04c8a568236547d42d263a061c3c3456cda8eb7c440c700a168204b054d45d7bf2652c04171a0a1f76eff7380c4c3018080',
]
const appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures')
const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding')
const combined = tx.combineSignedRawTransactions(rlpEncodedStrings)
const expectedRLPEncoded =
'0x49f901c4018505d21dba0085174876e80094f1f766ded1aae1e06e2ed6c85127dd69891f7b28b8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006f8d5f845820feaa0042800bfb7429b6c054fed37b86c473fdea9d4481d5d5b32cc92f34d744983b9a03ce733dfce2efd9f6ffaf70d50a0a211b94d84a8a18f1196e875053896a974bef845820feaa0a45210f00ff64784e0aac0597b7eb19ea0890144100fd8dc8bb0b2fe003cbe84a07ff706e9a3825be7767f389789927a5633cf4995790a8bfe26d9332300de5db0f845820feaa076a91becce2c632980731d97a319216030de7b1b94b04c8a568236547d42d263a061c3c3456cda8eb7c440c700a168204b054d45d7bf2652c04171a0a1f76eff73940000000000000000000000000000000000000000c4c3018080'
const expectedSignatures = [
[
'0x0fea',
'0x042800bfb7429b6c054fed37b86c473fdea9d4481d5d5b32cc92f34d744983b9',
'0x3ce733dfce2efd9f6ffaf70d50a0a211b94d84a8a18f1196e875053896a974be',
],
[
'0x0fea',
'0xa45210f00ff64784e0aac0597b7eb19ea0890144100fd8dc8bb0b2fe003cbe84',
'0x7ff706e9a3825be7767f389789927a5633cf4995790a8bfe26d9332300de5db0',
],
[
'0x0fea',
'0x76a91becce2c632980731d97a319216030de7b1b94b04c8a568236547d42d263',
'0x61c3c3456cda8eb7c440c700a168204b054d45d7bf2652c04171a0a1f76eff73',
],
]
expect(appendSignaturesSpy).to.have.been.callCount(rlpEncodedStrings.length)
expect(getRLPEncodingSpy).to.have.been.calledOnce
expect(combined).to.equal(expectedRLPEncoded)
checkSignature(tx, { expectedSignatures })
})
it('CAVERJS-UNIT-TRANSACTIONFD-493: combineSignedRawTransactions combines single feePayerSignature and sets feePayerSignatures in transaction', () => {
transactionObj.feePayer = '0xee43ecbed54e4862ed98c11d2e71b8bd04c1667e'
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const appendSignaturesSpy = sandbox.spy(tx, 'appendFeePayerSignatures')
const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding')
const rlpEncoded =
'0x49f90136018505d21dba0085174876e80094f1f766ded1aae1e06e2ed6c85127dd69891f7b28b8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006c4c301808094ee43ecbed54e4862ed98c11d2e71b8bd04c1667ef847f845820fe9a0f8f21b4d667b139e80818c2b8bfd6117ace4bc11157be3c3ee74c0360565356fa0346828779330f21b7d06be682ec8289f3211c4018a20385cabd0d0ebc2569f16'
const combined = tx.combineSignedRawTransactions([rlpEncoded])
const expectedSignatures = [
[
'0x0fe9',
'0xf8f21b4d667b139e80818c2b8bfd6117ace4bc11157be3c3ee74c0360565356f',
'0x346828779330f21b7d06be682ec8289f3211c4018a20385cabd0d0ebc2569f16',
],
]
expect(appendSignaturesSpy).to.have.been.calledOnce
expect(getRLPEncodingSpy).to.have.been.calledOnce
expect(combined).to.equal(rlpEncoded)
checkFeePayerSignature(tx, { expectedSignatures })
})
it('CAVERJS-UNIT-TRANSACTIONFD-494: combineSignedRawTransactions combines multiple feePayerSignatures and sets feePayerSignatures in transaction', () => {
transactionObj.feePayer = '0xee43ecbed54e4862ed98c11d2e71b8bd04c1667e'
transactionObj.feePayerSignatures = [
[
'0x0fe9',
'0xf8f21b4d667b139e80818c2b8bfd6117ace4bc11157be3c3ee74c0360565356f',
'0x346828779330f21b7d06be682ec8289f3211c4018a20385cabd0d0ebc2569f16',
],
]
const tx = caver.transaction.feeDelegatedChainDataAnchoring.create(transactionObj)
const rlpEncodedStrings = [
'0x49f90136018505d21dba0085174876e80094f1f766ded1aae1e06e2ed6c85127dd69891f7b28b8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006c4c301808094ee43ecbed54e4862ed98c11d2e71b8bd04c1667ef847f845820feaa0baa6a845e8c68ae8bf9acc7e018bceaab506e0818e0dc8db2afe3490a1927317a046bacf69af211302103f8c3841bc3cc6a79e2298ee4bc5d5e73b25f42ca98156',
'0x49f90136018505d21dba0085174876e80094f1f766ded1aae1e06e2ed6c85127dd69891f7b28b8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006c4c301808094ee43ecbed54e4862ed98c11d2e71b8bd04c1667ef847f845820fe9a05df342131bfdae8239829e16a4298d711c238d0d4ab679b864878be729362921a07e3a7f484d6eb139c6b652c96aaa8ac8df43a5dfb3adaff46bc552a2c6965cba',
]
const appendSignaturesSpy = sandbox.spy(tx, 'appendFeePayerSignatures')
const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding')
const combined = tx.combineSignedRawTransactions(rlpEncodedStrings)
const expectedRLPEncoded =
'0x49f901c4018505d21dba0085174876e80094f1f766ded1aae1e06e2ed6c85127dd69891f7b28b8aff8ad80b8aaf8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004058006c4c301808094ee43ecbed54e4862ed98c11d2e71b8bd04c1667ef8d5f845820fe9a0f8f21b4d667b139e80818c2b8bfd6117ace4bc11157be3c3ee74c0360565356fa0346828779330f21b7d06be682ec8289f3211c4018a20385cabd0d0ebc2569f16f845820feaa0baa6a845e8c68ae8bf9acc7e018bceaab506e0818e0dc8db2afe3490a1927317a046bacf69af211302103f8c3841bc3cc6a79e2298ee4bc5d5e73b25f42ca98156f845820fe9a05df342131bfdae8239