Skip to content

Instantly share code, notes, and snippets.

@zengzengzenghuy
Last active April 16, 2025 11:00
Show Gist options
  • Select an option

  • Save zengzengzenghuy/42fce269a8282120d538208e62938f37 to your computer and use it in GitHub Desktop.

Select an option

Save zengzengzenghuy/42fce269a8282120d538208e62938f37 to your computer and use it in GitHub Desktop.
Given a xDAI relayTokens or Transfer transaction hash from Gnosis Chain, call executeSignature on Ethereum to claim DAI
import {
createPublicClient,
createWalletClient,
http,
parseAbiItem,
decodeEventLog,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { gnosis, mainnet } from "viem/chains";
// Get input from CLI
const [, , privateKey, txHash] = process.argv;
if (!privateKey || !txHash) {
console.error("Usage: node myScript.js <privateKey> <txHash>");
process.exit(1);
}
// === Set up Clients ===
const gnosisClient = createPublicClient({ chain: gnosis, transport: http() });
const ethereumClient = createPublicClient({
chain: mainnet,
transport: http(),
});
const account = privateKeyToAccount(privateKey);
const walletClient = createWalletClient({
account,
chain: mainnet,
transport: http(),
});
// === Step 1: Fetch UserRequestForSignature event from tx ===
const EVENT_SIGNATURE =
"event UserRequestForSignature(address recipient, uint256 value, bytes32 nonce)";
async function main() {
const receipt = await gnosisClient.getTransactionReceipt({ hash: txHash });
const userRequestLog = receipt.logs.find((log) => {
try {
const decoded = decodeEventLog({
abi: [parseAbiItem(EVENT_SIGNATURE)],
data: log.data,
topics: log.topics,
});
return decoded.eventName === "UserRequestForSignature";
} catch (e) {
return false;
}
});
if (!userRequestLog) {
console.error("UserRequestForSignature event not found in transaction");
return;
}
const decoded = decodeEventLog({
abi: [parseAbiItem(EVENT_SIGNATURE)],
data: userRequestLog.data,
topics: userRequestLog.topics,
});
const { recipient, value, nonce } = decoded.args;
console.log("Extracted Event Data:", { recipient, value, nonce });
// === Step 2: Call getMessageHash ===
const bridgeAddress = "0x2d51eaa266eafcb59bb36dd3c7e99c515e58113a";
const msgHash = await gnosisClient.readContract({
address: bridgeAddress,
abi: [
{
name: "getMessageHash",
type: "function",
inputs: [
{ name: "_recipient", type: "address" },
{ name: "_value", type: "uint256" },
{ name: "_origTxHash", type: "bytes32" },
],
outputs: [{ type: "bytes32" }],
},
],
functionName: "getMessageHash",
args: [recipient, value, nonce],
});
console.log("Message Hash:", msgHash);
// === Step 3: getMessage & getSignatures ===
const message = await gnosisClient.readContract({
address: bridgeAddress,
abi: [
{
name: "getMessage",
type: "function",
inputs: [{ name: "_msgHash", type: "bytes32" }],
outputs: [{ type: "bytes" }],
},
],
functionName: "getMessage",
args: [msgHash],
});
const signatures = await gnosisClient.readContract({
address: bridgeAddress,
abi: [
{
name: "getSignatures",
type: "function",
inputs: [{ name: "_msgHash", type: "bytes32" }],
outputs: [{ type: "bytes" }],
},
],
functionName: "getSignatures",
args: [msgHash],
});
if (signatures.length !== 524) {
console.error("Not enough signatures from validators");
return;
}
console.log("Signatures validated.");
// === Step 4: simulate executeSignatures on Ethereum ===
const ethereumBridgeAddress = "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016";
try {
const simulation = await ethereumClient.simulateContract({
address: ethereumBridgeAddress,
abi: [
{
name: "executeSignatures",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "message", type: "bytes" },
{ name: "signatures", type: "bytes" },
],
outputs: [],
},
],
functionName: "executeSignatures",
args: [message, signatures],
account,
});
console.log(
"Simulation successful, transaction request:",
simulation.request
);
// Optional: Send transaction
const hash = await walletClient.writeContract(simulation.request);
console.log("Transaction sent:", hash);
} catch (err) {
console.error("Simulation failed:", err.message);
}
}
main();
@zengzengzenghuy
Copy link
Author

zengzengzenghuy commented Apr 16, 2025

This script simplify the process to collect signatures from xDAI Bridge Helper contract and claim DAI on Ethereum xDAI Bridge contract.

You would need the tx hash that calls Transfer or relayTokens to the Gnosis Chain xDAI Bridge, and private key with enough ETH on Ethereum to claim DAI on Ethereum.

  1. Setup
npm init -y
npm install viem
  1. Copy and paste the script into index.js
  2. Run
    node index.js $YourPrivateKey $OriginTxHashFromGnosisChain

Make sure the account has enough ETH to call the tx on Ethereum

Check here if you want to interact with block explorer.

Fund your account with ETH from other networks with https://www.gas.zip/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment