Skip to content
Merged
8 changes: 2 additions & 6 deletions packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ export class EthImpl implements Eth {
* @rpcParamValidationRules Applies JSON-RPC parameter validation according to the API specification
*
* @param {string} account The account to get the balance from
* @param {string | null} blockNumberOrTagOrHash The block number or tag or hash to get the balance from
* @param {string} blockNumberOrTagOrHash The block number or tag or hash to get the balance from
* @param {RequestDetails} requestDetails The request details for logging and tracking
* @returns {Promise<string>} A promise that resolves to the balance of the account in hexadecimal format.
*/
Expand All @@ -714,11 +714,7 @@ export class EthImpl implements Eth {
@cache(CacheService.getInstance(CACHE_LEVEL.L1), {
skipParams: [{ index: '1', value: constants.NON_CACHABLE_BLOCK_PARAMS }],
})
async getBalance(
account: string,
blockNumberOrTagOrHash: string | null,
requestDetails: RequestDetails,
): Promise<string> {
async getBalance(account: string, blockNumberOrTagOrHash: string, requestDetails: RequestDetails): Promise<string> {
return this.accountService.getBalance(account, blockNumberOrTagOrHash, requestDetails);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ export class AccountService implements IAccountService {
* Current implementation does not yet utilize blockNumber
*
* @param {string} account The account to get the balance from
* @param {string | null} blockNumberOrTagOrHash The block number or tag or hash to get the balance from
* @param {string} blockNumberOrTagOrHash The block number or tag or hash to get the balance from
* @param {RequestDetails} requestDetails The request details for logging and tracking
*/
public async getBalance(
account: string,
blockNumberOrTagOrHash: string | null,
blockNumberOrTagOrHash: string,
requestDetails: RequestDetails,
): Promise<string> {
const requestIdPrefix = requestDetails.formattedRequestId;
Expand Down Expand Up @@ -174,7 +174,7 @@ export class AccountService implements IAccountService {
* @param requestDetails
* @private
*/
private async extractBlockNumberAndTimestamp(blockNumberOrTagOrHash: string | null, requestDetails: RequestDetails) {
private async extractBlockNumberAndTimestamp(blockNumberOrTagOrHash: string, requestDetails: RequestDetails) {
let latestBlock: LatestBlockNumberTimestamp;
const latestBlockTolerance = 1;
let blockHashNumber, isHash;
Expand All @@ -192,7 +192,7 @@ export class AccountService implements IAccountService {
latestBlock = await this.blockNumberTimestamp(constants.ETH_GET_BALANCE, requestDetails);
}

if (blockNumberOrTagOrHash != null && blockNumberOrTagOrHash.length > 32) {
if (blockNumberOrTagOrHash.length > 32) {
isHash = true;
blockHashNumber = await this.mirrorNodeClient.getBlock(blockNumberOrTagOrHash, requestDetails);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,5 @@ export interface IAccountService {
requestDetails: RequestDetails,
) => Promise<string | JsonRpcError>;

getBalance: (
account: string,
blockNumberOrTagOrHash: string | null,
requestDetails: RequestDetails,
) => Promise<string>;
getBalance: (account: string, blockNumberOrTagOrHash: string, requestDetails: RequestDetails) => Promise<string>;
}
51 changes: 3 additions & 48 deletions packages/relay/tests/lib/eth/eth_getBalance.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,42 +59,10 @@ describe('@ethGetBalance using MirrorNode', async function () {
restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, JSON.stringify(MOCK_BLOCK_NUMBER_1000_RES));
restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(200, JSON.stringify(MOCK_BALANCE_RES));

const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails);
const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, 'latest', requestDetails);
expect(resBalance).to.equal(DEF_HEX_BALANCE);
});

it('should return balance for latest block from cache', async () => {
restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, JSON.stringify(MOCK_BLOCK_NUMBER_1000_RES));
restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(200, JSON.stringify(MOCK_BALANCE_RES));

const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails);
expect(resBalance).to.equal(DEF_HEX_BALANCE);

// next call should use cache
restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(404, {});

const resBalanceCached = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails);
expect(resBalanceCached).to.equal(resBalance);

// Third call should return new number using mirror node
const newBalance = 55555;
const newBalanceHex = numberTo0x(BigInt(newBalance) * TINYBAR_TO_WEIBAR_COEF_BIGINT);
restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(
200,
JSON.stringify({
account: CONTRACT_ADDRESS_1,
balance: {
balance: newBalance,
},
}),
);
// expire cache, instead of waiting for ttl we clear it to simulate expiry faster.
await cacheService.clear(requestDetails);

const resBalanceNew = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails);
expect(newBalanceHex).to.equal(resBalanceNew);
});

it('should return balance from mirror node with block number passed as param the same as latest', async () => {
const blockNumber = '0x2710';
restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, JSON.stringify(MOCK_BLOCKS_FOR_BALANCE_RES));
Expand Down Expand Up @@ -162,24 +130,11 @@ describe('@ethGetBalance using MirrorNode', async function () {
restMock.onGet(`contracts/${CONTRACT_ADDRESS_1}`).reply(200, JSON.stringify(null));
restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(404, JSON.stringify(NOT_FOUND_RES));

const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails);
const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, 'latest', requestDetails);
expect(resBalance).to.equal(constants.ZERO_HEX);
});

it('should return cached value for mirror nodes', async () => {
restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, JSON.stringify(MOCK_BLOCK_NUMBER_1000_RES));
restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(200, JSON.stringify(MOCK_BALANCE_RES));

const resNoCache = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails);

restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(404, NOT_FOUND_RES);

const resCached = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails);
expect(resNoCache).to.equal(DEF_HEX_BALANCE);
expect(resCached).to.equal(DEF_HEX_BALANCE);
});

it('should return cached value for mirror nodes that is not latest so will need to query mirror node', async () => {
it('should return cached balance for a specific block number if mirror node is unavailable', async () => {
const blockNumber = '0x1';
restMock.onGet('blocks/1').reply(200, JSON.stringify(DEFAULT_BLOCK));

Expand Down
34 changes: 34 additions & 0 deletions packages/server/tests/acceptance/rpc_batch2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,40 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () {
const manuallyCalculatedBalanceAtTx1Block = BigInt(initialBalance) + BigInt(ONE_TINYBAR);
expect(BigInt(balanceAtTx1Block).toString()).to.eq(manuallyCalculatedBalanceAtTx1Block.toString());
});

it('should return an error when the second parameter is missing in eth_getBalance', async function () {
await expect(
relay.call(
RelayCalls.ETH_ENDPOINTS.ETH_GET_BALANCE,
[Address.NON_EXISTING_ADDRESS],
Utils.formatRequestIdMessage(requestId),
),
).to.eventually.be.rejected.and.satisfy((err: any) => {
try {
const body = JSON.parse(err?.info?.responseBody || '{}');
return body?.error?.message?.includes('Missing value for required parameter');
} catch {
return false;
}
}, 'Expected error message to include "Missing value for required parameter"');
});

it('should return an error when null is provided as the second parameter in eth_getBalance', async function () {
await expect(
relay.call(
RelayCalls.ETH_ENDPOINTS.ETH_GET_BALANCE,
[Address.NON_EXISTING_ADDRESS, null],
Utils.formatRequestIdMessage(requestId),
),
).to.eventually.be.rejected.and.satisfy((err: any) => {
try {
const body = JSON.parse(err?.info?.responseBody || '{}');
return body?.error?.message?.includes('Invalid parameter 1: The value passed is not valid: null.');
} catch {
return false;
}
}, 'Expected error message to include "Invalid parameter 1: The value passed is not valid: null."');
});
});

describe('@release Hardcoded RPC Endpoints', () => {
Expand Down
Loading