Skip to content

Commit 3851f42

Browse files
committed
fix: preserve non-400 error
Signed-off-by: Logan Nguyen <[email protected]>
1 parent c6608dc commit 3851f42

File tree

2 files changed

+76
-35
lines changed

2 files changed

+76
-35
lines changed

packages/relay/src/lib/services/ethService/contractService/ContractService.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,16 @@ export class ContractService implements IContractService {
176176

177177
return prepend0x(trimPrecedingZeros(response.result));
178178
} catch (e: any) {
179-
if (e instanceof MirrorNodeClientError && e.isContractRevert()) {
180-
throw predefined.CONTRACT_REVERT(e.detail || e.message, e.data);
181-
} else {
182-
throw predefined.COULD_NOT_ESTIMATE_GAS_PRICE(e.detail || e.message);
179+
if (e instanceof MirrorNodeClientError) {
180+
if (e.isContractRevert()) {
181+
throw predefined.CONTRACT_REVERT(e.detail || e.message, e.data);
182+
} else if (e.statusCode === 400) {
183+
throw predefined.COULD_NOT_ESTIMATE_GAS_PRICE(e.detail || e.message);
184+
}
183185
}
186+
187+
// for any other errors, preserve and re-throw to the upper layer as more mapping logic may be applied there
188+
throw e;
184189
}
185190
}
186191

packages/relay/tests/lib/eth/eth_estimateGas.spec.ts

Lines changed: 67 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { numberTo0x } from '../../../src/formatters';
1111
import { SDKClient } from '../../../src/lib/clients';
1212
import constants from '../../../src/lib/constants';
1313
import { predefined } from '../../../src/lib/errors/JsonRpcError';
14+
import { MirrorNodeClientError } from '../../../src/lib/errors/MirrorNodeClientError';
1415
import { EthImpl } from '../../../src/lib/eth';
1516
import { IContractCallRequest, IContractCallResponse, RequestDetails } from '../../../src/lib/types';
1617
import { overrideEnvsInMochaDescribe } from '../../helpers';
@@ -29,6 +30,7 @@ let sdkClientStub: SinonStubbedInstance<SDKClient>;
2930
let getSdkClientStub: SinonStub<[], SDKClient>;
3031
let ethImplOverridden: Eth;
3132
const gasTxBaseCost = numberTo0x(constants.TX_BASE_COST);
33+
3234
describe('@ethEstimateGas Estimate Gas spec', async function () {
3335
this.timeout(10000);
3436
const { restMock, web3Mock, hapiServiceInstance, ethImpl, cacheService, mirrorNodeInstance, logger } =
@@ -92,28 +94,28 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
9294

9395
describe('eth_estimateGas with contract call', async function () {});
9496

95-
it('should eth_estimateGas with transaction.data null throws COULD_NOT_ESTIMATE_GAS_PRICE error', async function () {
97+
it('should eth_estimateGas with transaction.data null throws COULD_NOT_ESTIMATE_GAS_PRICE error on 400', async function () {
9698
const callData: IContractCallRequest = {
9799
from: '0x05fba803be258049a27b820088bab1cad2058871',
98100
value: '0x0',
99101
gasPrice: '0x0',
100102
data: null,
101103
};
102-
await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails);
104+
await mockContractCall(callData, true, 400, { errorMessage: '', statusCode: 400 }, requestDetails);
103105

104106
await expect(ethImpl.estimateGas(callData, null, requestDetails)).to.be.rejectedWith(
105107
JsonRpcError,
106108
gasPriceErrorMessage,
107109
);
108110
});
109111

110-
it('should eth_estimateGas to mirror node for contract call returns 501 throws COULD_NOT_ESTIMATE_GAS_PRICE error', async function () {
112+
it('should eth_estimateGas to mirror node for contract call returns 400 throws COULD_NOT_ESTIMATE_GAS_PRICE error', async function () {
111113
const callData: IContractCallRequest = {
112114
data: '0x608060405234801561001057600080fd5b506040516107893803806107898339818101604052810190610032919061015a565b806000908051906020019061004892919061004f565b50506102f6565b82805461005b90610224565b90600052602060002090601f01602090048101928261007d57600085556100c4565b82601f1061009657805160ff19168380011785556100c4565b828001600101855582156100c4579182015b828111156100c35782518255916020019190600101906100a8565b5b5090506100d191906100d5565b5090565b5b808211156100ee5760008160009055506001016100d6565b5090565b6000610105610100846101c0565b61019b565b90508281526020810184848401111561011d57600080fd5b6101288482856101f1565b509392505050565b600082601f83011261014157600080fd5b81516101518482602086016100f2565b91505092915050565b60006020828403121561016c57600080fd5b600082015167ffffffffffffffff81111561018657600080fd5b61019284828501610130565b91505092915050565b60006101a56101b6565b90506101b18282610256565b919050565b6000604051905090565b600067ffffffffffffffff8211156101db576101da6102b6565b5b6101e4826102e5565b9050602081019050919050565b60005b8381101561020f5780820151818401526020810190506101f4565b8381111561021e576000848401525b50505050565b6000600282049050600182168061023c57607f821691505b602082108114156102505761024f610287565b5b50919050565b61025f826102e5565b810181811067ffffffffffffffff8211171561027e5761027d6102b6565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b610484806103056000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae321714610057575b600080fd5b6100556004803603810190610050919061022c565b610075565b005b61005f61008f565b60405161006c91906102a6565b60405180910390f35b806000908051906020019061008b929190610121565b5050565b60606000805461009e9061037c565b80601f01602080910402602001604051908101604052809291908181526020018280546100ca9061037c565b80156101175780601f106100ec57610100808354040283529160200191610117565b820191906000526020600020905b8154815290600101906020018083116100fa57829003601f168201915b5050505050905090565b82805461012d9061037c565b90600052602060002090601f01602090048101928261014f5760008555610196565b82601f1061016857805160ff1916838001178555610196565b82800160010185558215610196579182015b8281111561019557825182559160200191906001019061017a565b5b5090506101a391906101a7565b5090565b5b808211156101c05760008160009055506001016101a8565b5090565b60006101d76101d2846102ed565b6102c8565b9050828152602081018484840111156101ef57600080fd5b6101fa84828561033a565b509392505050565b600082601f83011261021357600080fd5b81356102238482602086016101c4565b91505092915050565b60006020828403121561023e57600080fd5b600082013567ffffffffffffffff81111561025857600080fd5b61026484828501610202565b91505092915050565b60006102788261031e565b6102828185610329565b9350610292818560208601610349565b61029b8161043d565b840191505092915050565b600060208201905081810360008301526102c0818461026d565b905092915050565b60006102d26102e3565b90506102de82826103ae565b919050565b6000604051905090565b600067ffffffffffffffff8211156103085761030761040e565b5b6103118261043d565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b82818337600083830152505050565b60005b8381101561036757808201518184015260208101905061034c565b83811115610376576000848401525b50505050565b6000600282049050600182168061039457607f821691505b602082108114156103a8576103a76103df565b5b50919050565b6103b78261043d565b810181811067ffffffffffffffff821117156103d6576103d561040e565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f830116905091905056fea264697066735822122070d157c4efbb3fba4a1bde43cbba5b92b69f2fc455a650c0dfb61e9ed3d4bd6364736f6c634300080400330000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b696e697469616c5f6d7367000000000000000000000000000000000000000000',
113115
from: '0x81cb089c285e5ee3a7353704fb114955037443af',
114116
to: RECEIVER_ADDRESS,
115117
};
116-
await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails);
118+
await mockContractCall(callData, true, 400, { errorMessage: '', statusCode: 400 }, requestDetails);
117119

118120
await expect(ethImpl.estimateGas(callData, null, requestDetails)).to.be.rejectedWith(
119121
JsonRpcError,
@@ -144,26 +146,26 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
144146
expect((gas as string).toLowerCase()).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT).toLowerCase());
145147
});
146148

147-
it('should eth_estimateGas for contract deploy throws COULD_NOT_ESTIMATE_GAS_PRICE error on 501', async function () {
149+
it('should eth_estimateGas for contract deploy throws COULD_NOT_ESTIMATE_GAS_PRICE error on 400', async function () {
148150
const callData: IContractCallRequest = {
149151
data: '0x01',
150152
};
151-
await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails);
153+
await mockContractCall(callData, true, 400, { errorMessage: '', statusCode: 400 }, requestDetails);
152154

153155
await expect(ethImpl.estimateGas({ data: '0x01' }, null, requestDetails)).to.be.rejectedWith(
154156
JsonRpcError,
155157
gasPriceErrorMessage,
156158
);
157159
});
158160

159-
it('should eth_estimateGas to mirror node for transfer throws COULD_NOT_ESTIMATE_GAS_PRICE error on 501', async function () {
161+
it('should eth_estimateGas to mirror node for transfer throws COULD_NOT_ESTIMATE_GAS_PRICE error on 400', async function () {
160162
const callData: IContractCallRequest = {
161-
data: '0x',
162163
from: '0x81cb089c285e5ee3a7353704fb114955037443af',
163164
to: RECEIVER_ADDRESS,
164165
value: '0x2540BE400',
166+
// data: '0x',
165167
};
166-
await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails);
168+
await mockContractCall(callData, true, 400, { errorMessage: '', statusCode: 400 }, requestDetails);
167169
restMock
168170
.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`)
169171
.reply(200, JSON.stringify({ address: RECEIVER_ADDRESS }));
@@ -174,13 +176,13 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
174176
);
175177
});
176178

177-
it('should eth_estimateGas to mirror node for transfer without value throws COULD_NOT_ESTIMATE_GAS_PRICE error on 501', async function () {
179+
it('should eth_estimateGas to mirror node for transfer without value throws COULD_NOT_ESTIMATE_GAS_PRICE error on 400', async function () {
178180
const callData: IContractCallRequest = {
179-
data: '0x',
180181
from: '0x81cb089c285e5ee3a7353704fb114955037443af',
181182
to: RECEIVER_ADDRESS,
183+
// data: '0x',
182184
};
183-
await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails);
185+
await mockContractCall(callData, true, 400, { errorMessage: '', statusCode: 400 }, requestDetails);
184186
restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(200, { address: RECEIVER_ADDRESS });
185187

186188
await expect(ethImpl.estimateGas(callData, null, requestDetails)).to.be.rejectedWith(
@@ -304,12 +306,12 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
304306
expect((gas as string).toLowerCase()).to.equal(numberTo0x(gasEstimation).toLowerCase());
305307
});
306308

307-
it('should eth_estimateGas transfer with invalid value throws COULD_NOT_ESTIMATE_GAS_PRICE error', async function () {
309+
it('should eth_estimateGas transfer with invalid value throws COULD_NOT_ESTIMATE_GAS_PRICE error on 400', async function () {
308310
const callData: IContractCallRequest = {
309311
to: RECEIVER_ADDRESS,
310312
value: -100_000_000_000, //in tinybars
311313
};
312-
await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails);
314+
await mockContractCall(callData, true, 400, { errorMessage: '', statusCode: 400 }, requestDetails);
313315
restMock
314316
.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`)
315317
.reply(200, JSON.stringify({ address: RECEIVER_ADDRESS }));
@@ -326,9 +328,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
326328
).to.be.rejectedWith(JsonRpcError, gasPriceErrorMessage);
327329
});
328330

329-
it('should eth_estimateGas empty call throws COULD_NOT_ESTIMATE_GAS_PRICE error on 501', async function () {
331+
it('should eth_estimateGas empty call throws COULD_NOT_ESTIMATE_GAS_PRICE error on 400', async function () {
330332
const callData: IContractCallRequest = {};
331-
await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails);
333+
await mockContractCall(callData, true, 400, { errorMessage: '', statusCode: 400 }, requestDetails);
332334

333335
await expect(ethImpl.estimateGas({}, null, requestDetails)).to.be.rejectedWith(JsonRpcError, gasPriceErrorMessage);
334336
});
@@ -341,26 +343,26 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
341343
expect(gas).to.equal(numberTo0x(defaultGasOverride));
342344
});
343345

344-
it('should eth_estimateGas empty input throws COULD_NOT_ESTIMATE_GAS_PRICE error on 501', async function () {
346+
it('should eth_estimateGas empty input throws COULD_NOT_ESTIMATE_GAS_PRICE error on 400', async function () {
345347
const callData: IContractCallRequest = {
346348
data: '',
347349
estimate: true,
348350
};
349-
const contractsCallResponse: IContractCallResponse = { errorMessage: '', statusCode: 501 };
351+
const contractsCallResponse: IContractCallResponse = { errorMessage: '', statusCode: 400 };
350352

351-
await mockContractCall(callData, true, 501, contractsCallResponse, requestDetails);
353+
await mockContractCall(callData, true, 400, contractsCallResponse, requestDetails);
352354

353355
await expect(ethImpl.estimateGas({ data: '' }, null, requestDetails)).to.be.rejectedWith(
354356
JsonRpcError,
355357
gasPriceErrorMessage,
356358
);
357359
});
358360

359-
it('should eth_estimateGas empty input throws COULD_NOT_ESTIMATE_GAS_PRICE error on 501 with overridden default gas', async function () {
361+
it('should eth_estimateGas empty input throws COULD_NOT_ESTIMATE_GAS_PRICE error on 400 with overridden default gas', async function () {
360362
const callData: IContractCallRequest = {
361363
data: '',
362364
};
363-
await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails);
365+
await mockContractCall(callData, true, 400, { errorMessage: '', statusCode: 400 }, requestDetails);
364366

365367
await expect(ethImplOverridden.estimateGas({ data: '' }, null, requestDetails)).to.be.rejectedWith(
366368
JsonRpcError,
@@ -389,13 +391,13 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
389391
expect(gas).to.equal(gasTxBaseCost);
390392
});
391393

392-
it('should eth_estimateGas zero input throws COULD_NOT_ESTIMATE_GAS_PRICE error on 501 with overridden default gas', async function () {
394+
it('should eth_estimateGas zero input throws COULD_NOT_ESTIMATE_GAS_PRICE error on 400 with overridden default gas', async function () {
393395
const callData: IContractCallRequest = {
394-
data: '0x',
396+
gas: '0x0',
395397
};
396-
await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails);
398+
await mockContractCall(callData, true, 400, { errorMessage: '', statusCode: 400 }, requestDetails);
397399

398-
await expect(ethImplOverridden.estimateGas({ data: '0x' }, null, requestDetails)).to.be.rejectedWith(
400+
await expect(ethImplOverridden.estimateGas(callData, null, requestDetails)).to.be.rejectedWith(
399401
JsonRpcError,
400402
gasPriceErrorMessage,
401403
);
@@ -517,11 +519,16 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
517519
});
518520
});
519521

520-
it('should eth_estimateGas handles a 501 unimplemented response from the mirror node correctly by throwing COULD_NOT_ESTIMATE_GAS_PRICE error', async function () {
522+
it('should eth_estimateGas handles a 400 response from the mirror node correctly by throwing COULD_NOT_ESTIMATE_GAS_PRICE error', async function () {
523+
const callData: IContractCallRequest = {
524+
from: '0x05fba803be258049a27b820088bab1cad2058871',
525+
value: '0x1',
526+
gasPrice: '0x0',
527+
};
521528
await mockContractCall(
522-
transaction,
529+
callData,
523530
true,
524-
501,
531+
400,
525532
{
526533
_status: {
527534
messages: [
@@ -536,9 +543,10 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
536543
requestDetails,
537544
);
538545

539-
await expect(
540-
ethImpl.estimateGas({ ...transaction, data: '0x', value: '0x1' }, id, requestDetails),
541-
).to.be.rejectedWith(JsonRpcError, gasPriceErrorMessage);
546+
await expect(ethImpl.estimateGas(callData, null, requestDetails)).to.be.rejectedWith(
547+
JsonRpcError,
548+
gasPriceErrorMessage,
549+
);
542550
});
543551

544552
it('should should perform estimateGas precheck', async function () {
@@ -606,4 +614,32 @@ describe('@ethEstimateGas Estimate Gas spec', async function () {
606614
expect(transaction.gasPrice).to.eq(1000000);
607615
expect(transaction.gas).to.eq(14250000);
608616
});
617+
618+
describe('eth_estimateGas with mirror node 5xx server errors', function () {
619+
const serverErrorStatusCodes = [500, 501, 502, 503, 504];
620+
621+
serverErrorStatusCodes.forEach((statusCode) => {
622+
it(`should throw MirrorNodeClientError when mirror node returns ${statusCode} status code`, async function () {
623+
const callData: IContractCallRequest = {
624+
from: '0x05fba803be258049a27b820088bab1cad2058871',
625+
to: RECEIVER_ADDRESS,
626+
};
627+
628+
await mockContractCall(
629+
callData,
630+
true,
631+
statusCode,
632+
{ errorMessage: `Server error ${statusCode}`, statusCode },
633+
requestDetails,
634+
);
635+
636+
await expect(ethImpl.estimateGas(callData, null, requestDetails))
637+
.to.be.rejectedWith(MirrorNodeClientError)
638+
.and.eventually.satisfy((error: MirrorNodeClientError) => {
639+
expect(error.statusCode).to.equal(statusCode);
640+
return true;
641+
});
642+
});
643+
});
644+
});
609645
});

0 commit comments

Comments
 (0)