Getting Started

Learn how to get started with the Tap Ants Voucher System.

Prerequisites

Before you begin working with the Tap Ants Voucher System, ensure you have the following:

  • Ethereum Development Environment: Familiarity with Ethereum development tools and concepts.
  • Wallet: An Ethereum wallet like MetaMask for interacting with the contracts.
  • Testnet ETH: Some Sepolia testnet ETH for testing (available from faucets).
  • Development Tools: Node.js, npm/yarn, and a code editor.

Installation

Foundry is the recommended development environment for working with the Tap Ants Voucher System.

Install Foundry

Terminal
# Install Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup

# Clone the repository
git clone https://github.com/tapants-com/tapants-voucher.git
cd tapants-voucher

# Install dependencies
forge install

# Build the contracts
forge build

Quick Start Guides

For Developers

1. Connect to Existing Contracts

Connect to the existing contracts on Sepolia testnet to start experimenting.

import { createConfig, http } from 'wagmi'
import { sepolia } from 'wagmi/chains'
import { useReadContract, useWriteContract } from 'wagmi'

// Contract addresses on Sepolia testnet
const VOUCHER_ADDRESS = "0x4223f47b1EB3bDB97d0117Ae50e2cC65309c22AE";
const REDEEM_ADDRESS = "0x6cD3B9C6a28851377FCf305D3C269C328797Cc5E";
const TOKEN_ADDRESS = "0x8f71a7503284c69eb200605b5ab11fabc555c865";

// Create wagmi config
const config = createConfig({
  chains: [sepolia],
  transports: {
    [sepolia.id]: http(),
  },
})

// Example: Reading voucher balance
function VoucherBalance({ address }) {
  const { data: balance } = useReadContract({
    address: VOUCHER_ADDRESS,
    abi: voucherAbi,
    functionName: 'balanceOf',
    args: [address],
  })
  
  return <div>Balance: {balance ? formatUnits(balance, 18) : '0'}</div>
}

2. Deploy Your Own Contracts

Deploy your own instances of the contracts for a custom implementation.

// Using Foundry
// Create a deployment script in the script directory

// Example script.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Script.sol";
import "../src/TapAntsVoucher.sol";
import "../src/VoucherRedeemContract.sol";
import "../src/RedemptionToken.sol";

contract DeployScript is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        // Deploy the contracts
        RedemptionToken token = new RedemptionToken("Redemption Token", "RTK", msg.sender);
        TapAntsVoucher voucher = new TapAntsVoucher("Tap Ants Voucher", "TAV", 1, msg.sender);
        VoucherRedeemContract redeem = new VoucherRedeemContract(msg.sender);

        // Configure the contracts
        voucher.setRedeemContract(address(redeem));
        redeem.addSupportedVoucher(address(voucher), address(token), 1);

        vm.stopBroadcast();
    }
}

For Administrators

1. Mint and Distribute Vouchers

As the contract owner, mint vouchers and distribute them to users.

import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
import { parseUnits } from 'viem'

function MintVouchers() {
  const { data: hash, writeContract } = useWriteContract()
  
  const { isLoading, isSuccess } = useWaitForTransactionReceipt({
    hash,
  })
  
  async function mintVouchers() {
    const userAddress = "0x..."; // User's address
    const amount = parseUnits("100", 18); // 100 vouchers
    
    writeContract({
      address: VOUCHER_ADDRESS,
      abi: voucherAbi,
      functionName: 'mint',
      args: [userAddress, amount],
    })
  }
  
  return (
    <button onClick={mintVouchers} disabled={isLoading}>
      {isLoading ? 'Minting...' : 'Mint Vouchers'}
    </button>
  )
}

2. Configure Redemption Rules

Set up the redemption rules for vouchers.

import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'

function ConfigureRedemption() {
  const { data: hash, writeContract } = useWriteContract()
  
  const { isLoading, isSuccess } = useWaitForTransactionReceipt({
    hash,
  })
  
  async function addSupportedVoucher() {
    const voucherAddress = "0x..."; // Voucher contract address
    const tokenAddress = "0x..."; // Redemption token address
    const conversionRate = 1; // 1:1 conversion rate
    
    writeContract({
      address: REDEEM_ADDRESS,
      abi: redeemAbi,
      functionName: 'addSupportedVoucher',
      args: [voucherAddress, tokenAddress, conversionRate],
    })
  }
  
  return (
    <button onClick={addSupportedVoucher} disabled={isLoading}>
      {isLoading ? 'Adding...' : 'Add Supported Voucher'}
    </button>
  )
}

For End Users

1. Check Voucher Balance

Check your voucher balance.

import { useAccount, useReadContract } from 'wagmi'
import { formatUnits } from 'viem'

function VoucherBalanceChecker() {
  const { address } = useAccount()
  
  const { data: balance } = useReadContract({
    address: VOUCHER_ADDRESS,
    abi: voucherAbi,
    functionName: 'balanceOf',
    args: [address],
  })
  
  return (
    <div>
      <h2>Your Voucher Balance</h2>
      <p>{balance ? formatUnits(balance, 18) : '0'} TAV</p>
    </div>
  )
}

2. Redeem Vouchers

Redeem your vouchers for tokens.

import { useAccount, useReadContract, useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
import { parseUnits } from 'viem'

function RedeemVouchers() {
  const { address } = useAccount()
  
  // Check allowance
  const { data: allowance } = useReadContract({
    address: VOUCHER_ADDRESS,
    abi: voucherAbi,
    functionName: 'allowance',
    args: [address, REDEEM_ADDRESS],
  })
  
  // Approve vouchers
  const { data: approveHash, writeContract: approve } = useWriteContract()
  
  // Wait for approval transaction
  const { isLoading: isApproving, isSuccess: isApproved } = useWaitForTransactionReceipt({
    hash: approveHash,
  })
  
  // Redeem vouchers
  const { data: redeemHash, writeContract: redeem } = useWriteContract()
  
  // Wait for redeem transaction
  const { isLoading: isRedeeming, isSuccess: isRedeemed } = useWaitForTransactionReceipt({
    hash: redeemHash,
  })
  
  async function handleRedeem() {
    const amount = parseUnits("10", 18); // 10 vouchers
    
    // Check if we need to approve first
    if (!allowance || allowance < amount) {
      await approve({
        address: VOUCHER_ADDRESS,
        abi: voucherAbi,
        functionName: 'approve',
        args: [REDEEM_ADDRESS, amount],
      })
    }
    
    // Then redeem the vouchers
    await redeem({
      address: REDEEM_ADDRESS,
      abi: redeemAbi,
      functionName: 'redeem',
      args: [VOUCHER_ADDRESS, amount, "0x"],
    })
  }
  
  return (
    <button 
      onClick={handleRedeem} 
      disabled={isApproving || isRedeeming}
    >
      {isApproving ? 'Approving...' : isRedeeming ? 'Redeeming...' : 'Redeem Vouchers'}
    </button>
  )
}