From b98ba2a49b75c25c050da7d2fb5298d0f579dd52 Mon Sep 17 00:00:00 2001 From: Vladas <146100@gmail.com> Date: Tue, 23 Jan 2024 18:38:15 +0200 Subject: [PATCH] added fungible token implementation --- Cargo.lock | 65 ++----- Cargo.toml | 11 +- res/sweat.wasm | Bin 215896 -> 214769 bytes sweat/src/lib.rs | 4 +- sweat/src/token/fungible_token.rs | 299 +++++++++++++++++++++++++++++ sweat/src/token/mod.rs | 4 + sweat/src/token/storage_deposit.rs | 101 ++++++++++ 7 files changed, 427 insertions(+), 57 deletions(-) create mode 100644 sweat/src/token/fungible_token.rs create mode 100644 sweat/src/token/mod.rs create mode 100644 sweat/src/token/storage_deposit.rs diff --git a/Cargo.lock b/Cargo.lock index f41eea1..11f15f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -934,7 +934,7 @@ dependencies = [ name = "defer-stub" version = "0.1.0" dependencies = [ - "near-sdk 4.1.1 (git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd)", + "near-sdk", ] [[package]] @@ -1696,7 +1696,7 @@ dependencies = [ "integration-utils", "maplit", "near-contract-standards", - "near-sdk 4.1.1 (git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd)", + "near-sdk", "near-units", "near-workspaces", "pkg-config", @@ -1710,7 +1710,7 @@ dependencies = [ [[package]] name = "integration-trait" version = "0.1.0" -source = "git+https://github.com/sweatco/integration-utils.git?rev=2c3512ffba1540271a6b09ed266961173fce15c8#2c3512ffba1540271a6b09ed266961173fce15c8" +source = "git+https://github.com/sweatco/integration-utils.git?rev=d5dac489cad973b5c111015a16ec64afaf288c6b#d5dac489cad973b5c111015a16ec64afaf288c6b" dependencies = [ "proc-macro2", "quote", @@ -1720,14 +1720,14 @@ dependencies = [ [[package]] name = "integration-utils" version = "0.1.0" -source = "git+https://github.com/sweatco/integration-utils.git?rev=2c3512ffba1540271a6b09ed266961173fce15c8#2c3512ffba1540271a6b09ed266961173fce15c8" +source = "git+https://github.com/sweatco/integration-utils.git?rev=d5dac489cad973b5c111015a16ec64afaf288c6b#d5dac489cad973b5c111015a16ec64afaf288c6b" dependencies = [ "anyhow", "async-trait", "dotenv", "fake", "futures", - "near-sdk 4.1.1 (git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd)", + "near-sdk", "near-units", "near-workspaces", "tokio", @@ -2039,9 +2039,10 @@ dependencies = [ [[package]] name = "near-contract-standards" version = "4.1.1" -source = "git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd#8c48b26cc48d969c1e5f3162141fe9c824fccecd" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bacc932e79b26472797adfb21689294b6f90960d1570daaf1e0b682b59fcb35" dependencies = [ - "near-sdk 4.1.1 (git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd)", + "near-sdk", "schemars", "serde", "serde_json", @@ -2358,30 +2359,8 @@ dependencies = [ "near-crypto 0.14.0", "near-primitives 0.14.0", "near-primitives-core 0.14.0", - "near-sdk-macros 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "near-sys 0.2.1", - "near-vm-logic", - "once_cell", - "schemars", - "serde", - "serde_json", - "wee_alloc", -] - -[[package]] -name = "near-sdk" -version = "4.1.1" -source = "git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd#8c48b26cc48d969c1e5f3162141fe9c824fccecd" -dependencies = [ - "base64 0.13.1", - "borsh 0.9.3", - "bs58 0.4.0", - "near-abi", - "near-crypto 0.14.0", - "near-primitives 0.14.0", - "near-primitives-core 0.14.0", - "near-sdk-macros 4.1.1 (git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd)", - "near-sys 0.2.0", + "near-sdk-macros", + "near-sys", "near-vm-logic", "once_cell", "schemars", @@ -2402,28 +2381,12 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "near-sdk-macros" -version = "4.1.1" -source = "git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd#8c48b26cc48d969c1e5f3162141fe9c824fccecd" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "near-stdx" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6540152fba5e96fe5d575b79e8cd244cf2add747bb01362426bdc069bc3a23bc" -[[package]] -name = "near-sys" -version = "0.2.0" -source = "git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd#8c48b26cc48d969c1e5f3162141fe9c824fccecd" - [[package]] name = "near-sys" version = "0.2.1" @@ -2541,7 +2504,7 @@ dependencies = [ "near-jsonrpc-primitives", "near-primitives 0.17.0", "near-sandbox-utils", - "near-sdk 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "near-sdk", "near-token", "rand 0.8.5", "reqwest", @@ -3820,7 +3783,7 @@ name = "sweat" version = "0.1.0" dependencies = [ "near-contract-standards", - "near-sdk 4.1.1 (git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd)", + "near-sdk", "static_assertions", "sweat-model", ] @@ -3833,7 +3796,7 @@ dependencies = [ "async-trait", "integration-utils", "near-contract-standards", - "near-sdk 4.1.1 (git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd)", + "near-sdk", "near-units", "near-workspaces", "sweat-model", @@ -3848,7 +3811,7 @@ dependencies = [ "integration-trait", "integration-utils", "near-contract-standards", - "near-sdk 4.1.1 (git+https://github.com/sweatco/near-sdk-rs?rev=8c48b26cc48d969c1e5f3162141fe9c824fccecd)", + "near-sdk", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 460c1fd..f18ecdc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,14 @@ tokio = "1.28.0" anyhow = "1.0.75" async-trait = "0.1.74" +near-sdk = "4.1.1" +near-contract-standards = "4.1.1" + near-units = "0.2.0" near-workspaces = "0.9.0" -near-sdk = { git = "https://github.com/sweatco/near-sdk-rs", rev = "8c48b26cc48d969c1e5f3162141fe9c824fccecd" } -near-contract-standards = { git = "https://github.com/sweatco/near-sdk-rs", rev = "8c48b26cc48d969c1e5f3162141fe9c824fccecd" } -integration-trait = { git = "https://github.com/sweatco/integration-utils.git", rev = "2c3512ffba1540271a6b09ed266961173fce15c8" } -integration-utils = { git = "https://github.com/sweatco/integration-utils.git", rev = "2c3512ffba1540271a6b09ed266961173fce15c8" } - sweat-model = { path = "model" } sweat-integration = { path = "sweat-integration" } + +integration-trait = { git = "https://github.com/sweatco/integration-utils.git", rev = "d5dac489cad973b5c111015a16ec64afaf288c6b" } +integration-utils = { git = "https://github.com/sweatco/integration-utils.git", rev = "d5dac489cad973b5c111015a16ec64afaf288c6b" } diff --git a/res/sweat.wasm b/res/sweat.wasm index 58a3126ae0951fcf3d70044f444fdcb1b23e0108..aad066d42ec5feeea3d25c00917dddbcda4f3b25 100755 GIT binary patch delta 44213 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 Vt6WznTxzBs)M)w@+_8CZ{1@Vq>s0^% delta 46158 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 BX%zqf diff --git a/sweat/src/lib.rs b/sweat/src/lib.rs index b63cfbe..28dd729 100644 --- a/sweat/src/lib.rs +++ b/sweat/src/lib.rs @@ -4,7 +4,6 @@ extern crate static_assertions; use near_contract_standards::fungible_token::{ events::{FtBurn, FtMint}, metadata::{FungibleTokenMetadata, FungibleTokenMetadataProvider}, - FungibleToken, }; use near_sdk::{ borsh::{self, BorshDeserialize, BorshSerialize}, @@ -15,8 +14,11 @@ use near_sdk::{ }; use sweat_model::SweatApi; +use crate::token::FungibleToken; + mod defer; mod math; +pub(crate) mod token; #[near_bindgen] #[derive(BorshSerialize, BorshDeserialize, PanicOnDefault)] diff --git a/sweat/src/token/fungible_token.rs b/sweat/src/token/fungible_token.rs new file mode 100644 index 0000000..103be35 --- /dev/null +++ b/sweat/src/token/fungible_token.rs @@ -0,0 +1,299 @@ +use near_contract_standards::fungible_token::{ + core::FungibleTokenCore, + events::{FtBurn, FtTransfer}, + receiver::ext_ft_receiver, + resolver::{ext_ft_resolver, FungibleTokenResolver}, +}; +use near_sdk::{ + assert_one_yocto, + borsh::{self, BorshDeserialize, BorshSerialize}, + collections::LookupMap, + env, + json_types::U128, + log, require, AccountId, Balance, Gas, IntoStorageKey, PromiseOrValue, PromiseResult, StorageUsage, +}; + +const GAS_FOR_RESOLVE_TRANSFER: Gas = Gas(5_000_000_000_000); +const GAS_FOR_FT_TRANSFER_CALL: Gas = Gas(25_000_000_000_000 + GAS_FOR_RESOLVE_TRANSFER.0); + +const ERR_TOTAL_SUPPLY_OVERFLOW: &str = "Total supply overflow"; + +/// Implementation of a FungibleToken standard. +/// Allows to include NEP-141 compatible token to any contract. +/// There are next traits that any contract may implement: +/// - FungibleTokenCore -- interface with ft_transfer methods. FungibleToken provides methods for it. +/// - FungibleTokenMetaData -- return metadata for the token in NEP-148, up to contract to implement. +/// - StorageManager -- interface for NEP-145 for allocating storage per account. FungibleToken provides methods for it. +/// - AccountRegistrar -- interface for an account to register and unregister +/// +/// For example usage, see examples/fungible-token/src/lib.rs. +#[derive(BorshDeserialize, BorshSerialize)] +pub struct FungibleToken { + /// sha256(AccountId) | AccountId -> Account balance + pub accounts: LookupMapAdapter, + + /// Total supply of the all token. + pub total_supply: Balance, + + /// The storage size in bytes for one account. + pub account_storage_usage: StorageUsage, +} + +#[derive(BorshDeserialize, BorshSerialize)] +pub enum LookupMapKey { + Hash([u8; 32]), + AccountId(String), +} + +#[derive(BorshDeserialize, BorshSerialize)] +pub struct LookupMapAdapter { + inner: LookupMap, + skip_hashing_postfix: Option, +} + +impl FungibleToken { + pub fn new(prefix: S, skip_hashing_postfix: Option) -> Self + where + S: IntoStorageKey, + { + let mut this = Self { + accounts: LookupMapAdapter::new(prefix, skip_hashing_postfix), + total_supply: 0, + account_storage_usage: 0, + }; + this.measure_account_storage_usage(); + this + } + + fn measure_account_storage_usage(&mut self) { + let initial_storage_usage = env::storage_usage(); + let tmp_account_id = AccountId::new_unchecked("a".repeat(64)); + self.accounts.insert(&tmp_account_id, &0u128); + self.account_storage_usage = env::storage_usage() - initial_storage_usage; + self.accounts.remove(&tmp_account_id); + } + + pub fn internal_unwrap_balance_of(&self, account_id: &AccountId) -> Balance { + match self.accounts.get(account_id) { + Some(balance) => balance, + None => env::panic_str(format!("The account {} is not registered", &account_id).as_str()), + } + } + + pub fn internal_deposit(&mut self, account_id: &AccountId, amount: Balance) { + let balance = self.internal_unwrap_balance_of(account_id); + if let Some(new_balance) = balance.checked_add(amount) { + self.accounts.insert(account_id, &new_balance); + self.total_supply = self + .total_supply + .checked_add(amount) + .unwrap_or_else(|| env::panic_str(ERR_TOTAL_SUPPLY_OVERFLOW)); + } else { + env::panic_str("Balance overflow"); + } + } + + pub fn internal_withdraw(&mut self, account_id: &AccountId, amount: Balance) { + let balance = self.internal_unwrap_balance_of(account_id); + if let Some(new_balance) = balance.checked_sub(amount) { + self.accounts.insert(account_id, &new_balance); + self.total_supply = self + .total_supply + .checked_sub(amount) + .unwrap_or_else(|| env::panic_str(ERR_TOTAL_SUPPLY_OVERFLOW)); + } else { + env::panic_str("The account doesn't have enough balance"); + } + } + + pub fn internal_transfer( + &mut self, + sender_id: &AccountId, + receiver_id: &AccountId, + amount: Balance, + memo: Option, + ) { + require!(sender_id != receiver_id, "Sender and receiver should be different"); + require!(amount > 0, "The amount should be a positive number"); + self.internal_withdraw(sender_id, amount); + self.internal_deposit(receiver_id, amount); + FtTransfer { + old_owner_id: sender_id, + new_owner_id: receiver_id, + amount: &U128(amount), + memo: memo.as_deref(), + } + .emit(); + } + + pub fn internal_register_account(&mut self, account_id: &AccountId) { + if self.accounts.insert(account_id, &0).is_some() { + env::panic_str("The account is already registered"); + } + } +} + +impl FungibleTokenCore for FungibleToken { + fn ft_transfer(&mut self, receiver_id: AccountId, amount: U128, memo: Option) { + assert_one_yocto(); + let sender_id = env::predecessor_account_id(); + let amount: Balance = amount.into(); + self.internal_transfer(&sender_id, &receiver_id, amount, memo); + } + + fn ft_transfer_call( + &mut self, + receiver_id: AccountId, + amount: U128, + memo: Option, + msg: String, + ) -> PromiseOrValue { + assert_one_yocto(); + require!(env::prepaid_gas() > GAS_FOR_FT_TRANSFER_CALL, "More gas is required"); + let sender_id = env::predecessor_account_id(); + let amount: Balance = amount.into(); + self.internal_transfer(&sender_id, &receiver_id, amount, memo); + let receiver_gas = env::prepaid_gas() + .0 + .checked_sub(GAS_FOR_FT_TRANSFER_CALL.0) + .unwrap_or_else(|| env::panic_str("Prepaid gas overflow")); + // Initiating receiver's call and the callback + ext_ft_receiver::ext(receiver_id.clone()) + .with_static_gas(receiver_gas.into()) + .ft_on_transfer(sender_id.clone(), amount.into(), msg) + .then( + ext_ft_resolver::ext(env::current_account_id()) + .with_static_gas(GAS_FOR_RESOLVE_TRANSFER) + .ft_resolve_transfer(sender_id, receiver_id, amount.into()), + ) + .into() + } + + fn ft_total_supply(&self) -> U128 { + self.total_supply.into() + } + + fn ft_balance_of(&self, account_id: AccountId) -> U128 { + self.accounts.get(&account_id).unwrap_or(0).into() + } +} + +impl FungibleToken { + /// Internal method that returns the amount of burned tokens in a corner case when the sender + /// has deleted (unregistered) their account while the `ft_transfer_call` was still in flight. + /// Returns (Used token amount, Burned token amount) + pub fn internal_ft_resolve_transfer( + &mut self, + sender_id: &AccountId, + receiver_id: AccountId, + amount: U128, + ) -> (u128, u128) { + let amount: Balance = amount.into(); + + // Get the unused amount from the `ft_on_transfer` call result. + let unused_amount = match env::promise_result(0) { + PromiseResult::NotReady => env::abort(), + PromiseResult::Successful(value) => { + if let Ok(unused_amount) = near_sdk::serde_json::from_slice::(&value) { + std::cmp::min(amount, unused_amount.0) + } else { + amount + } + } + PromiseResult::Failed => amount, + }; + + if unused_amount > 0 { + let receiver_balance = self.accounts.get(&receiver_id).unwrap_or(0); + if receiver_balance > 0 { + let refund_amount = std::cmp::min(receiver_balance, unused_amount); + if let Some(new_receiver_balance) = receiver_balance.checked_sub(refund_amount) { + self.accounts.insert(&receiver_id, &new_receiver_balance); + } else { + env::panic_str("The receiver account doesn't have enough balance"); + } + + if let Some(sender_balance) = self.accounts.get(sender_id) { + if let Some(new_sender_balance) = sender_balance.checked_add(refund_amount) { + self.accounts.insert(sender_id, &new_sender_balance); + } else { + env::panic_str("Sender balance overflow"); + } + + FtTransfer { + old_owner_id: &receiver_id, + new_owner_id: sender_id, + amount: &U128(refund_amount), + memo: Some("refund"), + } + .emit(); + let used_amount = amount + .checked_sub(refund_amount) + .unwrap_or_else(|| env::panic_str(ERR_TOTAL_SUPPLY_OVERFLOW)); + return (used_amount, 0); + } else { + // Sender's account was deleted, so we need to burn tokens. + self.total_supply = self + .total_supply + .checked_sub(refund_amount) + .unwrap_or_else(|| env::panic_str(ERR_TOTAL_SUPPLY_OVERFLOW)); + log!("The account of the sender was deleted"); + FtBurn { + owner_id: &receiver_id, + amount: &U128(refund_amount), + memo: Some("refund"), + } + .emit(); + return (amount, refund_amount); + } + } + } + (amount, 0) + } +} + +impl FungibleTokenResolver for FungibleToken { + fn ft_resolve_transfer(&mut self, sender_id: AccountId, receiver_id: AccountId, amount: U128) -> U128 { + self.internal_ft_resolve_transfer(&sender_id, receiver_id, amount) + .0 + .into() + } +} + +impl LookupMapAdapter { + fn new(prefix: S, skip_hashing_postfix: Option) -> LookupMapAdapter { + Self { + inner: LookupMap::new(prefix), + skip_hashing_postfix: skip_hashing_postfix, + } + } + + fn hash_key(&self, account: &AccountId) -> LookupMapKey { + if let Some(postfix) = &self.skip_hashing_postfix { + if account.as_str().ends_with(postfix) { + LookupMapKey::AccountId(account.to_string()) + } else { + LookupMapKey::Hash(env::sha256_array(account.as_bytes())) + } + } else { + LookupMapKey::Hash(env::sha256_array(account.as_bytes())) + } + } + + pub fn get(&self, key: &AccountId) -> Option { + self.inner.get(&Self::hash_key(&self, key)) + } + + pub fn remove(&mut self, key: &AccountId) -> Option { + self.inner.remove(&Self::hash_key(&self, key)) + } + + pub fn insert(&mut self, key: &AccountId, value: &Balance) -> Option { + self.inner.insert(&Self::hash_key(&self, key), value) + } + + /// Returns true if the map contains a given key. + pub fn contains_key(&self, key: &AccountId) -> bool { + self.inner.contains_key(&Self::hash_key(&self, key)) + } +} diff --git a/sweat/src/token/mod.rs b/sweat/src/token/mod.rs new file mode 100644 index 0000000..ee09b5d --- /dev/null +++ b/sweat/src/token/mod.rs @@ -0,0 +1,4 @@ +mod fungible_token; +mod storage_deposit; + +pub(crate) use fungible_token::*; diff --git a/sweat/src/token/storage_deposit.rs b/sweat/src/token/storage_deposit.rs new file mode 100644 index 0000000..60c6145 --- /dev/null +++ b/sweat/src/token/storage_deposit.rs @@ -0,0 +1,101 @@ +use near_contract_standards::storage_management::{StorageBalance, StorageBalanceBounds, StorageManagement}; +use near_sdk::{assert_one_yocto, env, json_types::U128, log, AccountId, Balance, Promise}; + +use crate::token::FungibleToken; +impl FungibleToken { + /// Internal method that returns the Account ID and the balance in case the account was + /// unregistered. + pub fn internal_storage_unregister(&mut self, force: Option) -> Option<(AccountId, Balance)> { + assert_one_yocto(); + let account_id = env::predecessor_account_id(); + let force = force.unwrap_or(false); + if let Some(balance) = self.accounts.get(&account_id) { + if balance == 0 || force { + self.accounts.remove(&account_id); + self.total_supply -= balance; + Promise::new(account_id.clone()).transfer(self.storage_balance_bounds().min.0 + 1); + Some((account_id, balance)) + } else { + env::panic_str("Can't unregister the account with the positive balance without force") + } + } else { + log!("The account {} is not registered", &account_id); + None + } + } + + fn internal_storage_balance_of(&self, account_id: &AccountId) -> Option { + if self.accounts.contains_key(account_id) { + Some(StorageBalance { + total: self.storage_balance_bounds().min, + available: 0.into(), + }) + } else { + None + } + } +} + +impl StorageManagement for FungibleToken { + // `registration_only` doesn't affect the implementation for vanilla fungible token. + #[allow(unused_variables)] + fn storage_deposit(&mut self, account_id: Option, registration_only: Option) -> StorageBalance { + let amount: Balance = env::attached_deposit(); + let account_id = account_id.unwrap_or_else(env::predecessor_account_id); + if self.accounts.contains_key(&account_id) { + log!("The account is already registered, refunding the deposit"); + if amount > 0 { + Promise::new(env::predecessor_account_id()).transfer(amount); + } + } else { + let min_balance = self.storage_balance_bounds().min.0; + if amount < min_balance { + env::panic_str("The attached deposit is less than the minimum storage balance"); + } + + self.internal_register_account(&account_id); + let refund = amount - min_balance; + if refund > 0 { + Promise::new(env::predecessor_account_id()).transfer(refund); + } + } + self.internal_storage_balance_of(&account_id).unwrap() + } + + /// While storage_withdraw normally allows the caller to retrieve `available` balance, the basic + /// Fungible Token implementation sets storage_balance_bounds.min == storage_balance_bounds.max, + /// which means available balance will always be 0. So this implementation: + /// * panics if `amount > 0` + /// * never transfers Ⓝ to caller + /// * returns a `storage_balance` struct if `amount` is 0 + fn storage_withdraw(&mut self, amount: Option) -> StorageBalance { + assert_one_yocto(); + let predecessor_account_id = env::predecessor_account_id(); + if let Some(storage_balance) = self.internal_storage_balance_of(&predecessor_account_id) { + match amount { + Some(amount) if amount.0 > 0 => { + env::panic_str("The amount is greater than the available storage balance"); + } + _ => storage_balance, + } + } else { + env::panic_str(format!("The account {} is not registered", &predecessor_account_id).as_str()); + } + } + + fn storage_unregister(&mut self, force: Option) -> bool { + self.internal_storage_unregister(force).is_some() + } + + fn storage_balance_bounds(&self) -> StorageBalanceBounds { + let required_storage_balance = Balance::from(self.account_storage_usage) * env::storage_byte_cost(); + StorageBalanceBounds { + min: required_storage_balance.into(), + max: Some(required_storage_balance.into()), + } + } + + fn storage_balance_of(&self, account_id: AccountId) -> Option { + self.internal_storage_balance_of(&account_id) + } +}