DonationsFun: Building Cross-Chain with Axelar?MDS
Blockchains remain fragmented ecosystems, each functioning in its own silo. If we want a global, interconnected Web3, these chains need a common language. Axelar’s Mobius Development Stack offers that solution, making cross-chain communication seamless. MDS brings together three core components:
Donations Fun
Donations Fun leverages Axelar’s Mobius Development Stack to enable smooth, cross-chain charitable donations. MDS brings together a suite of tools that make interoperability easy and secure, so we can focus on what matters, helping you give back.
With Axelar’s Interchain Token Service (ITS), and Axelar Virtual Machine (AVM), Donations Fun connects multiple blockchains, allowing donations to flow effortlessly from one chain to another. Whether you’re on Ethereum, Avax, Optimism, or any supported chain, donating is as simple as sending tokens.
Axelar’s decentralized validator network ensures your donations are secure and resistant to censorship. Plus, with their SDK, we can easily expand the platform, adding more chains and features to enhance your experience.
?? Disclaimer: THE ONLY OFFICIAL WEBSITE IS AND WILL BE: https://donations.fun??
Right now, the dApp is on testnet, with donations available from Optimism, Avax, and Ethereum . Soon we’ll expand to all 69+ chains supported by Axelar. On mainnet, donations will initially be supported from Base and Optimism to charities on Ethereum or other chains.
(!) Early adopters, can test the platform, give us feedback, and help improve the experience. (Need test funds? dm us)
Supported Charities Include:
How it?works
2. Select a Charity from our list of trusted NGOs.
3. Choose Amount you want to donate.
?? Once these steps are completed, the donation is securely transferred to support meaningful causes globally. 100% of the amount will go to the charity, donations.fun does not perceive any fees.
Integration with???
Donations can be made anonymously using any wallet. However, if you want to make your donation public and encourage others to contribute as well, you can optionally connect an X account that will be associated with your address. Multiple addresses can be connected to the same X account.
Afterwards, if you donate with supported analytic tokens ( #axlUSDC, #axlETH, #wAVAX & more), your total amount in $ (dollars) will be calculated periodically and you can appear on the leaderboard.
If you associate an X account you also have access to the My Donations page in order to view all your last donations & analytics.
Future Developments
Meet the Developer
Rare? ?erban built Donations Fun with a focus on smart contracts across EVM chains and MultiversX. Inspired by platforms like pump.fun and friend.tech?, Rares wanted a SocialFi app with real-world impact, not just another product profiting off users. Buidly’s expertise in interchain development helped ensure seamless cross-chain integration, so you can donate no matter where you are in the crypto ecosystem.
Technical dive
Smart contract code is open source and can be found on Github.
The EVM contract is relatively simple and is only used as a proxy, in order to store analytics & emit events regarding the Leaderboard functionality. It stores a list of analyticsTokens which will be used when calculating the leaderboard $ (dollar) amount. Even though we support any token a user has in its wallet for donations, we need this list in order to prevent abuses for example a user donating with a memecoin with low liquidity for having their donation $ amount being too large.
领英推荐
We also store the knownCharities in the contract, making sure that the destination address of a transfer is correct. We also store knownCharitiesInterchain, which are charities that we support donating to but reside on another chain (eg: donating to a charity on Ethereum from Base).
The donate function will do a basic donation, sending the amount of the user to the charity on the same chain, calculating the analytics for that address and emitting some events. The platform does not take any fee percentage.
function donate(string calldata charityName, address token, uint256 amount) external payable {
bytes32 charityId = keccak256(abi.encodePacked(charityName));
address user = msg.sender;
address charityAddress = _donate(user, charityId, token, amount);
IERC20(token).safeTransferFrom(user, charityAddress, amount);
emit Donation(user, token, charityId, charityName, amount);
}
function _donate(address user, bytes32 charityId, address token, uint256 amount) internal returns (address) {
address charityAddress = knownCharities[charityId];
require(charityAddress != address(0), "charity does not exist");
require(amount > 0, "Donation amount must be greater than zero");
_handleAnalytics(user, token, amount);
return charityAddress;
}
function _handleAnalytics(address user, address token, uint256 amount) internal {
if (user != address(0) && analyticsTokens[token]) {
TokenAnalytic[] storage analytics = addressAnalytics[user];
bool tokenFound = false;
for (uint i = 0; i < analytics.length; i++) {
if (analytics[i].token == token) {
analytics[i].amount += amount;
tokenFound = true;
break;
}
}
if (!tokenFound) {
analytics.push(TokenAnalytic({
token: token,
amount: amount
}));
}
}
}
The donateInterchain function will do a cross chain donation, sending the amount of the user to a charity on another chain. This is done by calling the Axelar Interchain Token Service contract on that chain, doing an interchainTransfer using supported ITS tokens.
function donateInterchain(
string calldata charityName,
address token,
uint256 amount,
bytes32 tokenId,
string calldata destinationChain
) external payable {
bytes32 charityId = keccak256(abi.encodePacked(charityName));
address user = msg.sender;
bytes storage charityAddress = _donateInterchain(user, charityId, token, amount, destinationChain);
IERC20 tokenInterface = IERC20(token);
tokenInterface.safeTransferFrom(user, address(this), amount);
tokenInterface.approve(interchainTokenService, amount);
IInterchainTokenService(interchainTokenService)
.interchainTransfer{value: msg.value}(
tokenId,
destinationChain,
charityAddress,
amount,
"",
msg.value
);
InterchainData memory data = InterchainData(tokenId, destinationChain);
emit DonationInterchain(user, token, charityId, charityName, amount, data);
}
function _donateInterchain(address user, bytes32 charityId, address token, uint256 amount, string calldata destinationChain) internal returns (bytes storage) {
KnownCharityInterchain storage knownCharityInterchain = knownCharitiesInterchain[charityId];
require(knownCharityInterchain.charityAddress.length > 0, "charity does not exist");
require(
keccak256(abi.encodePacked(knownCharityInterchain.destinationChain))
== keccak256(abi.encodePacked(destinationChain)),
"invalid destination chain"
);
require(amount > 0, "Donation amount must be greater than zero");
_handleAnalytics(user, token, amount);
return knownCharityInterchain.charityAddress;
}
The contract can also be called from another chain by using directly the ITS contract callContractWithInterchainToken function. In this case, our Donate contract will get called on the _executeWithInterchainToken function with the token amount already being transferred to the contract, after which we transfer the amount directly to the charity and perform analytic operations. This method is used when donating for example from a chain which doesn’t have any Donate contract deployed on it (eg: Fantom) towards a charity on a supported chain (eg: Base)
function _executeWithInterchainToken(
bytes32, // commandId
string calldata sourceChain,
bytes calldata sourceAddress,
bytes calldata payload,
bytes32, // itsTokenId
address token,
uint256 amount
) override internal virtual {
// Decodes the encodePacked encoded payload, which should be easy to create from other chains without abi support
bytes32 charityId = payload.toBytes32(0);
address user = payload.toAddress(32); // can be zero address
address charityAddress = _donate(user, charityId, token, amount);
IERC20(token).safeTransfer(charityAddress, amount);
CrossChainData memory data = CrossChainData(sourceChain, sourceAddress);
emit DonationCrosschain(user, token, charityId, amount, data);
}
We this design we can support donating cross chain in a number of combinations:
Axelar SDK Utilization
interface IInterchainTokenExecutable {
/**
* @notice This will be called after the tokens are sent to this contract.
* @dev Execution should revert unless the msg.sender is the InterchainTokenService
* @param commandId The unique message id for the call.
* @param sourceChain The name of the source chain.
* @param sourceAddress The address that sent the contract call.
* @param data The data to be processed.
* @param tokenId The tokenId of the token manager managing the token.
* @param token The address of the token.
* @param amount The amount of tokens that were sent.
* @return bytes32 Hash indicating success of the execution.
*/
function executeWithInterchainToken(
bytes32 commandId,
string calldata sourceChain,
bytes calldata sourceAddress,
bytes calldata data,
bytes32 tokenId,
address token,
uint256 amount
) external returns (bytes32);
}
interface IInterchainTokenService {
function interchainTransfer(
bytes32 tokenId,
string calldata destinationChain,
bytes calldata destinationAddress,
uint256 amount,
bytes calldata metadata,
uint256 gasValue
) external payable;
}
Transaction Flow
2. Cross chain flow from a supported chain to a charity on an unsupported chain
3. Cross chain flow from a supported chain to a supported chain
The user sends the transaction calling the interchainTransfer function, which includes information about the the ITS tokenId, the destination chain, the token and the amount, as well as custom payload information for the Donate contract on the other chain which includes the charity id and an optional destination chain user address
Developer Tools and Technologies
With Donations Fun, it’s easier than ever to support the causes you care about, no matter where you are in the crypto ecosystem. Powered by Axelar, we’re breaking down barriers to make cross-chain donations as simple as possible.
Be part of something that’s changing the world, one transaction at a time!