Sube una foto o escanea con tu cámara. BOTANICA te lo dice todo — nombre, características, guía de cuidado, detección de plagas, y hasta puedes mintear un NFT.
De la foto al conocimiento experto en segundos — sin necesidad de ser botánico.
Sube una foto clara o usa tu cámara para mejores resultados.
Funciones gratuitas para comenzar, premium para los entusiastas de las plantas.
BOTANICA combina capacidades avanzadas de visión con IA y una base de datos botánica completa para dar a todos — desde principiantes curiosos hasta jardineros apasionados — conocimiento instantáneo y preciso de plantas al alcance de la mano.
Experiment with BOTANICA's AI Agent features in a safe testnet environment — no real funds, full functionality.
From a smart plant scanner to a fully autonomous AI Agent on Solana and Base — here's our journey from March to May 2026.
BOTANICA's smart contracts power NFT minting, token economy, AI Agent subscriptions, and DAO governance — deployed on Solana and Base.
// BOTANICA Plant NFT — Solana / Anchor use anchor_lang::prelude::*; use anchor_spl::token::{self, Token, MintTo}; use mpl_token_metadata::instruction as mpl_ix; declare_id!("BotaNFT1111111111111111111111111111111111111"); #[program] pub mod botanica_nft { use super::*; pub fn mint_plant_nft( ctx: Context<MintPlantNFT>, plant_name: String, latin_name: String, confidence: u8, uri: String, ) -> Result<()> { // Mint 1 NFT to user wallet let cpi_ctx = CpiContext::new( ctx.accounts.token_program.to_account_info(), MintTo { mint: ctx.accounts.mint.to_account_info(), to: ctx.accounts.token_account.to_account_info(), authority: ctx.accounts.payer.to_account_info(), }, ); token::mint_to(cpi_ctx, 1)?; // Store plant metadata on-chain let plant = &mut ctx.accounts.plant_data; plant.owner = ctx.accounts.payer.key(); plant.plant_name = plant_name; plant.latin_name = latin_name; plant.confidence = confidence; plant.mint_time = Clock::get()?.unix_timestamp; plant.nft_mint = ctx.accounts.mint.key(); Ok(()) } } #[account] pub struct PlantData { pub owner: Pubkey, pub plant_name: String, pub latin_name: String, pub confidence: u8, pub nft_mint: Pubkey, pub mint_time: i64, }
// $FLORA SPL Token — Botanica Utility Token use anchor_lang::prelude::*; use anchor_spl::token::{self, Token, MintTo, Burn}; declare_id!("FLORA111111111111111111111111111111111111111"); pub const TOTAL_SUPPLY: u64 = 1_000_000_000 * 10u64.pow(9); // 1B FLORA pub const DECIMALS: u8 = 9; #[program] pub mod flora_token { use super::*; /// Initialize $FLORA mint with fixed supply pub fn initialize(ctx: Context<Initialize>) -> Result<()> { let cpi_ctx = CpiContext::new( ctx.accounts.token_program.to_account_info(), MintTo { mint: ctx.accounts.mint.to_account_info(), to: ctx.accounts.treasury.to_account_info(), authority: ctx.accounts.authority.to_account_info(), }, ); token::mint_to(cpi_ctx, TOTAL_SUPPLY)?; Ok(()) } /// Reward user with FLORA on plant scan pub fn reward_scan( ctx: Context<RewardScan>, amount: u64, ) -> Result<()> { require!(amount <= 100 * 10u64.pow(9), FloraError::ExceedsRewardCap); token::mint_to( CpiContext::new_with_signer(/* ... */), amount )?; Ok(()) } /// Burn FLORA for premium upgrade pub fn burn_for_premium(ctx: Context<BurnPremium>) -> Result<()> { let burn_amount = 500 * 10u64.pow(9); // 500 FLORA token::burn( CpiContext::new(/* ... */), burn_amount )?; ctx.accounts.user_state.is_premium = true; Ok(()) } } #[error_code] pub enum FloraError { #[msg("Reward exceeds daily cap")] ExceedsRewardCap, }
// Agent Subscription — Botanica AI Agent Access use anchor_lang::prelude::*; declare_id!("AgentSub1111111111111111111111111111111111"); pub const MONTHLY_PRICE: u64 = 50 * 10u64.pow(9); // 50 FLORA/mo pub const YEARLY_PRICE: u64 = 500 * 10u64.pow(9); // 500 FLORA/yr pub const MONTH_SECS: i64 = 30 * 24 * 3600; #[program] pub mod agent_subscription { use super::*; pub fn subscribe( ctx: Context<Subscribe>, plan: SubscriptionPlan, ) -> Result<()> { let (price, duration) = match plan { SubscriptionPlan::Monthly => (MONTHLY_PRICE, MONTH_SECS), SubscriptionPlan::Yearly => (YEARLY_PRICE, MONTH_SECS * 12), }; // Transfer FLORA to protocol treasury anchor_spl::token::transfer( CpiContext::new(/* ... */), price )?; // Write subscription state on-chain let sub = &mut ctx.accounts.subscription; sub.subscriber = ctx.accounts.user.key(); sub.plan = plan; sub.expires_at = Clock::get()?.unix_timestamp + duration; sub.agent_calls = 0; Ok(()) } pub fn verify_active(ctx: Context<VerifySub>) -> Result<bool> { let now = Clock::get()?.unix_timestamp; Ok(ctx.accounts.subscription.expires_at > now) } } #[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)] pub enum SubscriptionPlan { Monthly, Yearly } #[account] pub struct Subscription { pub subscriber: Pubkey, pub plan: SubscriptionPlan, pub expires_at: i64, pub agent_calls: u32, }
// BotanicaDAO — On-chain Governance (Solana) use anchor_lang::prelude::*; declare_id!("BotaDAO1111111111111111111111111111111111111"); pub const VOTING_PERIOD: i64 = 3 * 24 * 3600; // 3 days pub const TIMELOCK: i64 = 2 * 24 * 3600; // 48h execution delay pub const QUORUM_BPS: u64 = 500; // 5% of total supply #[program] pub mod botanica_dao { use super::*; pub fn create_proposal( ctx: Context<CreateProposal>, title: String, description: String, action: ProposalAction, ) -> Result<()> { // Require 10,000 FLORA to propose require!(ctx.accounts.proposer_balance.amount >= 10_000 * 10u64.pow(9), DaoError::InsufficientFloraToPropose); let proposal = &mut ctx.accounts.proposal; proposal.proposer = ctx.accounts.proposer.key(); proposal.title = title; proposal.description = description; proposal.action = action; proposal.yes_votes = 0; proposal.no_votes = 0; proposal.end_time = Clock::get()?.unix_timestamp + VOTING_PERIOD; proposal.executed = false; Ok(()) } pub fn cast_vote( ctx: Context<CastVote>, support: bool, ) -> Result<()> { let now = Clock::get()?.unix_timestamp; require!(now < ctx.accounts.proposal.end_time, DaoError::VotingEnded); let weight = ctx.accounts.voter_token.amount; if support { ctx.accounts.proposal.yes_votes += weight; } else { ctx.accounts.proposal.no_votes += weight; } Ok(()) } pub fn execute_proposal(ctx: Context<ExecuteProposal>) -> Result<()> { let p = &mut ctx.accounts.proposal; let now = Clock::get()?.unix_timestamp; require!(now >= p.end_time + TIMELOCK, DaoError::TimelockActive); require!(p.yes_votes > p.no_votes, DaoError::ProposalRejected); require!(!p.executed, DaoError::AlreadyExecuted); p.executed = true; Ok(()) } } #[derive(AnchorSerialize, AnchorDeserialize, Clone)] pub enum ProposalAction { UpdateAgentParams { max_calls: u32 }, AllocateTreasury { recipient: Pubkey, amount: u64 }, UpgradeProgram { new_program_id: Pubkey }, }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/interfaces/IERC2981.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract BotanicaNFT is ERC721URIStorage, IERC2981, Ownable { uint256 public nextTokenId; uint96 public royaltyBps = 500; // 5% royalty struct PlantData { string plantName; string latinName; uint8 confidence; uint256 mintedAt; address discoverer; } mapping(uint256 => PlantData) public plants; event PlantMinted( address indexed to, uint256 indexed tokenId, string plantName, uint8 confidence ); constructor() ERC721("Botanica Plant NFT", "BPNFT") Ownable(msg.sender) {} function mintPlant( address to, string calldata plantName, string calldata latinName, uint8 confidence, string calldata tokenURI_ ) external returns (uint256 tokenId) { tokenId = nextTokenId++; _safeMint(to, tokenId); _setTokenURI(tokenId, tokenURI_); plants[tokenId] = PlantData(plantName, latinName, confidence, block.timestamp, to); emit PlantMinted(to, tokenId, plantName, confidence); } function royaltyInfo(uint256, uint256 salePrice) external view override returns (address, uint256) { return (owner(), (salePrice * royaltyBps) / 10000); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; contract FloraToken is ERC20, ERC20Burnable, AccessControl { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 public constant BRIDGE_ROLE = keccak256("BRIDGE_ROLE"); uint256 public constant MAX_SUPPLY = 1_000_000_000 * 10 ** 18; uint256 public constant PREMIUM_BURN = 500 * 10 ** 18; // 500 FLORA uint256 public constant AGENT_REWARD = 10 * 10 ** 18; // 10 FLORA/scan constructor() ERC20("Flora", "FLORA") { _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _mint(msg.sender, MAX_SUPPLY); } /// Burn FLORA to activate Premium function burnForPremium() external { burn(PREMIUM_BURN); emit PremiumActivated(msg.sender); } /// Wormhole bridge mint (cross-chain) function bridgeMint(address to, uint256 amount) external onlyRole(BRIDGE_ROLE) { require(totalSupply() + amount <= MAX_SUPPLY, "Exceeds max supply"); _mint(to, amount); } event PremiumActivated(address indexed user); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "./FloraToken.sol"; contract AgentSubscription { FloraToken public immutable flora; address public immutable treasury; uint256 public constant MONTHLY_PRICE = 50 * 10 ** 18; uint256 public constant YEARLY_PRICE = 500 * 10 ** 18; uint256 public constant MONTH = 30 days; struct Sub { uint256 expiresAt; bool yearly; uint256 agentCalls; } mapping(address => Sub) public subscriptions; event Subscribed(address indexed user, uint256 expiresAt, bool yearly); event Cancelled(address indexed user); constructor(address _flora, address _treasury) { flora = FloraToken(_flora); treasury = _treasury; } function subscribe(bool yearly) external { uint256 price = yearly ? YEARLY_PRICE : MONTHLY_PRICE; uint256 duration = yearly ? MONTH * 12 : MONTH; flora.transferFrom(msg.sender, treasury, price); subscriptions[msg.sender] = Sub(block.timestamp + duration, yearly, 0); emit Subscribed(msg.sender, block.timestamp + duration, yearly); } function isActive(address user) external view returns (bool) { return subscriptions[user].expiresAt > block.timestamp; } function cancel() external { subscriptions[msg.sender].expiresAt = block.timestamp; emit Cancelled(msg.sender); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "@openzeppelin/contracts/governance/Governor.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol"; import "@openzeppelin/contracts/governance/utils/IVotes.sol"; contract BotanicaDAO is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, GovernorTimelockControl { /// @param token_ $FLORA ERC20Votes token address /// @param tlock_ TimelockController (48h delay) constructor(IVotes token_, TimelockController tlock_) Governor("BotanicaDAO") GovernorSettings( 1, // 1 block voting delay 21600, // ~3 days voting period (Base ~2s/block) 10_000e18 // 10,000 FLORA to propose ) GovernorVotes(token_) GovernorTimelockControl(tlock_) {} /// 5% of total $FLORA supply = quorum function quorum(uint256 blockNumber) public view override returns (uint256) { return token.getPastTotalSupply(blockNumber) * 5 / 100; } // Required overrides function votingDelay() public view override (Governor, GovernorSettings) returns (uint256) { return super.votingDelay(); } function votingPeriod() public view override (Governor, GovernorSettings) returns (uint256) { return super.votingPeriod(); } function state(uint256 proposalId) public view override (Governor, GovernorTimelockControl) returns (ProposalState) { return super.state(proposalId); } }
Un equipo pequeño y enfocado construyendo el futuro de la inteligencia botánica — construido sobre Solana y Base.
El visionario detrás de BOTANICA. Llevando el producto desde el concepto hasta el Agente IA — construyendo el puente entre el conocimiento botánico y Web3.
@todoparatumasc3Construyendo la comunidad y el brand de BOTANICA. Contando la historia de cómo el Web3 está transformando la relación entre las personas y la naturaleza.
@sofiaramirez_boArchitect of BOTANICA's full-stack — from the AI integration and smart contracts on Solana & Base to the mobile-first web experience.
@rootclank