use near_contract_standards::fungible_token::metadata::{ FungibleTokenMetadata, FungibleTokenMetadataProvider, FT_METADATA_SPEC, }; use near_contract_standards::fungible_token::{ FungibleToken, FungibleTokenCore, FungibleTokenResolver, }; use near_contract_standards::storage_management::{ StorageBalance, StorageBalanceBounds, StorageManagement, }; use near_sdk::borsh::BorshSerialize; use near_sdk::collections::LazyOption; use near_sdk::json_types::U128; use near_sdk::{ env, log, near, require, AccountId, BorshStorageKey, NearToken, PanicOnDefault, Promise, PromiseOrValue, }; #[near(contract_state)] #[derive(PanicOnDefault)] pub struct Contract { token: FungibleToken, metadata: LazyOption, } const DATA_IMAGE_SVG_NEAR_ICON: &str = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 288 288'%3E%3Cg id='l' data-name='l'%3E%3Cpath d='M187.58,79.81l-30.1,44.69a3.2,3.2,0,0,0,4.75,4.2L191.86,103a1.2,1.2,0,0,1,2,.91v80.46a1.2,1.2,0,0,1-2.12.77L102.18,77.93A15.35,15.35,0,0,0,90.47,72.5H87.34A15.34,15.34,0,0,0,72,87.84V201.16A15.34,15.34,0,0,0,87.34,216.5h0a15.35,15.35,0,0,0,13.08-7.31l30.1-44.69a3.2,3.2,0,0,0-4.75-4.2L96.14,186a1.2,1.2,0,0,1-2-.91V104.61a1.2,1.2,0,0,1,2.12-.77l89.55,107.23a15.35,15.35,0,0,0,11.71,5.43h3.13A15.34,15.34,0,0,0,216,201.16V87.84A15.34,15.34,0,0,0,200.66,72.5h0A15.35,15.35,0,0,0,187.58,79.81Z'/%3E%3C/g%3E%3C/svg%3E"; #[derive(BorshSerialize, BorshStorageKey)] #[borsh(crate = "near_sdk::borsh")] enum StorageKey { FungibleToken, Metadata, } #[near] impl Contract { /// Initializes the contract with the given total supply owned by the given `owner_id` with /// default metadata (for example purposes only). #[init] pub fn new_default_meta(owner_id: &AccountId, total_supply: U128) -> Self { Self::new( owner_id, total_supply, &FungibleTokenMetadata { spec: FT_METADATA_SPEC.to_string(), name: "Example NEAR fungible token".to_string(), symbol: "EXAMPLE".to_string(), icon: Some(DATA_IMAGE_SVG_NEAR_ICON.to_string()), reference: None, reference_hash: None, decimals: 24, }, ) } /// Initializes the contract with the given total supply owned by the given `owner_id` with /// the given fungible token metadata. #[init] pub fn new(owner_id: &AccountId, total_supply: U128, metadata: &FungibleTokenMetadata) -> Self { require!(!env::state_exists(), "Already initialized"); metadata.assert_valid(); let mut this = Self { token: FungibleToken::new(StorageKey::FungibleToken), metadata: LazyOption::new(StorageKey::Metadata, Some(metadata)), }; this.token.internal_register_account(owner_id); this.token.internal_deposit(owner_id, total_supply.into()); near_contract_standards::fungible_token::events::FtMint { owner_id, amount: total_supply, memo: Some("new tokens are minted"), } .emit(); this } #[payable] pub fn near_withdraw(&mut self, amount: U128) -> Promise { self.token .internal_withdraw(&env::predecessor_account_id(), amount.into()); Promise::new(env::predecessor_account_id()).transfer(NearToken::from_yoctonear(amount.0)) } } #[near] impl FungibleTokenCore for Contract { #[payable] fn ft_transfer(&mut self, receiver_id: AccountId, amount: U128, memo: Option) { self.token.ft_transfer(receiver_id, amount, memo); } #[payable] fn ft_transfer_call( &mut self, receiver_id: AccountId, amount: U128, memo: Option, msg: String, ) -> PromiseOrValue { self.token.ft_transfer_call(receiver_id, amount, memo, msg) } fn ft_total_supply(&self) -> U128 { self.token.ft_total_supply() } fn ft_balance_of(&self, account_id: AccountId) -> U128 { self.token.ft_balance_of(account_id) } } #[near] impl FungibleTokenResolver for Contract { #[private] fn ft_resolve_transfer( &mut self, sender_id: AccountId, receiver_id: AccountId, amount: U128, ) -> U128 { let (used_amount, burned_amount) = self.token .internal_ft_resolve_transfer(&sender_id, receiver_id, amount); if burned_amount > 0 { log!("Account @{} burned {}", sender_id, burned_amount); } used_amount.into() } } #[near] impl StorageManagement for Contract { #[payable] fn storage_deposit( &mut self, account_id: Option, registration_only: Option, ) -> StorageBalance { self.token.storage_deposit(account_id, registration_only) } #[payable] fn storage_withdraw(&mut self, amount: Option) -> StorageBalance { self.token.storage_withdraw(amount) } #[payable] fn storage_unregister(&mut self, force: Option) -> bool { #[allow(unused_variables)] if let Some((account_id, balance)) = self.token.internal_storage_unregister(force) { log!("Closed @{} with {}", account_id, balance); true } else { false } } fn storage_balance_bounds(&self) -> StorageBalanceBounds { self.token.storage_balance_bounds() } fn storage_balance_of(&self, account_id: AccountId) -> Option { self.token.storage_balance_of(account_id) } } #[near] impl FungibleTokenMetadataProvider for Contract { fn ft_metadata(&self) -> FungibleTokenMetadata { self.metadata.get().unwrap() } }