-
Notifications
You must be signed in to change notification settings - Fork 166
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add chain event processors fro GMP API
- Loading branch information
Showing
6 changed files
with
380 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[ | ||
{ | ||
"name": "test-ethereum", | ||
"rpc": "https://ethereum-sepolia.rpc.subquery.network/public" | ||
}, | ||
{ | ||
"name": "test-avalanche", | ||
"rpc": "https://ava-testnet.public.blastapi.io/ext/bc/C/rpc" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,48 @@ | ||
const fs = require('fs'); | ||
|
||
const dotenv = require('dotenv'); | ||
|
||
// Load environment variables from .env file | ||
dotenv.config(); | ||
|
||
// Default configuration values | ||
const defaults = { | ||
// gRPC | ||
HOST: "localhost", | ||
PORT: "50051" | ||
PORT: "50051", | ||
|
||
// GMP API | ||
GMP_API_URL: "http://localhost:8080", | ||
}; | ||
|
||
function getConfig() { | ||
const serverHOST = process.env.HOST || defaults.HOST; | ||
const serverPort = process.env.PORT || defaults.PORT; | ||
|
||
const gmpAPIURL = process.env.GMP_API_URL || defaults.GMP_API_URL; | ||
|
||
return { | ||
serverHOST, | ||
serverPort | ||
serverPort, | ||
gmpAPIURL, | ||
}; | ||
} | ||
|
||
module.exports = getConfig; | ||
const chainsConfigFile = './chains.json'; | ||
|
||
function getChainConfig(chainName) { | ||
const chainsConfig = JSON.parse(fs.readFileSync(chainsConfigFile, 'utf8')); | ||
|
||
const chainConfig = chainsConfig.find(c => c.name === chainName); | ||
|
||
if (!chainConfig) { | ||
throw new Error(`RPC URL not found for chain: ${chainName}`); | ||
} | ||
|
||
return chainConfig; | ||
} | ||
|
||
module.exports = { | ||
getConfig, | ||
getChainConfig, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
const axios = require('axios'); | ||
const { ethers } = require('ethers'); | ||
const { getConfig, getChainConfig } = require('../config.js'); | ||
|
||
const { GMP_API_URL } = getConfig(); | ||
|
||
// ABI for the ContractCall and MessageApproved events | ||
const eventABI = [ | ||
"event MessageApproved(bytes32 indexed commandId, string sourceChain, string messageId, string sourceAddress, address indexed contractAddress, bytes32 indexed payloadHash)", | ||
]; | ||
|
||
const iface = new ethers.utils.Interface(eventABI); | ||
|
||
async function processMessageApprovedEvent(sourceChain, txHash, costAmount = "0", dryRun = false) { | ||
apiEvent = await constructAPIEvent(sourceChain, txHash, costAmount); | ||
console.log(apiEvent); | ||
|
||
if (dryRun === true) { | ||
return; | ||
} | ||
|
||
response = submitApproveEvent(apiEvent); | ||
console.log(response); | ||
} | ||
|
||
async function constructAPIEvent(destinationChain, txHash, costAmount) { | ||
try { | ||
const destinationChainConfig = getChainConfig(destinationChain); | ||
|
||
const provider = new ethers.providers.JsonRpcProvider(destinationChainConfig.rpc); | ||
|
||
// Fetch transaction receipt | ||
const receipt = await provider.getTransactionReceipt(txHash); | ||
|
||
if (!receipt) { | ||
throw new Error('Transaction receipt not found'); | ||
} | ||
|
||
// Find the relevant log | ||
const TOPIC = iface.getEventTopic('MessageApproved'); | ||
const relevantLog = receipt.logs.find(log => log.topics[0] === TOPIC); | ||
|
||
if (!relevantLog) { | ||
throw new Error('Relevant log not found'); | ||
} | ||
|
||
// Decode the event data | ||
const decodedLog = iface.parseLog(relevantLog); | ||
|
||
// Extract data from the decoded log | ||
const eventIndex = receipt.logs.indexOf(relevantLog); | ||
const eventID = `${txHash}-${eventIndex}`; | ||
const commandId = decodedLog.args.commandId; | ||
const messageId = decodedLog.args.messageId; | ||
const sourceChain = decodedLog.args.sourceChain; | ||
const sourceAddress = decodedLog.args.sourceAddress; | ||
const contractAddress = decodedLog.args.contractAddress; | ||
const payloadHash = ethers.utils.base64.encode(decodedLog.args.payloadHash); | ||
|
||
// Fetch block data for timestamp | ||
const block = await provider.getBlock(receipt.blockNumber); | ||
const timestamp = new Date(block.timestamp * 1000).toISOString(); | ||
|
||
// Construct the event object | ||
return { | ||
cost: { | ||
amount: costAmount, | ||
}, | ||
eventID, | ||
message: { | ||
destinationAddress: contractAddress, | ||
messageID: messageId, | ||
payloadHash, | ||
sourceAddress, | ||
sourceChain, | ||
}, | ||
meta: { | ||
commandID: commandId, | ||
finalized: true, | ||
fromAddress: receipt.from, | ||
timestamp, | ||
txID: txHash, | ||
}, | ||
type: 'MESSAGE_APPROVED', | ||
}; | ||
} catch (error) { | ||
console.error('Error:', error.message); | ||
throw error; | ||
} | ||
} | ||
|
||
async function submitApproveEvent(apiEvent) { | ||
destinationChain = apiEvent.destinationChain; | ||
const response = await axios.post(`${GMP_API_URL}/chains/${destinationChain}/events`, { | ||
events: [apiEvent], | ||
}); | ||
|
||
console.log('API Response:', response.data); | ||
return response.data; | ||
} | ||
|
||
module.exports = { | ||
processMessageApprovedEvent, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
const axios = require('axios'); | ||
const { ethers } = require('ethers'); | ||
const { getConfig, getChainConfig } = require('../config.js'); | ||
|
||
const { GMP_API_URL } = getConfig(); | ||
|
||
// ABI for the ContractCall event | ||
const eventABI = [ | ||
"event ContractCall(address indexed sender, string destinationChain, string destinationContractAddress, bytes32 indexed payloadHash, bytes payload)", | ||
]; | ||
|
||
const iface = new ethers.utils.Interface(eventABI); | ||
|
||
async function processContractCallEvent(sourceChain, txHash, dryRun = false) { | ||
apiEvent = await constructAPIEvent(sourceChain, txHash); | ||
console.log(apiEvent); | ||
|
||
if (dryRun === true) { | ||
return; | ||
} | ||
|
||
response = submitContractCallEvent(apiEvent); | ||
console.log(response); | ||
} | ||
|
||
async function constructAPIEvent(sourceChain, txHash) { | ||
try { | ||
const sourceChainConfig = getChainConfig(sourceChain); | ||
|
||
const provider = new ethers.providers.JsonRpcProvider(sourceChainConfig.rpc); | ||
|
||
// Fetch transaction receipt | ||
const receipt = await provider.getTransactionReceipt(txHash); | ||
|
||
if (!receipt) { | ||
throw new Error('Transaction receipt not found'); | ||
} | ||
|
||
// Find the relevant log | ||
const TOPIC = iface.getEventTopic('ContractCall'); | ||
const relevantLog = receipt.logs.find(log => log.topics[0] === TOPIC); | ||
|
||
if (!relevantLog) { | ||
throw new Error('Relevant log not found'); | ||
} | ||
|
||
// Decode the event data | ||
const decodedLog = iface.parseLog(relevantLog); | ||
|
||
// Extract data from the decoded log | ||
const eventIndex = receipt.logs.indexOf(relevantLog); | ||
const eventID = `${txHash}-${eventIndex}`; | ||
const sourceAddress = decodedLog.args.sender; | ||
const destinationChain = decodedLog.args.destinationChain; | ||
const destinationAddress = decodedLog.args.destinationContractAddress; | ||
const payloadHash = ethers.utils.base64.encode(decodedLog.args.payloadHash); | ||
const payload = ethers.utils.base64.encode(decodedLog.args.payload); | ||
|
||
// Fetch block data for timestamp | ||
const block = await provider.getBlock(receipt.blockNumber); | ||
const timestamp = new Date(block.timestamp * 1000).toISOString(); | ||
|
||
// Construct the event object | ||
return { | ||
destinationChain, | ||
eventID, | ||
message: { | ||
destinationAddress, | ||
messageID: eventID, | ||
payloadHash, | ||
sourceAddress, | ||
sourceChain, | ||
}, | ||
meta: { | ||
finalized: true, // Set to true if the event is finalized | ||
fromAddress: receipt.from, | ||
timestamp, | ||
txID: txHash, | ||
}, | ||
payload, | ||
type: 'CALL', | ||
}; | ||
} catch (error) { | ||
console.error('Error:', error.message); | ||
throw error; | ||
} | ||
} | ||
|
||
async function submitContractCallEvent(apiEvent) { | ||
sourceChain = apiEvent.message.sourceChain; | ||
const response = await axios.post(`${GMP_API_URL}/chains/${sourceChain}/events`, { | ||
events: [apiEvent], | ||
}); | ||
|
||
console.log('API Response:', response.data); | ||
return response.data; | ||
} | ||
|
||
module.exports = { | ||
processContractCallEvent, | ||
}; |
Oops, something went wrong.