Skip to content

Commit

Permalink
Merge pull request #19 from AbdulAhadArain/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
AbdulAhadArain authored Jul 18, 2024
2 parents 6bd760d + 6493ec9 commit dcdf6a4
Show file tree
Hide file tree
Showing 9 changed files with 2,725 additions and 2 deletions.
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
PORT=3000
MONGODB_URL=
MONGODB_URL=
RPC_URL=
EXPLORER_URL=
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"typescript": "^5.0.2"
},
"dependencies": {
"ethers": "^5.7.2",
"axios": "^1.5.0",
"cors": "^2.8.5",
"crypto-js": "^4.1.1",
Expand Down
18 changes: 17 additions & 1 deletion src/controllers/transactions.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const getTransaction = async (
const rpcUrl = matchedItem.rpcUrl;
const web3 = new Web3(rpcUrl);
const response = await web3.eth.getTransactionReceipt(
'0xcbd5570d6af92c82535e4640de2a26f1cefd976c07b890a8c9c3cc7023e79808',
req.params.txId as string,
);
if (response?.logs) {
res.send({
Expand All @@ -50,3 +50,19 @@ export const getTransaction = async (
next(error);
}
};

export const getTransactionMinedAndFinalizedDetail = async (
req: Request,
res: Response,
next: NextFunction,
): Promise<void> => {
try {
const response =
await transactionsService.fetchRemoteTransactionWithMinedAndFinalizedTx(
req.params.txId,
);
res.send(response);
} catch (error) {
next(error);
}
};
5 changes: 5 additions & 0 deletions src/routes/transactions.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ router.get(
validate(transactionValidation.getTransaction),
transactionController.getTransaction,
);
router.get(
'/finalized/:txId',
validate(transactionValidation.getTransaction),
transactionController.getTransactionMinedAndFinalizedDetail,
);

export default router;
263 changes: 263 additions & 0 deletions src/services/transaction.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import {
QuantumPortalRemoteTransaction,
} from '../interfaces';
import { chartData } from '../interfaces/QuantumPortalRemoteTransaction.interface';
import PocContractABI from '../utils/abi/poc.json';
import MGRContractABI from '../utils/abi/ledgerMgr.json';
import { ethers } from 'ethers';
import {
LEDGER_MANAGER_CONTRACT_ADDRESS,
MINER_ADDRESS,
} from '../utils/constants';

export const getTxs = async (
page: number,
Expand Down Expand Up @@ -130,3 +137,259 @@ export const totalTransactions = async (): Promise<Number> => {
const totalResults = await Promise.resolve(countPromise);
return totalResults;
};

export const fetchRemoteTransactionWithMinedAndFinalizedTx = async (
txHash: string,
) => {
const responseObj: any = {
sourceBlock: {},
registeredTxReceipt: {},
minedTxReceipt: {},
finalizedTxReceipt: {},
remoteTransactionRegistered: {},
targetBlock: {},
finalizedTxLog: {},
finalizedTransaction: {},
};
const contractAbi: any[] = [...PocContractABI.abi, ...MGRContractABI.abi];
contractAbi.push({
anonymous: false,
inputs: [
{
indexed: false,
internalType: 'string',
name: 'msg',
type: 'string',
},
],
name: 'Log',
type: 'event',
});
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
console.log('Fetching provider ', provider);
let tx = await provider.getTransaction(txHash);
console.log('Found transaction,', tx);
if (tx === null) {
return new Error('Transaction not found');
}
const block = await provider.getBlock(tx?.blockNumber);
console.log('Found block,', block);
responseObj.sourceBlock = {
targetChainId: tx.chainId,
nonce: tx.nonce,
timeStamp: block.timestamp,
};

// first decode the original call
const iface = new ethers.utils.Interface([
'function multiTransfer(address[] memory froms, uint[] memory inputs, address[] memory tos, uint[] memory values, uint blocknumber, bytes32 txid, uint timestamp, bytes memory remoteCall)',
]);
let inputs = iface.decodeFunctionData('multiTransfer', tx.data);

// console.log(inputs);
// console.log(inputs.remoteCall);

// now decode the remote call
const remoteCall = ethers.utils.defaultAbiCoder.decode(
['uint64', 'address', 'address', 'bytes', 'uint'],
ethers.utils.hexDataSlice(inputs.remoteCall, 0),
);

const receipt = await provider.getTransactionReceipt(txHash);
responseObj.registeredTxReceipt = receipt;
const infer = new ethers.utils.Interface(contractAbi);
console.log(receipt);

for (const log of receipt.logs) {
try {
let parsed = infer.parseLog(log);
if (parsed.name === 'RemoteTransactionRegistered') {
const eventArgs = parsed?.args;

// Print attribute names and their values
parsed?.eventFragment?.inputs.forEach((input: any, index: any) => {
if (input.type === 'tuple') {
responseObj.remoteTransactionRegistered[input.name] = {};
input.components.forEach((component: any, subIndex: any) => {
responseObj.remoteTransactionRegistered[input.name][
component.name
] = eventArgs[index][subIndex];
});
} else {
if (typeof eventArgs[index] == 'object') {
responseObj.remoteTransactionRegistered[input.name] =
ethers.utils.formatEther(eventArgs[index]);
} else
responseObj.remoteTransactionRegistered[input.name] =
eventArgs[index];
}
});
}
} catch (err) {
console.log('no receipt found for', txHash);
continue;
}
}
console.log('remote transaction', responseObj.remoteTransactionRegistered);

// let orgCallChainId = remoteCall[0];
let orgCallBeneficiary = remoteCall[1];
let orgCallTargetContract = remoteCall[2];
let orgCallMethodCall = remoteCall[3];

// now fetch the transactions from the miner
// TODO : This is a hack and it only works because we know the address of the miner/finalizer
// in reality we would not know this, we should instead store all txs to the ledger manager in a DB
let miner_address = MINER_ADDRESS;
const url = `${process.env.EXPLORER_URL}/api?module=account&action=txlist&address=${miner_address}`;

const response = await fetch(url);
const data = await response.json();

//console.log("received transaction list ", data);

const targetContracts = [
LEDGER_MANAGER_CONTRACT_ADDRESS, // ledger manager
];

// we only care about mine/finalise transactions
const filteredTransactions = data.result.filter((tx: any) =>
targetContracts.includes(tx.to),
);
// Sort transactions by block number, oldest block comes first, this allows us to find mine tx first
// before finalize
filteredTransactions.sort((a: any, b: any) => a.blockNumber - b.blockNumber);
//console.log(filteredTransactions);

// const ledgerabi = debugabis;
const inf = new ethers.utils.Interface(contractAbi);

let remoteBlockNonce;

// lets try to find the original transaction in the filtered list
for (const element of filteredTransactions) {
console.log('total transactions ', filteredTransactions.length);
console.log('searching ', element.hash);

try {
let tx = await provider.getTransaction(element.hash);
//console.log("Found transaction,", tx);

// first decode the original call
let inputs = inf.parseTransaction(tx);

//console.log({inputs});
// console.log(inputs.args);
// console.log(inputs.args.transactions);

if (inputs.args.transactions != undefined) {
for (let transaction of inputs.args.transactions) {
if (
transaction[1] == orgCallTargetContract &&
transaction[3] == orgCallBeneficiary &&
transaction[6][0] == orgCallMethodCall
) {
console.log('Found the mine transaction!!', transaction);
const receipt = await provider.getTransactionReceipt(element.hash);
responseObj.minedTxReceipt = receipt;
console.log(receipt);
for (const log of receipt.logs) {
try {
let parsed = inf.parseLog(log);
// Search for the target address in the parsed log
// if (JSON.stringify(parsed).includes(remoteCall[2])) {
// console.log(`Found target address in transaction: ${element.hash}`);
// console.log(parsed);
// }
console.log({ parsed });
let blockNonce = parsed.args[4].nonce;
console.log('Block nonce from mined tx is ', blockNonce);
remoteBlockNonce = blockNonce;
if (parsed.name === 'MinedBlockCreated') {
// remoteTransactionRegistered.eventName = parsed?.name;
// remoteTransactionRegistered.eventSignature = parsed?.signature;
const eventArgs = parsed?.args;

// Print attribute names and their values
parsed?.eventFragment?.inputs.forEach(
(input: any, index: any) => {
if (input.type === 'tuple') {
// console.log(`${input.name}:`);

input.components.forEach(
(component: any, subIndex: any) => {
if (typeof eventArgs[index][subIndex] == 'object') {
ethers.utils.formatEther(
(responseObj.targetBlock[input.name][
component.name
] = eventArgs[index][subIndex]),
);
} else
responseObj.targetBlock[input.name][
component.name
] = eventArgs[index][subIndex];
},
);
} else {
if (typeof eventArgs[index] == 'object') {
responseObj.targetBlock[input.name] =
ethers.utils.formatEther(eventArgs[index]);
} else
responseObj.targetBlock[input.name] =
eventArgs[index];
}
},
);
console.log({ targetBlock: responseObj.targetBlock });
}
} catch (err) {
console.log('no receipt found for', element.hash);
continue;
}
}
}
}
} else {
// console.log(inputs.args);
// console.log(typeof(remoteBlockNonce) );
// console.log(remoteBlockNonce.toString());
// console.log(inputs.args.blockNonce.integerValue() );
if (
remoteBlockNonce != undefined &&
remoteBlockNonce.toString() == inputs.args.blockNonce.toString()
) {
console.log('Found the finalize transaction!!', tx);
responseObj.finalizedTx = tx;
let inputs = inf.parseTransaction(tx);
console.log(inputs);

responseObj.finalizedTxLog.eventName = inputs?.name;
responseObj.finalizedTxLog.eventSignature = inputs?.signature;
const eventArgs = inputs?.args;

// Print attribute names and their values
inputs?.functionFragment?.inputs.forEach((input: any, index: any) => {
if (input.type === 'tuple') {
console.log(`${input.name}:`);
responseObj.finalizedTxLog[input.name] = {};
input.components.forEach((component: any, subIndex: any) => {
// console.log(component.name, ":", eventArgs[index][subIndex]);
responseObj.finalizedTxLog[input.name][component.name] =
eventArgs[index][subIndex];
});
} else {
// console.log(`${input.name}: ${eventArgs[index]}`);
responseObj.finalizedTxLog[input.name] = eventArgs[index];
}
});
console.log({
finalizedTxLog: responseObj.finalizedTxLog,
});
}
}
} catch {
continue;
}
}
return responseObj;
};
Loading

0 comments on commit dcdf6a4

Please sign in to comment.