DonationsFun: Building Cross-Chain with Axelar?MDS

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:

  • General Message Passing (GMP) as the gateway for sending messages and calling smart contracts across different chains
  • Interchain Token Service (ITS), built on top of GMP, for securely moving assets around.
  • Axelar Virtual Machine (AVM) as the execution layer that powers this cross-chain logic

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:

  • Human Rights Foundation?
  • Green Peace?
  • Internet Archive?
  • Save the Children?
  • …and (soon) more

How it?works

  1. Connect your Wallet

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

  • Expand Supported NGOs
  • Add more blockchain networks, like Sui Network, @Stacks, @Solana, @StellarOrg and MultiversX
  • Referral features to get more users donating and competing for the leaderboard


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:

  • from a supported chain to itself
  • from a supported chain to another supported chain
  • from a supported chain to a charity on an unsupported chain
  • from an unsupported chain to a charity on a supported chain


Axelar SDK Utilization

  • We have used the Axelar ITS contract interfaces in order to develop our smart contracts, making sure that they are compatible with the Axelar ITS protocol.

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;
}        

  • The Axelar JS SDKs are rather limiting, and don’t provide tools for interacting with the ITS contracts yet, so we had to make a custom solution.
  • The Axelar Query API SDK will be used to calculate the gas fees needed when doing a cross chain transaction.
  • The dApp also interacts with a custom microservice, which stores information in a PostgreSQL database, listens for blockchain events and uses Moralis & Alchemy APIs in order to retrieve blockchain information like tokens for an wallet address, token prices etc.


Transaction Flow

  1. Simple same chain donating

  • The user approves the token transfer towards the Donate contract
  • The user sends the transaction calling the donate function, which includes information about the charity, token and amount
  • The Donate contract forwards the amount to the charity


2. Cross chain flow from a supported chain to a charity on an unsupported chain

  • The user approves the token transfer towards the Donate contract?
  • The user sends the transaction calling the donateInterchain function, which includes information about the charity, token and amount, the ITS tokenId and the destination chain?
  • The Donate contract forwards the amount to the Axelar ITS contract, which will send it cross chain to the charity


3. Cross chain flow from a supported chain to a supported chain

  • The user approves the token transfer towards the Axelar ITS contract

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

  • Tech Stack: NestJs, React, Shadcn, TailwindCss
  • List of programming languages, frameworks, and tools used in developing Donations Fun: Solidity, Typescript, Wagmi?, ReOwn AppKit
  • Continuous Integration/Continuous Deployment (CI/CD): Digital Ocean & Github Wallet Integration: any EVM wallet supported by ReOwn AppKit library ( @Metamask?, @Waletconnect etc)


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!


要查看或添加评论,请登录

buidly的更多文章

社区洞察

其他会员也浏览了