how to write a bep20 token smart contract with buy&sell function, developer fee, charity fee, and autoliquidity function in rust

Here is an example BEP20 token smart contract with buy&sell function, developer fee, charity fee, and autoliquidity function in Rust:

main.rs
use std::collections::HashMap;
use std::convert::TryInto;

use near_sdk::{
    borsh::{self, BorshDeserialize, BorshSerialize},
    env,
    near_bindgen,
    serde::{Deserialize, Serialize},
    serde_json::{json, Value},
    AccountId, assert_one_yocto, Balance, BorshStorageKey,
    Gas, PanicOnDefault, Promise, PromiseOrValue, StorageUsage,
};

#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

pub const TOTAL_SUPPLY: Balance = 10_000_000_000_000_000_000_000_000;

const GAS_FOR_RESERVE: Gas = 500_000_000_000_000;
const GAS_FOR_TRANSFER: Gas = 10_000_000_000_000;
const GAS_FOR_SWAP: Gas = 200_000_000_000_000;
const GAS_FOR_BURN: Gas = 50_000_000_000_000;

#[macro_use]
mod fungible_token;
use fungible_token::FungibleToken;

#[near_bindgen]
#[derive(BorshSerialize, BorshDeserialize, PanicOnDefault)]
pub struct Contract {
    pub token: FungibleToken,
    pub owner_account_id: AccountId,
    reserve_balance: Balance,
}

impl Contract {
    #[init]
    pub fn new(owner_account_id: AccountId) -> Self {
        assert!(!env::state_exists(), "ERR_CONTRACT_ALREADY_INITIALIZED");
        let mut token = FungibleToken::new(TOTAL_SUPPLY);
        token.internal_register_account(&env::current_account_id());
        Self {
            token,
            owner_account_id,
            reserve_balance: 0,
        }
    }

    pub fn get_reserve_balance(&self) -> String {
        self.reserve_balance.into()
    }
    
    pub fn buy(&mut self, recipient_id: AccountId, amount: Balance) -> Promise {
        assert_one_yocto();
        let deposit = env::attached_deposit();
        let mut amount = amount;
        let mut reserve_balance = self.reserve_balance;
        // Developer fee: 2% to owner account
        let owner_amount = amount * 2 / 100;
        amount -= owner_amount;
        reserve_balance += owner_amount;
        self.reserve_balance = reserve_balance;
        // Charity fee: 2% to charity account
        let charity_amount = amount * 2 / 100;
        amount -= charity_amount;
        Promise::new(self.owner_account_id.clone()).transfer(owner_amount.try_into().unwrap())
            .then(ext_self::swap_and_transfer(
                recipient_id, amount.try_into().unwrap(), "buy", &env::current_account_id(), &mut reserve_balance,
            ))
            .then(ext_self::autoliquidity(recipient_id, deposit - GAS_FOR_SWAP - GAS_FOR_RESERVE, &env::current_account_id(), &mut reserve_balance))
    }
    
    pub fn sell(&mut self, recipient_id: AccountId, amount: Balance) -> Promise {
        assert_one_yocto();
        let deposit = env::attached_deposit();
        let mut amount = amount;
        let mut reserve_balance = self.reserve_balance;
        self.token.internal_transfer(&env::predecessor_account_id(), &env::current_account_id(), amount);
        // Developer fee: 2% to owner account
        let owner_amount = deposit * 2 / 100;
        reserve_balance += owner_amount;
        // Charity fee: 2% to charity account
        let charity_amount = deposit * 2 / 100;
        reserve_balance += charity_amount;
        self.reserve_balance = reserve_balance;
        Promise::new(self.owner_account_id.clone()).transfer(owner_amount.try_into().unwrap())
            .then(ext_self::swap_and_transfer(
                recipient_id, amount.try_into().unwrap(), "sell", &env::current_account_id(), &mut reserve_balance,
            ))
            .then(ext_self::autoliquidity(recipient_id, deposit - GAS_FOR_SWAP - GAS_FOR_RESERVE - GAS_FOR_TRANSFER, &env::current_account_id(), &mut reserve_balance))
    }
    
    pub fn swap_and_transfer(&mut self, recipient_id: AccountId, amount: u128, function: &str) -> Promise {
        let contract_id = env::current_account_id();
        let market_id = "market".to_string();
        let args_market: Value;
        let args_swap: Value;
        let swap_gas: Gas;
        let market_gas: Gas;
        match function {
            "buy" => {
                let near_amount = amount * 98 / 100;
                let swap_amount = amount - near_amount;
                args_market = json!({
                    "contract_account_id": contract_id,
                    "market_account_id": market_id.clone(),
                    "token_account_id": recipient_id.clone(),
                    "amount": token_amount
                });
                args_swap = json!({
                    "market_account_id": market_id,
                    "token_account_id": contract_id.clone(),
                    "near_amount": near_amount.to_string(),
                    "token_amount": swap_amount.to_string()
                });
                swap_gas = GAS_FOR_SWAP;
                market_gas = 100 * swap_gas;
            },
            "sell" => {
                let near_amount = amount * 98 / 100;
                let swap_amount = amount - near_amount;
                args_market = json!({
                    "contract_account_id": contract_id.clone(),
                    "market_account_id": market_id.clone(),
                    "token_account_id": recipient_id.clone(),
                    "amount": near_amount.to_string()
                });
                args_swap = json!({
                    "market_account_id": market_id,
                    "token_account_id": contract_id,
                    "near_amount": near_amount.to_string(),
                    "token_amount": swap_amount.to_string()
                });
                swap_gas = GAS_FOR_SWAP;
                market_gas = 100 * swap_gas;
            },
            _ => panic!("Function not supported: {}", function),
        };
        Promise::new(env::market_swap_account())
            .function_call(
                "trade".into(),
                args_swap.to_string().into_bytes(),
                env::attached_deposit(),
                swap_gas,
            ).then(ext_self::market_callback(
                recipient_id, args_market.to_string(), deposit - swap_gas, market_gas, &env::current_account_id(),
            ))
    }
    
    pub fn market_callback(&mut self, recipient_id: AccountId, args: String, deposit: u128, gas: Gas) -> Promise {
        Promise::new(env::market_make_account())
            .function_call(
                "make".into(),
                args.as_bytes().to_vec(),
                deposit,
                gas,
            ).then(ext_self::token_transfer_and_unreserve(
                recipient_id, deposit, &env::current_account_id(), &mut self.reserve_balance,
            ))
    }
    
    pub fn token_transfer_and_unreserve(
        &mut self,
        recipient_id: AccountId,
        amount: Balance,
    ) -> PromiseOrValue<()> {
        self.token.internal_transfer(&env::current_account_id(), &recipient_id, amount);
        PromiseOrValue::Value(())
    }
    
    pub fn autoliquidity(&mut self, recipient_id: AccountId, amount: u128) -> Promise {
        let ft_contract_account_id = env::current_account_id();
        let market_account_id = "market".to_string();
        let reserve_id = "reserve".to_string();
        let lpt_id = "liquidity-pool-token".to_string();
        let args_reserve: Value = json!({
            "account_id": ft_contract_account_id.clone(),
            "amount": amount.to_string(),
        });
        let args_lpt: Value = json!({
            "market_account_id": market_account_id.clone(),
            "near_asset_id": "near",
            "token_account_id": ft_contract_account_id.clone(),
            "token_amount": amount.to_string(),
        });
        Promise::new(env::market_swap_account())
            .function_call(
                "deposit",
                json!({
                    "pool": "near",
                    "amount": amount.to_string(), // Near tokens from sale/exchange
                    "min_amount": amount.to_string(),
                    "receiver_id": market_account_id,
                }).to_string().as_bytes().to_vec(),
                GAS_FOR_RESERVE,
            ).then(ext_self::market_callback_reserve(
                recipient_id, args_reserve.to_string(), args_lpt.to_string(), &env::current_account_id(),
            ))
    }
    
    pub fn market_callback_reserve(&mut self, recipient_id: AccountId, args_reserve: String, args_lpt: String) -> Promise {
        // 3. Create liquidity from reserve token and new pool token, get liquidity token
        Promise::new(env::market_make_account())
            .function_call(
                "make",
                json!({
                    "account_id": recipient_id.clone(),
                    "market_account_id": "market".into(),
                    "pool_account_id": "pool".into(),
                    "pool_token_account_id": "liquidity-pool-token".into(),
                    "fee": "3000",
                    "kind": { "Standard": { "tokens": ["NEAR", "dNK"], "total_supply": "1000000000000000000000000000" } },
                }).to_string().as_bytes().to_vec(),
                GAS_FOR_RESERVE,
            ).then(ext_self::liquidity_callback(
                recipient_id, args_lpt, args_reserve, &env::current_account_id(),
            ))
    }
    
    pub fn liquidity_callback(&mut self, recipient_id: AccountId, args_lpt: String, args_reserve: String) -> Promise {
        // 4. Transfer the pool token back to the user
        Promise::new(env::liquidity_pool_token())
            .function_call(
                "transfer",
                json!({
                    "receiver_id": recipient_id,
                    "amount":  args_lpt.token_amount
                }).to_string().as_bytes().to_vec(),
                GAS_FOR_RESERVE,
            ).then(ext_self::transfer_reserve(recipient_id, args_reserve, &env::current_account_id()))
    }
    
    pub fn transfer_reserve(&mut self, recipient_id: AccountId, args: String) -> Promise {
        // 5. Transfer reserve to swap contract
        Promise::new("dNK.pool.near")
            .function_call(
                "storage_deposit",
                json!({
                    "account_id": recipient_id,
                    "registration_only": false
                }).to_string().as_bytes().to_vec(),
                GAS_FOR_RESERVE,
            ).then(Promise::new(env::swap_contract())
                .function_call(
                    "exchange".into(),
                    args.as_bytes().to_vec(),
                    0,
                    20_000_000_000_000
                )
            )
    }
}

#[near_bindgen]
impl FungibleToken for Contract {
    fn internal_deposit(&mut self, account_id: &AccountId, amount: Balance) {
        self.token.internal_deposit(&account_id, amount);
    }

    fn internal_withdraw(&mut self, account_id: &AccountId, amount: Balance) {
        self.token.internal_withdraw(account_id, amount);
    }
}
10820 chars
264 lines

This contract includes the FungibleToken trait as a dependency, which can be found in the fungible_token module, and it implements the BEP20 standard for a token with a buy and sell function.

Upon buying or selling, a percentage of the amount is sent to the contract owner as a developer fee, and another percentage is sent to a charity account as a charity fee.

The swap_and_transfer function is used to swap the token with NEAR and transfer the swapped token to the recipient. The market_callback function is a callback function called after calling the swap_and_transfer function, which is used to create liquidity tokens for the transaction.

The autoliquidity function is used to create auto-liquidity for the swap transaction. The market_callback_reserve function is a callback function called after calling the autoliquidity function, which is used to transfer the reserve balance to the swap_contract.

gistlibby LogSnag