Skip to content

Instantly share code, notes, and snippets.

@NetanelBasal
Created June 24, 2025 18:38
Show Gist options
  • Save NetanelBasal/b200dd655f78b119ea9623cf1bc95780 to your computer and use it in GitHub Desktop.
Save NetanelBasal/b200dd655f78b119ea9623cf1bc95780 to your computer and use it in GitHub Desktop.
import {
createWalletClient,
encodeFunctionData,
http,
parseEther,
Hex,
createPublicClient,
encodePacked,
keccak256,
parseAbi,
} from "viem";
import { keys } from "./keys/keys.js";
import { sepolia } from "viem/chains";
import { privateKeyToAccount, sign } from "viem/accounts";
const netanel = privateKeyToAccount(keys.netanel.privateKey);
const tal = privateKeyToAccount(keys.tal.privateKey);
const contractAddress: Hex = "0x033eA0ff6b6C63032F95cA1796791D2FFAD9677B";
const batchCallAndSponsor: Hex = "0xBDBA7F921f8C8AFF014749Fc17324e832291bfB0";
const netanelWalletClient = createWalletClient({
account: netanel,
chain: sepolia,
transport: http(),
});
const talWalletClient = createWalletClient({
account: tal,
chain: sepolia,
transport: http(),
});
const readClient = createPublicClient({
chain: sepolia,
transport: http(),
});
const abi = parseAbi(["function ping() returns (address caller)"]);
const batchAbi = parseAbi([
"function execute((address to,uint256 value,bytes data)[] calls,bytes signature) payable",
"function execute((address to,uint256 value,bytes data)[] calls) payable",
"function nonce() view returns (uint256)",
]);
function toEthSignedMessageHash(messageHash: Hex): Hex {
return keccak256(
encodePacked(
["string", "bytes32"],
["\x19Ethereum Signed Message:\n32", messageHash]
)
);
}
export async function netanelPaysFlow() {
const batchCalls = [
{
to: contractAddress,
value: 0n,
data: encodeFunctionData({ abi, functionName: "ping" }),
},
{
to: "0x8C02B437016118aA9bca0EEE178Dc3eB7E175E87",
value: parseEther("0.01"),
data: "0x",
},
] as const;
const data = encodeFunctionData({
abi: batchAbi,
functionName: "execute",
args: [batchCalls],
});
const authorization = await netanelWalletClient.signAuthorization({
executor: "self",
contractAddress: batchCallAndSponsor,
});
const txHash = await netanelWalletClient.sendTransaction({
to: netanel.address,
data,
authorizationList: [authorization],
});
console.log(
"✅ Netanel paid & sent tx:",
`https://eth-sepolia.blockscout.com/tx/${txHash}`
);
}
// ─── Flow 2: Tal Pays (Delegated Execution) ──────────────────────────────
export async function talPaysFlow() {
const batchCalls = [
{
to: contractAddress,
value: 0n,
data: encodeFunctionData({ abi, functionName: "ping" }),
},
{
to: "0x8C02B437016118aA9bca0EEE178Dc3eB7E175E87",
value: parseEther("0.01"),
data: "0x",
} as const,
];
const encodedCall = encodePacked(
["address", "uint256", "bytes", "address", "uint256", "bytes"],
[
batchCalls[0].to,
batchCalls[0].value,
batchCalls[0].data,
batchCalls[1].to,
batchCalls[1].value,
batchCalls[1].data,
]
);
const nonce = await readClient.readContract({
address: netanel.address,
abi: batchAbi,
functionName: "nonce",
});
const digest = keccak256(
encodePacked(["uint256", "bytes"], [BigInt(nonce), encodedCall])
);
const signature = await sign({
hash: toEthSignedMessageHash(digest),
privateKey: keys.netanel.privateKey,
to: "hex",
});
const data = encodeFunctionData({
abi: batchAbi,
functionName: "execute",
args: [batchCalls, signature],
});
const txHash = await talWalletClient.sendTransaction({
to: netanel.address,
data,
});
console.log(
"✅ Tal paid gas & sent tx for Netanel:",
`https://eth-sepolia.blockscout.com/tx/${txHash}`
);
}
async function main() {
// await netanelPaysFlow();
// await talPaysFlow();
}
main().catch(console.error);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment