
Join the KibokoDAO Revolution: Limited NFTs to Shape the Future of Web3 in the African Savannah.
Welcome to Web3, a world where digital assets thrive, ownership is decentralized, and the power of community drives progress. In this brave new ecosystem, NFTs are more than just collectibles—they're your gateway to influence and innovation. At the heart of this evolution lies KibokoDAO NFTs, a Decentralized Autonomous Organization powered by membership NFTs on the Lisk blockchain and hosted on Rarible.Why Lisk?Lisk is redefining blockchain development with its modular approach, empowering de...

Payout Models for Content Creators: A Sustainable Future
Farcaster 2026 writing contest

Africa, We’re About to Get BaD: 7 Countries, One Mission, Infinite Vibes
In a world where DAOs are the new black and Web3 is more than just a buzzword you pretend to understand in front of your tech friends, BuildaDAO (BaD) is taking things to a whole new level of decentralized chaos and creativity. And guess what? We’re going BaD across SEVEN African countries. That’s right—seven places where jollof, nyama choma, bunny chow, and chapati are as essential as block explorers. Kenyans, you can store chapatis on decentralized nodes, your chapatis won't get messed with...
<100 subscribers

Join the KibokoDAO Revolution: Limited NFTs to Shape the Future of Web3 in the African Savannah.
Welcome to Web3, a world where digital assets thrive, ownership is decentralized, and the power of community drives progress. In this brave new ecosystem, NFTs are more than just collectibles—they're your gateway to influence and innovation. At the heart of this evolution lies KibokoDAO NFTs, a Decentralized Autonomous Organization powered by membership NFTs on the Lisk blockchain and hosted on Rarible.Why Lisk?Lisk is redefining blockchain development with its modular approach, empowering de...

Payout Models for Content Creators: A Sustainable Future
Farcaster 2026 writing contest

Africa, We’re About to Get BaD: 7 Countries, One Mission, Infinite Vibes
In a world where DAOs are the new black and Web3 is more than just a buzzword you pretend to understand in front of your tech friends, BuildaDAO (BaD) is taking things to a whole new level of decentralized chaos and creativity. And guess what? We’re going BaD across SEVEN African countries. That’s right—seven places where jollof, nyama choma, bunny chow, and chapati are as essential as block explorers. Kenyans, you can store chapatis on decentralized nodes, your chapatis won't get messed with...
Share Dialog
Share Dialog


A cautionary tale of a developer, a cat contract, and a tragic misunderstanding of trust.
Once upon a time in the land of Solidity, I wrote a smart contract. It was bold. It was elegant. It was… secure.
At least, that’s what I told myself.
public owner;
constructor() {
owner = tx.origin;
}
"Look at that!" I said, proudly flexing in front of my rubber duck. "Only the person who deployed this contract — me — can call the sensitive functions! tx.origin makes sure no impostors get through."
If only I knew.
One day, a new developer friend — let’s call her Mallory — sent me a new dApp to try out. It was called KittySnuggles.sol and promised to send you non-stop pictures of pixelated cats directly to your wallet.
How could I say no?
I clicked.
I approved the transaction.
It did, in fact, show me an adorable cat.
But then… my main contract — the one I lovingly named BankOfMe.sol — sent Mallory all my ETH.
Let’s rewind.
My contract had this:
withdrawAll() external {
require(tx.origin == owner, "Not the owner!");
payable(owner).transfer(address(this).balance);
}
Seems fine, right?
WRONG.
Here's what really happened when I clicked on KittySnuggles:
I interacted with KittySnuggles (a malicious contract).
It, in turn, called BankOfMe and triggered withdrawAll().
Inside BankOfMe, tx.origin was still me, because I initiated the transaction!
The contract happily thought I was the one calling it.
But msg.sender was KittySnuggles, not me.
Mallory's code ran. My ETH vanished.
You see, tx.origin is like that overly trusting friend who always believes whoever started the drama is the good guy — even if the drama has passed through a dozen toxic people on its way to you.
Meanwhile, msg.sender is more like a strict bouncer at a nightclub. It checks who is knocking right now, not who told someone to tell someone to knock.
Replace:
require(tx.origin == owner, "Not the owner!");
With:
require(msg.sender == owner, "Not the owner!");
Now, only the actual person or contract currently calling the function is checked — not the innocent human who just wanted cat pictures.
Never trust tx.origin for authentication. It's like leaving your keys under the doormat and being surprised when someone uses them.
And Mallory? She’s living her best life somewhere on-chain, sipping piña coladas and rolling in my Ether. And I still don’t have my pixel cats.
“In Solidity, trust the caller (
msg.sender), not the sender’s sender’s sender’s sender’s… ghost.”
A cautionary tale of a developer, a cat contract, and a tragic misunderstanding of trust.
Once upon a time in the land of Solidity, I wrote a smart contract. It was bold. It was elegant. It was… secure.
At least, that’s what I told myself.
public owner;
constructor() {
owner = tx.origin;
}
"Look at that!" I said, proudly flexing in front of my rubber duck. "Only the person who deployed this contract — me — can call the sensitive functions! tx.origin makes sure no impostors get through."
If only I knew.
One day, a new developer friend — let’s call her Mallory — sent me a new dApp to try out. It was called KittySnuggles.sol and promised to send you non-stop pictures of pixelated cats directly to your wallet.
How could I say no?
I clicked.
I approved the transaction.
It did, in fact, show me an adorable cat.
But then… my main contract — the one I lovingly named BankOfMe.sol — sent Mallory all my ETH.
Let’s rewind.
My contract had this:
withdrawAll() external {
require(tx.origin == owner, "Not the owner!");
payable(owner).transfer(address(this).balance);
}
Seems fine, right?
WRONG.
Here's what really happened when I clicked on KittySnuggles:
I interacted with KittySnuggles (a malicious contract).
It, in turn, called BankOfMe and triggered withdrawAll().
Inside BankOfMe, tx.origin was still me, because I initiated the transaction!
The contract happily thought I was the one calling it.
But msg.sender was KittySnuggles, not me.
Mallory's code ran. My ETH vanished.
You see, tx.origin is like that overly trusting friend who always believes whoever started the drama is the good guy — even if the drama has passed through a dozen toxic people on its way to you.
Meanwhile, msg.sender is more like a strict bouncer at a nightclub. It checks who is knocking right now, not who told someone to tell someone to knock.
Replace:
require(tx.origin == owner, "Not the owner!");
With:
require(msg.sender == owner, "Not the owner!");
Now, only the actual person or contract currently calling the function is checked — not the innocent human who just wanted cat pictures.
Never trust tx.origin for authentication. It's like leaving your keys under the doormat and being surprised when someone uses them.
And Mallory? She’s living her best life somewhere on-chain, sipping piña coladas and rolling in my Ether. And I still don’t have my pixel cats.
“In Solidity, trust the caller (
msg.sender), not the sender’s sender’s sender’s sender’s… ghost.”
Fabian Owuor
Fabian Owuor
No comments yet