Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/inner into main #515

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions src/networkProviders/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ export interface INetworkProvider {
/**
* Fetches data about the non-fungible tokens held by account.
*/
getNonFungibleTokensOfAccount(address: IAddress, pagination?: IPagination): Promise<NonFungibleTokenOfAccountOnNetwork[]>;
getNonFungibleTokensOfAccount(
address: IAddress,
pagination?: IPagination,
): Promise<NonFungibleTokenOfAccountOnNetwork[]>;

/**
* Fetches data about a specific fungible token held by an account.
Expand All @@ -56,7 +59,11 @@ export interface INetworkProvider {
/**
* Fetches data about a specific non-fungible token (instance) held by an account.
*/
getNonFungibleTokenOfAccount(address: IAddress, collection: string, nonce: number): Promise<NonFungibleTokenOfAccountOnNetwork>;
getNonFungibleTokenOfAccount(
address: IAddress,
collection: string,
nonce: number,
): Promise<NonFungibleTokenOfAccountOnNetwork>;

/**
* Fetches the state of a transaction.
Expand All @@ -80,7 +87,7 @@ export interface INetworkProvider {

/**
* Simulates the processing of an already-signed transaction.
*
*
*/
simulateTransaction(tx: ITransaction): Promise<any>;

Expand Down Expand Up @@ -118,8 +125,8 @@ export interface INetworkProvider {
export interface IContractQuery {
address: IAddress;
caller?: IAddress;
func: { toString(): string; };
value?: { toString(): string; };
func: { toString(): string };
value?: { toString(): string };
getEncodedArguments(): string[];
}

Expand All @@ -132,7 +139,9 @@ export interface ITransaction {
toSendable(): any;
}

export interface IAddress { bech32(): string; }
export interface IAddress {
bech32(): string;
}

export interface ITransactionNext {
sender: string;
Expand All @@ -150,4 +159,6 @@ export interface ITransactionNext {
guardian: string;
signature: Uint8Array;
guardianSignature: Uint8Array;
}
relayer?: string;
innerTransactions?: ITransactionNext[];
}
103 changes: 103 additions & 0 deletions src/networkProviders/providers.dev.net.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,49 @@ describe("test network providers on devnet: Proxy and API", function () {
}
});

it("should have same response for getTransaction() (relayed V3)", async function () {
this.timeout(20000);

// Transaction was sent using mxpy, as follows:
// mxpy tx new --pem=~/multiversx-sdk/testwallets/latest/users/alice.pem --receiver=erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx --value=1000000000000000000 --gas-limit=50000 --recall-nonce --relayer=erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede --inner-transactions-outfile=inner.json --proxy=https://devnet-gateway.multiversx.com
// mxpy tx new --pem=~/multiversx-sdk/testwallets/latest/users/grace.pem --receiver=erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede --gas-limit=100000 --recall-nonce --chain=D --inner-transactions=inner.json --proxy=https://devnet-gateway.multiversx.com --send

const txHash = "8cdfb790be8cd4b331da486ba014ed56d0dbac70c1cfbadf11db2edd48d0e437";
const apiResponse = await apiProvider.getTransaction(txHash);
const proxyResponse = await proxyProvider.getTransaction(txHash, true);

assert.deepEqual(proxyResponse.innerTransactions, [
{
nonce: BigInt(9340),
value: BigInt("1000000000000000000"),
receiver: "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx",
sender: "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
data: Buffer.from([]),
gasPrice: BigInt(1000000000),
gasLimit: BigInt(50000),
chainID: "D",
version: 2,
signature: Buffer.from(
"0993c2f3a47c01cf8330e54571ea9340aae481d0d5212af31b62eb7194e199231f105134aae28a75bb48b53a3dff09d6c6208843c8e0376617cf62d3bfb60204",
"hex",
),
senderUsername: "",
receiverUsername: "",
guardian: "",
guardianSignature: Buffer.from([]),
options: 0,
relayer: "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede",
},
]);

ignoreKnownTransactionDifferencesBetweenProviders(apiResponse, proxyResponse);
assert.deepEqual(apiResponse, proxyResponse);

// Also assert completion
assert.isTrue(apiResponse.isCompleted);
assert.isTrue(proxyResponse.isCompleted);
});

// TODO: Strive to have as little differences as possible between Proxy and API.
function ignoreKnownTransactionDifferencesBetweenProviders(
apiResponse: TransactionOnNetwork,
Expand All @@ -284,8 +327,68 @@ describe("test network providers on devnet: Proxy and API", function () {
proxyResponse.blockNonce = 0;
proxyResponse.hyperblockNonce = 0;
proxyResponse.hyperblockHash = "";

// API does not provide "innerTransactions" (Spica), for the moment.
proxyResponse.innerTransactions = [];
}

it("should send `TransactionNext` (as relayed V3)", async function () {
this.timeout(50000);

// Transaction was created using mxpy, as follows:
// mxpy tx new --pem=~/multiversx-sdk/testwallets/latest/users/alice.pem --receiver=erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx --value=1000000000000000000 --gas-limit=50000 --nonce=7 --chain=D --relayer=erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede --inner-transactions-outfile=inner.json
// mxpy tx new --pem=~/multiversx-sdk/testwallets/latest/users/grace.pem --receiver=erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede --gas-limit=100000 --nonce=42 --chain=D --inner-transactions=inner.json

const transactionNext: ITransactionNext = {
nonce: BigInt(42),
value: BigInt(0),
receiver: "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede",
sender: "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede",
data: new Uint8Array(),
gasPrice: BigInt(1000000000),
gasLimit: BigInt(100000),
chainID: "D",
version: 2,
signature: Buffer.from(
"c623854967c954d13681035d5b24be68a5a58d25e7efdc75c7d59b5c389e1ad6c9d21a6f41149ec6e8bd051d74f3636a60ce047062f05c748600c36348238e0b",
"hex",
),
senderUsername: "",
receiverUsername: "",
guardian: "",
guardianSignature: new Uint8Array(),
options: 0,
innerTransactions: [
{
nonce: BigInt(7),
value: BigInt("1000000000000000000"),
receiver: "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx",
sender: "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
data: new Uint8Array(),
gasPrice: BigInt(1000000000),
gasLimit: BigInt(50000),
chainID: "D",
version: 2,
signature: Buffer.from(
"40808231154b9924c0d5f885d320f4ab666308f7443ea128ac26029b1de07abfcee6412e1249a9c0fcf79638d9691be3c9fe75dd7c85462082f9b86c4008b30e",
"hex",
),
senderUsername: "",
receiverUsername: "",
guardian: "",
guardianSignature: new Uint8Array(),
options: 0,
relayer: "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede",
},
],
};

const apiTxNextHash = await apiProvider.sendTransaction(transactionNext);
const proxyTxNextHash = await proxyProvider.sendTransaction(transactionNext);

assert.equal(apiTxNextHash, proxyTxNextHash);
});

it("should have the same response for transactions with events", async function () {
const hash = "1b04eb849cf87f2d3086c77b4b825d126437b88014327bbf01437476751cb040";

Expand Down
55 changes: 48 additions & 7 deletions src/networkProviders/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { TransactionLogs } from "./transactionLogs";
import { TransactionReceipt } from "./transactionReceipt";

export function prepareTransactionForBroadcasting(transaction: ITransaction | ITransactionNext): any {
if ("toSendable" in transaction){
if ("toSendable" in transaction) {
return transaction.toSendable();
}

Expand All @@ -15,8 +15,12 @@ export function prepareTransactionForBroadcasting(transaction: ITransaction | IT
value: transaction.value.toString(),
receiver: transaction.receiver,
sender: transaction.sender,
senderUsername: transaction.senderUsername ? Buffer.from(transaction.senderUsername).toString("base64") : undefined,
receiverUsername: transaction.receiverUsername ? Buffer.from(transaction.receiverUsername).toString("base64") : undefined,
senderUsername: transaction.senderUsername
? Buffer.from(transaction.senderUsername).toString("base64")
: undefined,
receiverUsername: transaction.receiverUsername
? Buffer.from(transaction.receiverUsername).toString("base64")
: undefined,
gasPrice: Number(transaction.gasPrice),
gasLimit: Number(transaction.gasLimit),
data: transaction.data.length === 0 ? undefined : Buffer.from(transaction.data).toString("base64"),
Expand All @@ -25,8 +29,15 @@ export function prepareTransactionForBroadcasting(transaction: ITransaction | IT
options: transaction.options,
guardian: transaction.guardian || undefined,
signature: Buffer.from(transaction.signature).toString("hex"),
guardianSignature: transaction.guardianSignature.length === 0 ? undefined : Buffer.from(transaction.guardianSignature).toString("hex"),
}
guardianSignature:
transaction.guardianSignature.length === 0
? undefined
: Buffer.from(transaction.guardianSignature).toString("hex"),
relayer: transaction.relayer ? transaction.relayer : undefined,
innerTransactions: transaction.innerTransactions
? transaction.innerTransactions.map((tx) => prepareTransactionForBroadcasting(tx))
: undefined,
};
}

export class TransactionOnNetwork {
Expand Down Expand Up @@ -54,18 +65,23 @@ export class TransactionOnNetwork {
receipt: TransactionReceipt = new TransactionReceipt();
contractResults: ContractResults = new ContractResults([]);
logs: TransactionLogs = new TransactionLogs();
innerTransactions: ITransactionNext[] = [];

constructor(init?: Partial<TransactionOnNetwork>) {
Object.assign(this, init);
}

static fromProxyHttpResponse(txHash: string, response: any, processStatus?: TransactionStatus | undefined): TransactionOnNetwork {
static fromProxyHttpResponse(
txHash: string,
response: any,
processStatus?: TransactionStatus | undefined,
): TransactionOnNetwork {
let result = TransactionOnNetwork.fromHttpResponse(txHash, response);
result.contractResults = ContractResults.fromProxyHttpResponse(response.smartContractResults || []);

if (processStatus) {
result.status = processStatus;
result.isCompleted = result.status.isSuccessful() || result.status.isFailed()
result.isCompleted = result.status.isSuccessful() || result.status.isFailed();
}

return result;
Expand Down Expand Up @@ -102,10 +118,35 @@ export class TransactionOnNetwork {

result.receipt = TransactionReceipt.fromHttpResponse(response.receipt || {});
result.logs = TransactionLogs.fromHttpResponse(response.logs || {});
result.innerTransactions = (response.innerTransactions || []).map(this.innerTransactionFromHttpResource);

return result;
}

private static innerTransactionFromHttpResource(resource: any): ITransactionNext {
return {
nonce: BigInt(resource.nonce || 0),
value: BigInt(resource.value || 0),
receiver: resource.receiver,
sender: resource.sender,
// We discard "senderUsername" and "receiverUsername" (to avoid future discrepancies between Proxy and API):
senderUsername: "",
receiverUsername: "",
gasPrice: BigInt(resource.gasPrice),
gasLimit: BigInt(resource.gasLimit),
data: Buffer.from(resource.data || "", "base64"),
chainID: resource.chainID,
version: resource.version,
options: resource.options || 0,
guardian: resource.guardian || "",
signature: Buffer.from(resource.signature, "hex"),
guardianSignature: resource.guardianSignature
? Buffer.from(resource.guardianSignature, "hex")
: Buffer.from([]),
relayer: resource.relayer,
};
}

getDateTime(): Date {
return new Date(this.timestamp * 1000);
}
Expand Down
Loading