Skip to content

Commit

Permalink
feat: add internal transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
ErnoW committed Mar 21, 2023
1 parent 5d97adf commit 6d8dbac
Show file tree
Hide file tree
Showing 23 changed files with 792 additions and 30 deletions.
6 changes: 6 additions & 0 deletions .changeset/soft-oranges-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@moralisweb3/common-evm-utils': minor
'@moralisweb3/evm-api': minor
---

Add internal transactions param to several operations and `Moralis.Evm.transaction.getInternalTransactions()`
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { Core } from '@moralisweb3/common-core';
import { EvmInternalTransaction } from './EvmInternalTransaction';
import { setupEvmUtils } from '../../test/setup';
import { EvmInternalTransactionInput } from './types';

const exampleInput: EvmInternalTransactionInput = {
chain: '0x1',
transactionHash: '0x2ac6283fb30fe33499416b0388ff27145a0eeb6aa8b37bca40af87d7f1c74e2d',
blockNumber: 16876143,
blockHash: '0xc8d7592122307a771c5172af09699b5a2d36fa540d0fbc656f3d52c619c7536e',
type: 'STATICCALL',
from: '0x283af0b28c62c092c9727f1ee09c02ca627eb7f5',
to: '0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85',
value: '100000',
gas: '263200',
gasUsed: '2569',
input: '0x96e494e8d40a37cd10c71cb3896d1b05b6c707e29cb5aeff0278c6fc7e5e5b31623a1baa',
output: '0x0000000000000000000000000000000000000000000000000000000000000001',
};

describe('EvmInternalTransaction', () => {
let core: Core;

beforeAll(() => {
core = setupEvmUtils();
});

beforeEach(() => {
core.config.reset();
});

/**
* Creation
*/
it('should create a new EvmInternalTransaction', () => {
const transaction = EvmInternalTransaction.create(exampleInput);

expect(transaction.chain.hex).toBe('0x1');
expect(transaction.transactionHash).toBe('0x2ac6283fb30fe33499416b0388ff27145a0eeb6aa8b37bca40af87d7f1c74e2d');
expect(transaction.blockNumber.toString()).toBe('16876143');
expect(transaction.blockHash).toBe('0xc8d7592122307a771c5172af09699b5a2d36fa540d0fbc656f3d52c619c7536e');
expect(transaction.type).toBe('STATICCALL');
expect(transaction.from.lowercase).toBe('0x283af0b28c62c092c9727f1ee09c02ca627eb7f5');
expect(transaction.to.lowercase).toBe('0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85');
expect(transaction.value.toString()).toBe('100000');
expect(transaction.gas.toString()).toBe('263200');
expect(transaction.gasUsed.toString()).toBe('2569');
expect(transaction.input).toBe('0x96e494e8d40a37cd10c71cb3896d1b05b6c707e29cb5aeff0278c6fc7e5e5b31623a1baa');
expect(transaction.output).toBe('0x0000000000000000000000000000000000000000000000000000000000000001');
});

/**
* Formatting
*/
it('should return formatting in json', () => {
const transaction = EvmInternalTransaction.create(exampleInput);

const value = transaction.toJSON();

expect(value).toStrictEqual({
chain: '0x1',
from: '0x283af0b28c62c092c9727f1ee09c02ca627eb7f5',
to: '0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85',
transactionHash: '0x2ac6283fb30fe33499416b0388ff27145a0eeb6aa8b37bca40af87d7f1c74e2d',
gas: '263200',
gasUsed: '2569',
blockNumber: '16876143',
blockHash: '0xc8d7592122307a771c5172af09699b5a2d36fa540d0fbc656f3d52c619c7536e',
input: '0x96e494e8d40a37cd10c71cb3896d1b05b6c707e29cb5aeff0278c6fc7e5e5b31623a1baa',
output: '0x0000000000000000000000000000000000000000000000000000000000000001',
value: '100000',
type: 'STATICCALL',
});
});

/**
* Methods
*/
it('should check equality of 2 transactions of the same value', () => {
const transactionA = EvmInternalTransaction.create(exampleInput);
const transactionB = EvmInternalTransaction.create(exampleInput);

expect(transactionA.equals(transactionB)).toBeTruthy();
});

it('should check equality of 2 transactions of the same value via a static method', () => {
const transactionA = EvmInternalTransaction.create(exampleInput);
const transactionB = EvmInternalTransaction.create(exampleInput);

expect(EvmInternalTransaction.equals(transactionA, transactionB)).toBeTruthy();
});

it('should check inequality when chain is different', () => {
const transactionA = EvmInternalTransaction.create(exampleInput);
const transactionB = EvmInternalTransaction.create({ ...exampleInput, chain: '0x2' });

expect(transactionA.equals(transactionB)).toBeFalsy();
});

it('should check inequality when transactionHash is different', () => {
const transactionA = EvmInternalTransaction.create(exampleInput);
const transactionB = EvmInternalTransaction.create({
...exampleInput,
transactionHash: '0x2ac6283fb30fe33499416b0388ff27145a0eeb6aa8b37bca40af87d7xxxxxxxx',
});

expect(transactionA.equals(transactionB)).toBeFalsy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import Core, { MoralisDataObject, BigNumber, CoreProvider } from '@moralisweb3/common-core';
import { EvmAddress } from '../EvmAddress';
import { EvmChain } from '../EvmChain';
import { EvmInternalTransactionInput, EvmInternalTransactionData } from './types';

/**
* Valid input for a new EvmInternalTransaction instance.
* This can be an existing {@link EvmInternalTransaction} or a valid {@link EvmInternalTransactionInput} object
*/
export type EvmInternalTransactionish = EvmInternalTransactionInput | EvmInternalTransaction;

/**
* The EvmTranaction is a representation of a published transaction.
*
* Use this class any time you work with a transaction.
*
* @category DataType
*/
export class EvmInternalTransaction implements MoralisDataObject {
/**
* Create a new instance of EvmInternalTransaction from any valid transaction input
* @param data - the EvmInternalTransactionish type
* @example
* ```
* const transaction = EvmInternalTransaction.create(data);
*```
*/
static create(data: EvmInternalTransactionish, core?: Core) {
if (data instanceof EvmInternalTransaction) {
return data;
}
const finalCore = core ?? CoreProvider.getDefault();
return new EvmInternalTransaction(data, finalCore);
}

private _data: EvmInternalTransactionData;

constructor(data: EvmInternalTransactionInput, core: Core) {
this._data = EvmInternalTransaction.parse(data, core);
}

static parse = (data: EvmInternalTransactionInput, core: Core): EvmInternalTransactionData => ({
chain: EvmChain.create(data.chain),
from: EvmAddress.create(data.from, core),
to: EvmAddress.create(data.to, core),
transactionHash: data.transactionHash,
gas: BigNumber.create(data.gas),
gasUsed: BigNumber.create(data.gasUsed),
blockNumber: BigNumber.create(data.blockNumber),
blockHash: data.blockHash,
input: data.input,
output: data.output,
value: BigNumber.create(data.value),
type: data.type,
});

/**
* Check the equality between two Evm internal transactions
* @param dataA - The first transaction
* @param dataB - The second transaction
* @example
* ```ts
* EvmInternalTransaction.equals(dataA, dataB)
* ```
*/
static equals(dataA: EvmInternalTransactionish, dataB: EvmInternalTransactionish) {
const transactionA = EvmInternalTransaction.create(dataA);
const transactionB = EvmInternalTransaction.create(dataB);

if (!transactionA._data.chain.equals(transactionB._data.chain)) {
return false;
}

if (transactionA._data.transactionHash !== transactionB._data.transactionHash) {
return false;
}

return true;
}

/**
* Checks the equality of the current transaction with another evm transaction
* @param data - the transaction to compare with
* @example
* ```ts
* transaction.equals(data)
* ```
*/
equals(data: EvmInternalTransactionish): boolean {
return EvmInternalTransaction.equals(this, data);
}

toJSON() {
const data = this._data;
return {
...data,
to: data.to?.format(),
from: data.from?.format(),
gas: data.gas?.toString(),
gasUsed: data.gasUsed?.toString(),
value: data.value?.toString(),
chain: data.chain?.format(),
blockNumber: data.blockNumber?.toString(),
};
}

/**
* @returns a JSON represention of the transaction.
* @example
* ```
* transaction.format()
* ```
*/
format() {
return this.toJSON();
}

get result() {
return this._data;
}

get chain() {
return this._data.chain;
}

get transactionHash() {
return this._data.transactionHash;
}

get blockNumber() {
return this._data.blockNumber;
}

get blockHash() {
return this._data.blockHash;
}

get type() {
return this._data.type;
}

get from() {
return this._data.from;
}

get to() {
return this._data.to;
}

get value() {
return this._data.value;
}

get gas() {
return this._data.gas;
}

get gasUsed() {
return this._data.gasUsed;
}

get input() {
return this._data.input;
}

get output() {
return this._data.output;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './EvmInternalTransaction';
export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { BigNumber, BigNumberish } from '@moralisweb3/common-core';
import { EvmAddressish, EvmAddress } from '../EvmAddress';
import { EvmChain, EvmChainish } from '../EvmChain';

/**
* Valid EvmInternalTransactionLog input.
*
* @example
* ```ts
* const input = {
* "chain": "0x1",
* "transactionHash": "0x2ac6283fb30fe33499416b0388ff27145a0eeb6aa8b37bca40af87d7f1c74e2d",
* "block_number": 16876143,
* "block_hash": "0xc8d7592122307a771c5172af09699b5a2d36fa540d0fbc656f3d52c619c7536e",
* "type": "STATICCALL",
* "from": "0x283af0b28c62c092c9727f1ee09c02ca627eb7f5",
* "to": "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85",
* "value": "0",
* "gas": "263200",
* "gas_used": "2569",
* "input": "0x96e494e8d40a37cd10c71cb3896d1b05b6c707e29cb5aeff0278c6fc7e5e5b31623a1baa",
* "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
* }
* ```
*/
export interface EvmInternalTransactionInput {
chain: EvmChainish;
transactionHash: string;
blockNumber: BigNumberish;
blockHash: string;
type: string;
from: EvmAddressish;
to: EvmAddressish;
value: BigNumberish;
gas: BigNumberish;
gasUsed: BigNumberish;
input: string;
output: string;
}

/**
* Represents a processed internal transaction log.
*/
export interface EvmInternalTransactionData {
chain: EvmChain;
transactionHash: string;
blockNumber: BigNumber;
blockHash: string;
type: string;
from: EvmAddress;
to: EvmAddress;
value: BigNumber;
gas: BigNumber;
gasUsed: BigNumber;
input: string;
output: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { EvmNative } from '../EvmNative';
import { EvmTransactionLog } from '../EvmTransactionLog';
import { EvmSignature } from '../EvmSignature/EvmSignature';
import { EvmTransactionInput, EvmTransactionData } from './types';
import { EvmInternalTransaction } from '../EvmInternalTransaction';

/**
* Valid input for a new EvmTransaction instance.
Expand Down Expand Up @@ -69,6 +70,9 @@ export class EvmTransaction implements MoralisDataObject {
receiptStatus: maybe(data.receiptStatus, (status) => +status),

logs: (data.logs ?? []).map((log) => EvmTransactionLog.create(log)),
internalTransactions: (data.internalTransactions ?? []).map((transaction) =>
EvmInternalTransaction.create(transaction),
),

signature: maybe(data.signature, EvmSignature.create),
});
Expand Down Expand Up @@ -124,6 +128,7 @@ export class EvmTransaction implements MoralisDataObject {
chain: data.chain?.format(),
contractAddress: data.contractAddress?.format(),
logs: data.logs.map((log) => log.toJSON()),
internalTransactions: data.internalTransactions.map((transaction) => transaction.toJSON()),
signature: data.signature?.toJSON(),
blockNumber: data.blockNumber?.toString(),
blockTimestamp: data.blockTimestamp.toString(),
Expand Down Expand Up @@ -273,6 +278,17 @@ export class EvmTransaction implements MoralisDataObject {
return this._data.contractAddress;
}

/**
* @returns the internal transactions
* @example
* ```
* transaction.logs // EvmInternalTransaction[]
* ```
*/
get internalTransactions() {
return this._data.internalTransactions;
}

/**
* @returns the transaction logs
* @example
Expand Down
Loading

0 comments on commit 6d8dbac

Please sign in to comment.