Análise de Contrato Inteligente Sway: Funcionamento do Sistema Agrícola em sway-farm

Modelagem de Dados

A aplicação define estruturas voltadas ao gerenciamento do progresso do jogador e dos elementos da fazenda.

pub struct Agricultor {
    pub nivel_habilidade: u64,
    pub total_vendido: u64,
}

Os tipos de cultura são representados por um enum que atualmente contém uma única variante:

pub enum TipoCultura {
    Tomate: (),
}

Cada cultura plantada carrega o tipo e o momento em que foi semeada:

pub struct Cultura {
    pub variedade: TipoCultura,
    pub instante_plantio: Option<u64>,
}

O terreno do jogador é modelado como um vetor de tamanho fixo, limitado a dez posições:

pub struct Terreno {
    pub canteiros: [Option<Cultura>; 10],
}

Esquema de Persistência

O cnotrato utiliza diversos mapas de armazenamento para manter registros seguros e isolados por identidade:

  • agricultores – dados do perfil do jogador
  • sementes_estoque – quantidade de sementes de cada tipo
  • canteiros_plantados – vetor de culturas em crescimento
  • itens_colhidos – produtos no invantário
  • posicao_jogador – coordenadas no mundo

Principais Mecânicas Implementadas

Registro de Novo Jogador

A função de inicialização cria um perfil, concede moedas iniciais e prepara o terreno vazio. A verificação impede registros duplicados:

#[storage(read, write)]
fn cadastrar_jogador(ident: Identity) {
    require(
        storage.agricultores.get(ident).try_read().is_none(),
        "perfil já existente",
    );
    let novo = Agricultor { nivel_habilidade: 1, total_vendido: 0 };
    storage.agricultores.insert(ident, novo);
    storage.canteiros_plantados.insert(ident, Terreno::novo());
    cunhar_moedas(ident, SUB_ID_PADRAO, 1_000_000_000);
    log(EventoNovoJogador { endereco: ident });
}

O avanço de nível depende do volume total vendido, calculado como novo_nivel * novo_nivel * 3_000_000. O jogador precisa superar esse valor para habilitar a progressão.

Aquisição e Plantio

Para plantar, é necessário adquirir sementes. O custo de cada tipo é fixo e pago com tokens nativos:

#[storage(read, write), payable]
fn comprar_sementes(cultura: TipoCultura, qtd: u64, destino: Identity) {
    let preco_unitario = match cultura {
        TipoCultura::Tomate => 750_000,
    };
    let custo_total = qtd * preco_unitario;
    require(msg_amount() >= custo_total, Erro::SaldoInsuficiente);
    let saldo_atual = storage.sementes_estoque.get((destino, cultura)).try_read().unwrap_or(0);
    storage.sementes_estoque.insert((destino, cultura), saldo_atual + qtd);
}

O plantio consome uma semente e insere a cultura em um índice específico do terreno. O instante de semeadura é registrado com o timestamp corrente:

#[storage(read, write)]
fn semear_no_indice(cultura: TipoCultura, idx: u64, ident: Identity) {
    let qtd_atual = storage.sementes_estoque.get((ident, cultura)).try_read().unwrap_or(0);
    require(qtd_atual >= 1, Erro::SementesFaltando(1));
    storage.sementes_estoque.insert((ident, cultura), qtd_atual - 1);
    let mut terreno = storage.canteiros_plantados.get(ident).try_read().unwrap();
    let nova_cultura = Cultura {
        variedade: cultura,
        instante_plantio: Some(timestamp()),
    };
    terreno.canteiros[idx] = Some(nova_cultura);
    storage.canteiros_plantados.insert(ident, terreno);
}

É possível acelerar o crescimento pagando taxas adicionais; o tempo de maturação é reduzido somando‑se 600 segundos ao instante de plantio.

Colheita e Comercialização

A colheita verifica se cada cultura já atingiu a maturidade (considerando um período fixo de 600 segundos) e transfere o item para o inventário:

#[storage(read, write)]
fn colher(indices: Vec<u64>, ident: Identity) {
    let agora = timestamp();
    let duracao_crescimento = 120 * 5;
    let mut terreno = storage.canteiros_plantados.get(ident).try_read().unwrap();
    let mut idx = 0;
    while idx < indices.len() {
        let pos = indices.get(idx).unwrap();
        let cultura = terreno.canteiros[pos].as_ref().unwrap();
        let plantio = cultura.instante_plantio.unwrap();
        let pronto_em = plantio + duracao_crescimento;
        require(agora >= pronto_em, "cultura imatura");
        terreno.canteiros[pos] = None;
        let qtd = storage.itens_colhidos.get((ident, cultura.variedade)).try_read().unwrap_or(0);
        storage.itens_colhidos.insert((ident, cultura.variedade), qtd + 1);
        idx += 1;
    }
    storage.canteiros_plantados.insert(ident, terreno);
}

Os produtos colhidos podem ser vendidos por moedas, atualizando o total vendido do jogador:

#[storage(read, write)]
fn vender_item(cultura: TipoCultura, qtd: u64, ident: Identity) {
    let em_estoque = storage.itens_colhidos.get((ident, cultura)).try_read().unwrap_or(0);
    require(em_estoque >= qtd, Erro::EstoqueInsuficiente);
    let valor_venda = match cultura {
        TipoCultura::Tomate => 7_500_000 * qtd,
    };
    let mut perfil = storage.agricultores.get(ident).try_read().unwrap();
    perfil.total_vendido += valor_venda;
    storage.agricultores.insert(ident, perfil);
    storage.itens_colhidos.insert((ident, cultura), em_estoque - qtd);
    cunhar_moedas(ident, SUB_ID_PADRAO, valor_venda);
}

Proteções e Boas Práticas

  • Toda operação sensível valida entradas com require, evitando estouros de quantidade ou acessos não autorizados.
  • Um enum Erro padroniza mensagens para falta de saldo, sementes e ativos incorretos.
  • Eventos são emitidos (log) em ações importantes, como compra de sementes e colheita, facilitando a observação off‑chain.
  • As funções recebem explicitamente a identidade do jogador, garantindo que ninguém manipule dados alheios.

Integração com o Frontend

O lado cliente comunica‑se com o contrato por meio de chamadas tipadas, utilizando o SDK TypeScript do Fuel. A execução de funções prepara a transação com os parâmetros e o valor em tokens necessário:

async comprarSementes(tipo: TipoCultura, quantidade: number, endereco: string): Promise<void> {
  const preco = tipo === TipoCultura.Tomate ? 750000 : 0;
  const custo = preco * quantidade;
  const tx = this.contrato.functions.comprar_sementes(tipo, quantidade, endereco)
    .callParams({ amount: custo, assetId: BaseAssetId });
  await tx.waitForResult();
}

Componentes como BuySeeds.tsx e MarketModal.tsx renderizam a interface de mercado e gerenciam o ciclo de jogo.

A estrutura modular do sway-farm facilita a extensão: novos tipos de cultura, sistemas de negociação entre jogadores, eventos climáticos e integração com NFTs são caminhos naturais de evolução. O projeto demonstra a aplicação prática da linguagem Sway em jogos totalmente on‑chain.

Tags: Sway Contrato Inteligente Jogo Blockchain Fuel Network Desenvolvimento de Jogos

Publicado em 10-19 17:21