Skip to content

Commit

Permalink
Merge pull request #395 from multiversx/sc-query-adapter
Browse files Browse the repository at this point in the history
Adaptor for executing smart contract queries
  • Loading branch information
popenta authored Mar 7, 2024
2 parents 2f0d036 + f921d34 commit 7cd9085
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 40 deletions.
1 change: 1 addition & 0 deletions src/adapters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./queryRunnerAdapter";
44 changes: 44 additions & 0 deletions src/adapters/queryRunnerAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Address } from "../address";
import { IAddress } from "../interface";
import { IContractQueryResponse } from "../interfaceOfNetwork";
import { SmartContractQuery, SmartContractQueryResponse } from "../smartContractQuery";

interface INetworkProvider {
queryContract(query: IQuery): Promise<IContractQueryResponse>;
}

interface IQuery {
address: IAddress;
caller?: IAddress;
func: { toString(): string };
value?: { toString(): string };
getEncodedArguments(): string[];
}

export class QueryRunnerAdapter {
private readonly networkProvider: INetworkProvider;

constructor(options: { networkProvider: INetworkProvider }) {
this.networkProvider = options.networkProvider;
}

async runQuery(query: SmartContractQuery): Promise<SmartContractQueryResponse> {
const legacyQuery: IQuery = {
address: Address.fromBech32(query.contract),
caller: query.caller ? Address.fromBech32(query.caller) : undefined,
func: query.function,
value: query.value,
getEncodedArguments: () => query.arguments.map((arg) => Buffer.from(arg).toString("hex")),
};

const legacyQueryResponse = await this.networkProvider.queryContract(legacyQuery);
const queryResponse = new SmartContractQueryResponse({
function: query.function,
returnCode: legacyQueryResponse.returnCode.toString(),
returnMessage: legacyQueryResponse.returnMessage,
returnDataParts: legacyQueryResponse.getReturnDataParts(),
});

return queryResponse;
}
}
40 changes: 31 additions & 9 deletions src/smartContractQueriesController.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { SmartContractQueryResponse } from "./smartContractQuery";
import { AbiRegistry, BigUIntValue, BooleanValue, BytesValue, Tuple, U16Value, U64Value } from "./smartcontracts";
import { bigIntToBuffer } from "./smartcontracts/codec/utils";
import { MockNetworkProvider, loadAbiRegistry } from "./testutils";
import { QueryRunnerAdapter } from "./adapters/queryRunnerAdapter";

describe("test smart contract queries controller", () => {
describe("createQuery", () => {
it("works without ABI, when arguments are buffers", function () {
const adapter = new QueryRunnerAdapter({ networkProvider: new MockNetworkProvider() });
const controller = new SmartContractQueriesController({
networkProvider: new MockNetworkProvider(),
queryRunner: adapter,
});

const query = controller.createQuery({
Expand All @@ -26,8 +28,9 @@ describe("test smart contract queries controller", () => {
});

it("works without ABI, when arguments are typed values", function () {
const adapter = new QueryRunnerAdapter({ networkProvider: new MockNetworkProvider() });
const controller = new SmartContractQueriesController({
networkProvider: new MockNetworkProvider(),
queryRunner: adapter,
});

const query = controller.createQuery({
Expand All @@ -42,8 +45,9 @@ describe("test smart contract queries controller", () => {
});

it("fails without ABI, when arguments aren't buffers, nor typed values", function () {
const adapter = new QueryRunnerAdapter({ networkProvider: new MockNetworkProvider() });
const controller = new SmartContractQueriesController({
networkProvider: new MockNetworkProvider(),
queryRunner: adapter,
});

assert.throws(() => {
Expand All @@ -56,9 +60,12 @@ describe("test smart contract queries controller", () => {
});

it("works with ABI, when arguments are native JS objects", async function () {
const controller = new SmartContractQueriesController({
const adapter = new QueryRunnerAdapter({
networkProvider: new MockNetworkProvider(),
});
const controller = new SmartContractQueriesController({
abi: await loadAbiRegistry("src/testdata/lottery-esdt.abi.json"),
queryRunner: adapter,
});

const query = controller.createQuery({
Expand All @@ -73,9 +80,12 @@ describe("test smart contract queries controller", () => {
});

it("works with ABI, when arguments typed values", async function () {
const controller = new SmartContractQueriesController({
const adapter = new QueryRunnerAdapter({
networkProvider: new MockNetworkProvider(),
});
const controller = new SmartContractQueriesController({
abi: await loadAbiRegistry("src/testdata/lottery-esdt.abi.json"),
queryRunner: adapter,
});

const query = controller.createQuery({
Expand Down Expand Up @@ -113,9 +123,12 @@ describe("test smart contract queries controller", () => {
],
});

const controller = new SmartContractQueriesController({
const adapter = new QueryRunnerAdapter({
networkProvider: new MockNetworkProvider(),
});
const controller = new SmartContractQueriesController({
abi: abi,
queryRunner: adapter,
});

const query = controller.createQuery({
Expand Down Expand Up @@ -149,9 +162,12 @@ describe("test smart contract queries controller", () => {
describe("runQuery", () => {
it("calls queryContract on the network provider", async function () {
const networkProvider = new MockNetworkProvider();
const controller = new SmartContractQueriesController({
const adapter = new QueryRunnerAdapter({
networkProvider: networkProvider,
});
const controller = new SmartContractQueriesController({
queryRunner: adapter,
});

networkProvider.mockQueryContractOnFunction(
"bar",
Expand All @@ -176,9 +192,12 @@ describe("test smart contract queries controller", () => {

describe("parseQueryResponse", () => {
it("works without ABI", function () {
const controller = new SmartContractQueriesController({
const adapter = new QueryRunnerAdapter({
networkProvider: new MockNetworkProvider(),
});
const controller = new SmartContractQueriesController({
queryRunner: adapter,
});

const response = new SmartContractQueryResponse({
function: "bar",
Expand All @@ -193,9 +212,12 @@ describe("test smart contract queries controller", () => {
});

it("works with ABI", async function () {
const controller = new SmartContractQueriesController({
const adapter = new QueryRunnerAdapter({
networkProvider: new MockNetworkProvider(),
});
const controller = new SmartContractQueriesController({
abi: await loadAbiRegistry("src/testdata/lottery-esdt.abi.json"),
queryRunner: adapter,
});

const response = new SmartContractQueryResponse({
Expand Down
37 changes: 6 additions & 31 deletions src/smartContractQueriesController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { Address } from "./address";
import { Err } from "./errors";
import { IAddress } from "./interface";
import { IContractQueryResponse } from "./interfaceOfNetwork";
import { SmartContractQuery, SmartContractQueryResponse } from "./smartContractQuery";
import { ArgSerializer, ContractFunction, EndpointDefinition, NativeSerializer, ResultsParser } from "./smartcontracts";
Expand All @@ -9,26 +7,18 @@ interface IAbi {
getEndpoint(name: string | ContractFunction): EndpointDefinition;
}

interface INetworkProvider {
queryContract(query: ILegacyQuery): Promise<IContractQueryResponse>;
}

interface ILegacyQuery {
address: IAddress;
caller?: IAddress;
func: { toString(): string };
value?: { toString(): string };
getEncodedArguments(): string[];
interface IQueryRunner {
runQuery(query: SmartContractQuery): Promise<SmartContractQueryResponse>;
}

export class SmartContractQueriesController {
private readonly abi?: IAbi;
private readonly networkProvider: INetworkProvider;
private readonly queryRunner: IQueryRunner;
private readonly legacyResultsParser: ResultsParser;

constructor(options: { abi?: IAbi; networkProvider: INetworkProvider }) {
constructor(options: { abi?: IAbi; queryRunner: IQueryRunner }) {
this.abi = options.abi;
this.networkProvider = options.networkProvider;
this.queryRunner = options.queryRunner;
this.legacyResultsParser = new ResultsParser();
}

Expand Down Expand Up @@ -92,22 +82,7 @@ export class SmartContractQueriesController {
}

async runQuery(query: SmartContractQuery): Promise<SmartContractQueryResponse> {
const legacyQuery: ILegacyQuery = {
address: Address.fromBech32(query.contract),
caller: query.caller ? Address.fromBech32(query.caller) : undefined,
func: query.function,
value: query.value,
getEncodedArguments: () => query.arguments.map((arg) => Buffer.from(arg).toString("hex")),
};

const legacyQueryResponse = await this.networkProvider.queryContract(legacyQuery);
const queryResponse = new SmartContractQueryResponse({
function: query.function,
returnCode: legacyQueryResponse.returnCode.toString(),
returnMessage: legacyQueryResponse.returnMessage,
returnDataParts: legacyQueryResponse.getReturnDataParts(),
});

const queryResponse = await this.queryRunner.runQuery(query);
return queryResponse;
}

Expand Down

0 comments on commit 7cd9085

Please sign in to comment.