Karma Wallet is an Account Abstraction (AA) smart wallet that automatically rounds up your gas fees and sends the spare change to a Tip Jar.
For example, if your transaction gas fee is $0.087, Karma Wallet will round it up to the nearest $0.01 and send the difference ($0.003) to the Tip Jar. This way, every transaction you make leaves behind a little karma for a cause you care about.
Powered by ERC-4337 smart accounts, Karma Wallet seamlessly integrates this tipping logic into your wallet’s transaction flow without requiring any additional user action.
Gas fees are a necessary part of using the blockchain, but they’re often odd amounts that leave “dust” in the wallet. Karma Wallet turns these small amounts into meaningful contributions:
- Support open-source projects without actively thinking about it.
- Fund community initiatives in a passive, automated way.
- Gamify generosity by making every transaction part of a bigger giving story.
Instead of letting your micro-ETH fragments sit idle, they get converted into micro-donations.
Karma Wallet is built on ERC-4337 and works within the UserOperation lifecycle. Here’s the exact flow:
1️⃣ Transaction Initiation
- User interacts with a dApp via Karma Wallet.
- The wallet constructs a UserOperation with:
- sender = Karma Wallet smart account address.
- callData = Encoded function call to the target dApp contract.
- value = Transaction value.
- maxFeePerGas = Current gas settings.
- maxPriorityFeePerGas = Current gas settings.
- paymasterAndData = Empty bytes.
- signature = Empty bytes.
2️⃣ UserOperation is signed and sent to Bundler
- The wallet sends the UserOperation to the Bundler.
- The Bundler processes the UserOperation and sends it to the EntryPoint.
- The EntryPoint processes the UserOperation and sends it to the Account Abstraction smart contract.
3️⃣ Account Abstraction Smart Contract
function execute(address dest, uint256 value, bytes calldata func) external {
_requireFromEntryPointOrOwner();
uint256 gasPre = gasleft();
_call(dest, value, func);
uint256 gasPost = gasleft();
uint256 gasUsed = gasPre - gasPost;
uint256 balance = address(this).balance;
uint256 tip = _calculateTip(gasUsed);
if (tip < balance) {
_jar.deposit{value: tip}(gasUsed, tip);
}
}
function _calculateTip(uint256 gasUsed) internal view returns (uint256) {
uint256 ethPriceUSD = _oracleAdaptor.getLatestEthPriceInUsd();
uint256 gasCostInWei = gasUsed * tx.gasprice;
uint256 gasCostInUSD = (gasCostInWei * ethPriceUSD) / 1e18;
uint256 tipInUSD = 0;
uint256 roundingMultipleUSD = 1e16; // $0.01 in 18-decimal USD
if (gasCostInUSD > 0) {
uint256 remainder = gasCostInUSD % roundingMultipleUSD;
if (remainder > 0) {
tipInUSD = roundingMultipleUSD - remainder;
}
}
uint256 tipInWei = (tipInUSD * 1e18) / ethPriceUSD;
return tipInWei;
}
- The Account Abstraction smart contract processes the UserOperation and sends it to the EntryPoint.
gasPreandgasPostmeasure the gas consumed during the call._calculateTip(gasUsed)converts gas used into USD value and determines the rounded-up tip.- If the wallet has enough ETH to cover the tip, it’s sent to the _jar contract.
|
|
|
|
|
|
| Contract | Chain | Address |
|---|---|---|
| Karma Account Factory | Morph Holesky | 0xe2567b2a7214877d395dfa6ca72335644b26dc23 |
| Pyth Adaptor | Morph Holesky | 0x2ca12ac2cb30b9acbdb6d9c7dcfc895338904a93 |
- Clone the Alto Bundler repository.
git clone https://github.com/pimlicolabs/alto.git- Install the dependencies.
cd alto
pnpm install- Create Config for Morph Holesky Testnet at
alto-config.json.
{
"entrypoints": "0x0000000071727De22E5E9d8BAf0edAc6f37da032", // v0.7 Entrypoint
"executor-private-keys": "private-key",
"utility-private-key": "private-key",
"rpc-url": "https://rpc-quicknode-holesky.morphl2.io",
"safe-mode": false,
"port": 4337,
"chain-type": "op-stack",
"legacy-transactions": true
}- Update L1GasProxy Contract Address in
src/utils/preVerificationGasCalulator.ts:L588:
const opGasPriceOracle = getContract({
abi: OpL1FeeAbi,
- address: "0x420000000000000000000000000000000000000F",
+ address: "0x530000000000000000000000000000000000000f",
client: {
public: publicClient
}
})- Build the bundler.
pnpm build- Run the bundler.
./alto config="alto.config.json"- Start a local Proxy
npx local-cors-proxy --proxyUrl http://localhost:4337The following repository is a turborepo and divided into the following:
- apps/web - The web application built using Vite.
First install the dependencies by running the following:
pnpm install && cd apps/webThen fill in the Environment variables in apps/www/.env.local
VITE_REOWN_PROJECT_ID="reown-project-id"
VITE_BUNDLER_URL="http://localhost:8010/proxy"Then run the following command to start the application:
pnpm devThe current Karma Wallet is a modified SimpleSmartAccount, but future plans include:
- Migrating to Safe-based modular accounts.
- Using preExec and postExec hooks:
preExec: Estimate and lock in tip before execution.postExec: Execute final tip transfer after actual gas cost known.
- Supporting multi-recipient tips (split between multiple causes).
- Integrating Paymaster sponsorship for gasless tip donations.
The Karma Wallet is licensed under the MIT License.





