diff --git a/src/smart-contracts/.env.example b/src/smart-contracts/.env.example deleted file mode 100644 index bf56017..0000000 --- a/src/smart-contracts/.env.example +++ /dev/null @@ -1,14 +0,0 @@ -# Configuration for Sei blockchain integration -# Copy this file to .env and replace with your actual values - -# Sei testnet configuration - Atlantic-1 -NEXT_PUBLIC_SEI_NETWORK="atlantic-1" -NEXT_PUBLIC_SEI_RPC_URL="https://rpc.atlantic-1.sei.io" -NEXT_PUBLIC_SEI_REST_URL="https://rest.atlantic-1.sei.io" - -# WILD token contract address -# Replace with your deployed contract address after deployment -NEXT_PUBLIC_SEI_TOKEN_FACTORY_ADDRESS="sei1..." - -# Optional: Wallet for automated tests (use only for testing) -# SEI_TEST_MNEMONIC="your twelve word mnemonic here only for testing" \ No newline at end of file diff --git a/src/smart-contracts/README.md b/src/smart-contracts/README.md deleted file mode 100644 index f26069d..0000000 --- a/src/smart-contracts/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Wildlife Token Smart Contract - -This directory contains the smart contract code for the Wildlife Token (WILD) used in the Wildlife Sightings application. - -## Overview - -The `WildlifeToken.sol` contract is an ERC-20 token designed to be deployed on the Sei EVM. It provides functionality for: - -- Minting tokens as rewards for wildlife documentation -- Setting different reward amounts for various wildlife species -- Managing authorized distributors who can award tokens - -## Deployment Instructions - -To deploy this contract on the Sei EVM: - -1. Install Hardhat or Truffle as your Ethereum development framework -2. Configure your deployment script to target Sei EVM -3. Deploy with an RPC endpoint for Sei testnet (atlantic-1): - - RPC URL: https://rpc.atlantic-1.sei.io - - Chain ID: 1 - -## Contract Configuration - -Before deployment, you may want to customize: - -- The token name and symbol -- Maximum token supply -- Initial token distribution -- Default reward amounts for different species - -## Authorizing the Backend - -After deployment, you'll need to: - -1. Call `setDistributor(backendAddress, true)` to authorize your backend server to distribute tokens -2. Configure the backend with the deployed contract address -3. Update the `.env` file with: - ``` - NEXT_PUBLIC_SEI_TOKEN_FACTORY_ADDRESS=0x... (your deployed contract address) - ``` - -## Security Considerations - -- Only authorized distributors can mint new tokens -- The contract has a capped supply to prevent inflation -- The owner can update reward amounts for different species - -## Testing - -Before mainnet deployment, thoroughly test the contract on Sei testnet: - -1. Test token minting and distribution -2. Verify reward calculations for different species -3. Check authorization controls \ No newline at end of file diff --git a/src/smart-contracts/WildlifeToken.sol b/src/smart-contracts/WildlifeToken.sol deleted file mode 100644 index e33886d..0000000 --- a/src/smart-contracts/WildlifeToken.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; - -/** - * @title WildlifeToken - * @dev ERC20 token for rewarding wildlife documentation contributions - * To be deployed on Sei Atlantic-1 testnet EVM - */ -contract WildlifeToken is ERC20Capped, Ownable { - // Events - event TokensAwarded(address indexed recipient, uint256 amount, string species); - - // Reward amounts for different wildlife types - mapping(string => uint256) public speciesRewards; - - // List of authorized reward distributors - mapping(address => bool) public authorizedDistributors; - - // Maximum token supply: 100 million tokens - uint256 private constant MAX_SUPPLY = 100_000_000 * 10**18; - - /** - * @dev Constructor that sets up the Wildlife Token - */ - constructor() - ERC20("Wildlife Conservation Token", "WILD") - ERC20Capped(MAX_SUPPLY) - Ownable(msg.sender) - { - // Initialize with base reward values (in tokens, will be multiplied by decimals) - speciesRewards["default"] = 10 * 10**18; // Default reward - - // Rare species receive higher rewards - speciesRewards["elephant"] = 25 * 10**18; - speciesRewards["tiger"] = 25 * 10**18; - speciesRewards["rhino"] = 30 * 10**18; - speciesRewards["panda"] = 25 * 10**18; - speciesRewards["gorilla"] = 25 * 10**18; - speciesRewards["whale"] = 25 * 10**18; - speciesRewards["dolphin"] = 15 * 10**18; - speciesRewards["snow leopard"] = 30 * 10**18; - speciesRewards["eagle"] = 15 * 10**18; - - // Set contract creator as an authorized distributor - authorizedDistributors[msg.sender] = true; - - // Mint initial supply for rewards pool (10% of total supply) - _mint(msg.sender, MAX_SUPPLY / 10); - } - - /** - * @dev Add or update a distributor authorization - * @param distributor Address to authorize or deauthorize - * @param authorized True to authorize, false to deauthorize - */ - function setDistributor(address distributor, bool authorized) external onlyOwner { - authorizedDistributors[distributor] = authorized; - } - - /** - * @dev Set the reward amount for a specific species - * @param species Name of the wildlife species - * @param amount Reward amount in tokens - */ - function setSpeciesReward(string calldata species, uint256 amount) external onlyOwner { - speciesRewards[species] = amount; - } - - /** - * @dev Award tokens to a user for documenting wildlife - * @param recipient Address of the recipient - * @param species Name of the wildlife species documented - * @return Amount of tokens awarded - */ - function awardTokens(address recipient, string calldata species) external returns (uint256) { - // Check if caller is an authorized distributor - require(authorizedDistributors[msg.sender], "Not authorized to distribute tokens"); - - // Get reward amount for the species (or default if not set) - uint256 rewardAmount = speciesRewards[species]; - if (rewardAmount == 0) { - rewardAmount = speciesRewards["default"]; - } - - // Mint tokens to the recipient - _mint(recipient, rewardAmount); - - // Emit event for tracking - emit TokensAwarded(recipient, rewardAmount, species); - - return rewardAmount; - } - - /** - * @dev Override _mint to respect the cap - */ - function _mint(address account, uint256 amount) internal override(ERC20, ERC20Capped) { - ERC20Capped._mint(account, amount); - } -} \ No newline at end of file diff --git a/src/smart-contracts/cosmwasm/README.md b/src/smart-contracts/cosmwasm/README.md deleted file mode 100644 index 214dc7b..0000000 --- a/src/smart-contracts/cosmwasm/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Wildlife Token CosmWasm Contract - -This directory contains the CosmWasm smart contract code for the Wildlife Token (WILD) used in the Wildlife Sightings application. - -## Overview - -The `wildlife_token.rs` contract is a CW20-compatible token designed for the Sei blockchain. It builds on the standard CW20 token implementation and adds functionality for: - -- Minting tokens as rewards for wildlife documentation -- Setting different reward amounts for various wildlife species -- Managing authorized distributors who can award tokens - -## Deployment Instructions - -To deploy this contract on the Sei Atlantic-1 testnet: - -1. Set up a Rust development environment with CosmWasm -2. Compile the contract to Wasm -3. Deploy the compiled contract using a Sei wallet with: - ``` - sei-cli tx wasm store wildlife_token.wasm --from --chain-id atlantic-1 --gas-prices 0.01usei - ``` -4. Instantiate the deployed contract: - ``` - sei-cli tx wasm instantiate '{"name":"Wildlife Conservation Token","symbol":"WILD","decimals":6}' --from --label "Wildlife Token" --chain-id atlantic-1 - ``` - -## Contract Configuration - -The contract is pre-configured with: - -- Token name: "Wildlife Conservation Token" -- Token symbol: "WILD" -- Decimals: 6 -- Initial supply: 10% of max supply (10,000,000 WILD) -- Default reward: 10 WILD per sighting -- Rare species rewards: 15-30 WILD depending on rarity - -## Authorizing the Backend - -After deployment, you'll need to: - -1. Call the `add_distributor` function to authorize your backend server to distribute tokens -2. Configure the backend with the deployed contract address -3. Update the `.env` file with: - ``` - NEXT_PUBLIC_SEI_TOKEN_FACTORY_ADDRESS=sei1... (your deployed contract address) - ``` - -## Testing - -Before using on the testnet, thoroughly test the contract using: - -1. Unit tests for contract functionality -2. Test token minting and distribution on the Sei testnet -3. Verify reward calculations for different species - -## Contract Messages - -The contract supports the following messages: - -### Execute Messages - -- Standard CW20 messages for transfers, allowances, etc. -- `set_species_reward`: Set the reward amount for a specific species -- `award_tokens`: Mint and send tokens to a user who documented wildlife -- `add_distributor`: Authorize an address to award tokens -- `remove_distributor`: Remove distributor authorization - -### Query Messages - -- Standard CW20 balance and token info queries -- `species_reward`: Get the reward amount for a specific species -- `is_distributor`: Check if an address is authorized to award tokens -- `all_species_rewards`: List all configured species and their reward amounts \ No newline at end of file diff --git a/src/smart-contracts/cosmwasm/wildlife_token.rs b/src/smart-contracts/cosmwasm/wildlife_token.rs deleted file mode 100644 index 1017e5c..0000000 --- a/src/smart-contracts/cosmwasm/wildlife_token.rs +++ /dev/null @@ -1,352 +0,0 @@ -// wildlife_token.rs - CosmWasm contract for Wildlife Token (WILD) -// This is a simplified example of a CosmWasm contract for the Sei blockchain -// To be deployed on Atlantic-1 testnet - -use cosmwasm_std::{ - entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, Uint128, - Addr, -}; -use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg}; -use cw20_base::contract::{execute as cw20_execute, query as cw20_query}; -use cw20_base::msg::{ExecuteMsg as Cw20ExecuteMsg, QueryMsg as Cw20QueryMsg}; -use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -// Custom messages for our Wildlife Token contract -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -#[serde(rename_all = "snake_case")] -pub enum ExecuteMsg { - // Standard CW20 messages - Transfer { recipient: String, amount: Uint128 }, - Burn { amount: Uint128 }, - Send { contract: String, amount: Uint128, msg: Binary }, - IncreaseAllowance { spender: String, amount: Uint128, expires: Option }, - DecreaseAllowance { spender: String, amount: Uint128, expires: Option }, - TransferFrom { owner: String, recipient: String, amount: Uint128 }, - BurnFrom { owner: String, amount: Uint128 }, - SendFrom { owner: String, contract: String, amount: Uint128, msg: Binary }, - // Custom Wildlife Token messages - SetSpeciesReward { species: String, amount: Uint128 }, - AwardTokens { recipient: String, species: String }, - AddDistributor { address: String }, - RemoveDistributor { address: String }, -} - -// State for tracking distributors and species rewards -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct WildlifeState { - pub distributors: Vec, - pub species_rewards: HashMap, -} - -// Initialize the contract -#[entry_point] -pub fn instantiate( - deps: DepsMut, - _env: Env, - info: MessageInfo, - msg: InstantiateMsg, -) -> StdResult { - // Initialize CW20 token - let token_info = TokenInfo { - name: "Wildlife Conservation Token".to_string(), - symbol: "WILD".to_string(), - decimals: 6, - total_supply: Uint128::zero(), - mint: Some(MinterData { - minter: info.sender.clone(), - cap: Some(Uint128::from(100_000_000_000_000u128)), // 100 million tokens with 6 decimals - }), - }; - TOKEN_INFO.save(deps.storage, &token_info)?; - - // Initialize wildlife-specific state - let mut species_rewards = HashMap::new(); - - // Set default reward - species_rewards.insert("default".to_string(), Uint128::from(10_000_000u128)); // 10 WILD - - // Set rewards for rare species - species_rewards.insert("elephant".to_string(), Uint128::from(25_000_000u128)); - species_rewards.insert("tiger".to_string(), Uint128::from(25_000_000u128)); - species_rewards.insert("rhino".to_string(), Uint128::from(30_000_000u128)); - species_rewards.insert("panda".to_string(), Uint128::from(25_000_000u128)); - species_rewards.insert("gorilla".to_string(), Uint128::from(25_000_000u128)); - species_rewards.insert("whale".to_string(), Uint128::from(25_000_000u128)); - species_rewards.insert("dolphin".to_string(), Uint128::from(15_000_000u128)); - species_rewards.insert("snow leopard".to_string(), Uint128::from(30_000_000u128)); - species_rewards.insert("eagle".to_string(), Uint128::from(15_000_000u128)); - - let wildlife_state = WildlifeState { - distributors: vec![info.sender.clone()], - species_rewards, - }; - - WILDLIFE_STATE.save(deps.storage, &wildlife_state)?; - - // Mint initial supply for rewards pool (10% of max supply) - let initial_supply = Uint128::from(10_000_000_000_000u128); - cw20_execute( - deps, - _env, - info, - Cw20ExecuteMsg::Mint { - recipient: info.sender.to_string(), - amount: initial_supply, - }, - )?; - - Ok(Response::default() - .add_attribute("method", "instantiate") - .add_attribute("token_name", "Wildlife Conservation Token") - .add_attribute("token_symbol", "WILD") - .add_attribute("initial_supply", initial_supply)) -} - -// Execute function for handling messages -#[entry_point] -pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> StdResult { - match msg { - // For standard CW20 operations, use the base implementation - ExecuteMsg::Transfer { recipient, amount } => { - cw20_execute(deps, env, info, Cw20ExecuteMsg::Transfer { recipient, amount }) - } - ExecuteMsg::Burn { amount } => { - cw20_execute(deps, env, info, Cw20ExecuteMsg::Burn { amount }) - } - ExecuteMsg::Send { contract, amount, msg } => { - cw20_execute(deps, env, info, Cw20ExecuteMsg::Send { contract, amount, msg }) - } - ExecuteMsg::IncreaseAllowance { spender, amount, expires } => { - cw20_execute(deps, env, info, Cw20ExecuteMsg::IncreaseAllowance { spender, amount, expires }) - } - ExecuteMsg::DecreaseAllowance { spender, amount, expires } => { - cw20_execute(deps, env, info, Cw20ExecuteMsg::DecreaseAllowance { spender, amount, expires }) - } - ExecuteMsg::TransferFrom { owner, recipient, amount } => { - cw20_execute(deps, env, info, Cw20ExecuteMsg::TransferFrom { owner, recipient, amount }) - } - ExecuteMsg::BurnFrom { owner, amount } => { - cw20_execute(deps, env, info, Cw20ExecuteMsg::BurnFrom { owner, amount }) - } - ExecuteMsg::SendFrom { owner, contract, amount, msg } => { - cw20_execute(deps, env, info, Cw20ExecuteMsg::SendFrom { owner, contract, amount, msg }) - } - - // Custom wildlife token operations - ExecuteMsg::SetSpeciesReward { species, amount } => { - // Only contract owner can set rewards - let token_info = TOKEN_INFO.load(deps.storage)?; - if let Some(mint) = token_info.mint { - if mint.minter != info.sender { - return Err(cosmwasm_std::StdError::generic_err("Unauthorized")); - } - } else { - return Err(cosmwasm_std::StdError::generic_err("No minter set")); - } - - let mut state = WILDLIFE_STATE.load(deps.storage)?; - state.species_rewards.insert(species.clone(), amount); - WILDLIFE_STATE.save(deps.storage, &state)?; - - Ok(Response::new() - .add_attribute("action", "set_species_reward") - .add_attribute("species", species) - .add_attribute("amount", amount.to_string())) - } - - ExecuteMsg::AwardTokens { recipient, species } => { - // Check if caller is an authorized distributor - let state = WILDLIFE_STATE.load(deps.storage)?; - if !state.distributors.contains(&info.sender) { - return Err(cosmwasm_std::StdError::generic_err("Not authorized to distribute tokens")); - } - - // Get reward amount for the species (or default if not set) - let species_lower = species.to_lowercase(); - let reward_amount = match state.species_rewards.get(&species_lower) { - Some(amount) => *amount, - None => match state.species_rewards.get("default") { - Some(amount) => *amount, - None => return Err(cosmwasm_std::StdError::generic_err("No default reward set")), - }, - }; - - // Get recipient address - let recipient_addr = deps.api.addr_validate(&recipient)?; - - // Mint tokens to the recipient - cw20_execute( - deps, - env, - info, - Cw20ExecuteMsg::Mint { - recipient: recipient_addr.to_string(), - amount: reward_amount, - }, - )?; - - Ok(Response::new() - .add_attribute("action", "award_tokens") - .add_attribute("recipient", recipient) - .add_attribute("species", species) - .add_attribute("amount", reward_amount.to_string())) - } - - ExecuteMsg::AddDistributor { address } => { - // Only contract owner can add distributors - let token_info = TOKEN_INFO.load(deps.storage)?; - if let Some(mint) = token_info.mint { - if mint.minter != info.sender { - return Err(cosmwasm_std::StdError::generic_err("Unauthorized")); - } - } else { - return Err(cosmwasm_std::StdError::generic_err("No minter set")); - } - - let addr = deps.api.addr_validate(&address)?; - let mut state = WILDLIFE_STATE.load(deps.storage)?; - if !state.distributors.contains(&addr) { - state.distributors.push(addr.clone()); - WILDLIFE_STATE.save(deps.storage, &state)?; - } - - Ok(Response::new() - .add_attribute("action", "add_distributor") - .add_attribute("address", address)) - } - - ExecuteMsg::RemoveDistributor { address } => { - // Only contract owner can remove distributors - let token_info = TOKEN_INFO.load(deps.storage)?; - if let Some(mint) = token_info.mint { - if mint.minter != info.sender { - return Err(cosmwasm_std::StdError::generic_err("Unauthorized")); - } - } else { - return Err(cosmwasm_std::StdError::generic_err("No minter set")); - } - - let addr = deps.api.addr_validate(&address)?; - let mut state = WILDLIFE_STATE.load(deps.storage)?; - state.distributors.retain(|x| x != &addr); - WILDLIFE_STATE.save(deps.storage, &state)?; - - Ok(Response::new() - .add_attribute("action", "remove_distributor") - .add_attribute("address", address)) - } - } -} - -// Query function for reading contract state -#[entry_point] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - match msg { - // For standard CW20 queries, use the base implementation - QueryMsg::Balance { address } => cw20_query(deps, _env, Cw20QueryMsg::Balance { address }), - QueryMsg::TokenInfo {} => cw20_query(deps, _env, Cw20QueryMsg::TokenInfo {}), - QueryMsg::Minter {} => cw20_query(deps, _env, Cw20QueryMsg::Minter {}), - QueryMsg::Allowance { owner, spender } => { - cw20_query(deps, _env, Cw20QueryMsg::Allowance { owner, spender }) - } - QueryMsg::AllAllowances { owner, start_after, limit } => { - cw20_query(deps, _env, Cw20QueryMsg::AllAllowances { owner, start_after, limit }) - } - QueryMsg::AllAccounts { start_after, limit } => { - cw20_query(deps, _env, Cw20QueryMsg::AllAccounts { start_after, limit }) - } - - // Custom wildlife token queries - QueryMsg::SpeciesReward { species } => { - let state = WILDLIFE_STATE.load(deps.storage)?; - let species_lower = species.to_lowercase(); - let reward = match state.species_rewards.get(&species_lower) { - Some(amount) => *amount, - None => match state.species_rewards.get("default") { - Some(amount) => *amount, - None => Uint128::zero(), - }, - }; - to_binary(&SpeciesRewardResponse { species, reward }) - } - - QueryMsg::IsDistributor { address } => { - let state = WILDLIFE_STATE.load(deps.storage)?; - let addr = deps.api.addr_validate(&address)?; - let is_distributor = state.distributors.contains(&addr); - to_binary(&IsDistributorResponse { address, is_distributor }) - } - - QueryMsg::AllSpeciesRewards {} => { - let state = WILDLIFE_STATE.load(deps.storage)?; - let rewards: Vec = state.species_rewards - .iter() - .map(|(species, amount)| SpeciesReward { - species: species.clone(), - reward: *amount, - }) - .collect(); - to_binary(&AllSpeciesRewardsResponse { rewards }) - } - } -} - -// Custom query messages -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum QueryMsg { - // Standard CW20 queries - Balance { address: String }, - TokenInfo {}, - Minter {}, - Allowance { owner: String, spender: String }, - AllAllowances { owner: String, start_after: Option, limit: Option }, - AllAccounts { start_after: Option, limit: Option }, - // Custom Wildlife Token queries - SpeciesReward { species: String }, - IsDistributor { address: String }, - AllSpeciesRewards {}, -} - -// Query response types -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct SpeciesRewardResponse { - pub species: String, - pub reward: Uint128, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct IsDistributorResponse { - pub address: String, - pub is_distributor: bool, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct SpeciesReward { - pub species: String, - pub reward: Uint128, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct AllSpeciesRewardsResponse { - pub rewards: Vec, -} - -// Instantiate message -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct InstantiateMsg { - pub name: String, - pub symbol: String, - pub decimals: u8, -} - -// State variable for wildlife-specific data -pub const WILDLIFE_STATE: Item = Item::new("wildlife_state"); \ No newline at end of file