Impersonation
Send transactions from any address on your Stagenet — without needing its private key.
Use it to:
- Call gated functions as an owner, admin, or multisig.
- Move tokens from whale addresses into target accounts.
- Reproduce flows that depend on specific production wallets.
How it works
When you impersonate an address, that address is added to an allowlist on your Stagenet.
Your Stagenet will then accept eth_sendTransaction requests where the from field matches the impersonated address.
No private key or signature is required. The EVM sees the impersonated address as the transaction’s msg.sender.
Impersonation only works with eth_sendTransaction, where the transaction is submitted to the Stagenet RPC as an unsigned transaction request.
It does not work with eth_sendRawTransaction. Raw transactions are already signed locally, so the sender is fixed by the signature before the transaction reaches your Stagenet.
Use a JSON-RPC signer — one that sends transaction requests through the Stagenet RPC, rather than signing locally with a private key:
- ethers:
provider.getSigner(address) - viem:
createWalletClient({ account: address, transport: http(stagenetUrl) })
Methods
stagenet.impersonateAccount(address)
Adds an address to the impersonation allowlist.
await stagenet.impersonateAccount("0x28C6c06298d514Db089934071355E5743bf21d60");Returns true on success.
stagenet.stopImpersonatingAccount(address)
Removes an address from the impersonation allowlist. After this, unsigned transactions from that address are rejected unless it is impersonated again.
await stagenet.stopImpersonatingAccount(whale);Returns true on success.
stagenet.getImpersonatedAccounts()
Lists every address currently on the allowlist (lowercased).
const accounts = await stagenet.getImpersonatedAccounts();Returns string[].
Usage
ethers
import { ethers } from "ethers";
import { createStagenet } from "contract.dev";
const stagenetUrl = "<YOUR_STAGENET_RPC_URL>";
const stagenet = createStagenet(stagenetUrl);
const provider = new ethers.JsonRpcProvider(stagenetUrl);
const whale = "0x28C6c06298d514Db089934071355E5743bf21d60";
const recipient = "0x1111111111111111111111111111111111111111";
const usdc = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
// 1. Start impersonating
await stagenet.impersonateAccount(whale);
// 2. Get a JSON-RPC signer for the address
const signer = await provider.getSigner(whale);
// 3. Send a transaction as the impersonated address
const erc20 = new ethers.Contract(
usdc,
["function transfer(address to, uint256 amount) returns (bool)"],
signer,
);
await erc20.transfer(recipient, 1_000_000n);
// 4. Stop impersonating when finished
await stagenet.stopImpersonatingAccount(whale);viem
import { createWalletClient, http } from "viem";
import { createStagenet } from "contract.dev";
const stagenetUrl = "<YOUR_STAGENET_RPC_URL>";
const stagenet = createStagenet(stagenetUrl);
const whale = "0x28C6c06298d514Db089934071355E5743bf21d60";
// 1. Start impersonating
await stagenet.impersonateAccount(whale);
// 2. Create a wallet client using only the address
const wallet = createWalletClient({
account: whale,
transport: http(stagenetUrl),
});
// 3. Send a transaction as the impersonated address
await wallet.writeContract({
address: usdcAddress,
abi: erc20Abi,
functionName: "transfer",
args: [recipient, 1_000_000n],
});
// 4. Stop impersonating when finished
await stagenet.stopImpersonatingAccount(whale);Multiple accounts
You can impersonate multiple addresses at the same time.
The JSON-RPC signer you use determines which impersonated address is used as msg.sender.
await stagenet.impersonateAccount(alice);
await stagenet.impersonateAccount(bob);
const aliceSigner = await provider.getSigner(alice);
const bobSigner = await provider.getSigner(bob);
await myContract.connect(aliceSigner).vote(1);
await myContract.connect(bobSigner).vote(2);Topping up gas
Impersonated accounts still need native tokens to pay for gas. If the address is empty, fund it with addBalance first:
await stagenet.impersonateAccount(whale);
await stagenet.addBalance(whale, 10n ** 18n); // 1 ETH for gas