Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xCheddar impl #14

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/

/xcheddar/res/
/res
/xcheddar/tests
/neardev

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ members = [
# "./p1-staking-pool-dyn",
"./p2-token-staking-fixed",
"./p3-farm",
"./p3-farm-nft",
"./xcheddar",
"./p4-pool"
]


Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ Cheddar Network is the leading ecosystem for NEAR dapps. Our mission is to be gr
## Cheddar Defi Farm

A Defi token and farm on NEAR. Cheddar is a fun way for NEAR users to collect, swap and send Cheddar. To get Cheddar you can swap NEAR and stake it in the farm to stack even more Cheddar. Cheddar will also include a DAO (Phase II) where users can lock Cheddar to receive governance tokens to participate in the development process while earning additional rewards.

## XCheddar token

Token which allows users stake their Cheddar tokens and get some rewards for it. Base model is the same as CRV and [veCRV](https://resources.curve.fi/base-features/understanding-crv) locked tokens model. After 30-days period distribution of rewards starts and it counting from ```reward_per_sec``` reward parameter. Locked token have a virtual price depends on amount of locked/minted XCheddar tokens
9 changes: 3 additions & 6 deletions cheddar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
serde = { version = "*", features = ["derive"] }
serde_json = "*"
near-sdk = { git = "https://github.com/near/near-sdk-rs", tag="3.1.0" }
near-contract-standards = { git = "https://github.com/near/near-sdk-rs", tag="3.1.0" }
uint = { version = "0.9.0", default-features = false }

[dev-dependencies]
# near-primitives = { git = "https://github.com/nearprotocol/nearcore.git" }
# near-sdk-sim = { git = "https://github.com/near/near-sdk-rs.git", version="v3.1.0" }
near-sys = "0.1.0"
near-contract-standards = "4.0.0"
near-sdk = "4.0.0"
2 changes: 1 addition & 1 deletion cheddar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ Main features of Cheddar Coin are:

## Technicalities

The Cheddar Coin implements the `NEP-141` standard. It's a fungible token.
The Cheddar Coin implements the `NEP-141` standard. It's a fungible token.
8 changes: 4 additions & 4 deletions cheddar/src/internal.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use near_sdk::json_types::{ValidAccountId, U128};
use near_sdk::json_types::U128;
use near_sdk::{AccountId, Balance, PromiseResult};

use crate::storage::AccBalance;
Expand All @@ -13,7 +13,7 @@ impl Contract {
}

#[inline]
pub(crate) fn assert_minter(&self, account_id: String) {
pub(crate) fn assert_minter(&self, account_id: AccountId) {
assert!(self.minters.contains(&account_id), "not a minter");
}

Expand Down Expand Up @@ -123,10 +123,10 @@ impl Contract {
pub(crate) fn ft_resolve_transfer_adjust(
&mut self,
sender_id: &AccountId,
receiver_id: ValidAccountId,
receiver_id: AccountId,
amount: U128,
) -> (u128, u128) {
let receiver_id: AccountId = receiver_id.into();

let amount: Balance = amount.into();

// Get the unused amount from the `ft_on_transfer` call result.
Expand Down
123 changes: 63 additions & 60 deletions cheddar/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/// Cheddar Token
///
/// Functionality:
/// - No account storage complexity - Since NEAR slashed storage price by 10x
/// it does not make sense to add that friction (storage backup per user).
Expand All @@ -11,24 +10,15 @@
///
use near_contract_standards::fungible_token::{
core::FungibleTokenCore,
metadata::{FungibleTokenMetadata, FungibleTokenMetadataProvider, FT_METADATA_SPEC},
resolver::FungibleTokenResolver,
metadata::{FungibleTokenMetadata, FungibleTokenMetadataProvider, FT_METADATA_SPEC},
};
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::{LazyOption, LookupMap};
use near_sdk::json_types::{ValidAccountId, U128};
use near_sdk::json_types::U128;
use near_sdk::{
assert_one_yocto, env, ext_contract, log, near_bindgen, AccountId, Balance, Gas,
assert_one_yocto, env, log, ext_contract, near_bindgen, AccountId, Balance,
PanicOnDefault, PromiseOrValue,
};

const TGAS: Gas = 1_000_000_000_000;
const GAS_FOR_RESOLVE_TRANSFER: Gas = 5 * TGAS;
const GAS_FOR_FT_TRANSFER_CALL: Gas = 25 * TGAS + GAS_FOR_RESOLVE_TRANSFER;
const NO_DEPOSIT: Balance = 0;

near_sdk::setup_alloc!();

mod internal;
mod migrations;
mod storage;
Expand Down Expand Up @@ -165,9 +155,14 @@ impl Contract {
self.metadata.set(&m);
}

pub fn set_owner(&mut self, owner_id: ValidAccountId) {
pub fn set_owner(&mut self, owner_id: AccountId) {
self.assert_owner();
self.owner_id = owner_id.as_ref().clone();
assert!(
env::is_valid_account_id(owner_id.as_bytes()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need to validate, AccountId should be validated automatically

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFIAK, v4 validates account automatically

"Account @{} is invalid!",
owner_id.clone()
);
self.owner_id = owner_id.clone();
}

/// Get the owner of this account.
Expand Down Expand Up @@ -240,86 +235,84 @@ impl Contract {
#[near_bindgen]
impl FungibleTokenCore for Contract {
#[payable]
fn ft_transfer(&mut self, receiver_id: ValidAccountId, amount: U128, memo: Option<String>) {
fn ft_transfer(&mut self, receiver_id: AccountId, amount: U128, memo: Option<String>) {
robert-zaremba marked this conversation as resolved.
Show resolved Hide resolved
assert_one_yocto();
let sender_id = env::predecessor_account_id();
let amount: Balance = amount.into();
self.internal_transfer(&sender_id, receiver_id.as_ref(), amount, memo);
self.internal_transfer(&sender_id, &receiver_id, amount, memo);
}

#[payable]
fn ft_transfer_call(
&mut self,
receiver_id: ValidAccountId,
receiver_id: AccountId,
amount: U128,
memo: Option<String>,
msg: String,
) -> PromiseOrValue<U128> {
assert_one_yocto();
let sender_id = env::predecessor_account_id();
let amount: Balance = amount.into();
self.internal_transfer(&sender_id, receiver_id.as_ref(), amount, memo);
self.internal_transfer(&sender_id, &receiver_id, amount, memo);
// Initiating receiver's call and the callback
// ext_fungible_token_receiver::ft_on_transfer(
// ext_ft calls like this was deprecated in v4.0.0 near-sdk-rs
/*
ext_ft_receiver::ft_on_transfer(
sender_id.clone(),
amount.into(),
msg,
receiver_id.as_ref(),
receiver_id.clone(),
NO_DEPOSIT,
env::prepaid_gas() - GAS_FOR_FT_TRANSFER_CALL,
)
.then(ext_self::ft_resolve_transfer(
sender_id,
receiver_id.into(),
receiver_id,
amount.into(),
&env::current_account_id(),
env::current_account_id(),
NO_DEPOSIT,
GAS_FOR_RESOLVE_TRANSFER,
))
*/
ext_ft_receiver::ext(receiver_id.clone())
.with_static_gas(env::prepaid_gas() - GAS_FOR_FT_TRANSFER_CALL)
.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()),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we want to update to a new API, then let's remove the previous version. Although I would keep the original and only do minimal changes to already deployed token.

.into()
}

fn ft_total_supply(&self) -> U128 {
self.total_supply.into()
}

fn ft_balance_of(&self, account_id: ValidAccountId) -> U128 {
self._balance_of(account_id.as_ref()).into()
}
}

#[near_bindgen]
impl FungibleTokenResolver for Contract {
/// 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)
#[private]
fn ft_resolve_transfer(
&mut self,
sender_id: ValidAccountId,
receiver_id: ValidAccountId,
amount: U128,
) -> U128 {
let sender_id: AccountId = sender_id.into();
let (used_amount, burned_amount) =
self.ft_resolve_transfer_adjust(&sender_id, receiver_id, amount);
if burned_amount > 0 {
log!("{} tokens burned", burned_amount);
}
return used_amount.into();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why removing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I correctly remember I do this because of new standards version


#[near_bindgen]
impl FungibleTokenMetadataProvider for Contract {
fn ft_metadata(&self) -> FungibleTokenMetadata {
self.internal_get_ft_metadata()
fn ft_balance_of(&self, account_id: AccountId) -> U128 {
self._balance_of(&account_id).into()
}
}

#[ext_contract(ext_ft_receiver)]
pub trait FungibleTokenReceiver {
/// Called by fungible token contract after `ft_transfer_call` was initiated by
/// `sender_id` of the given `amount` with the transfer message given in `msg` field.
/// The `amount` of tokens were already transferred to this contract account and ready to be used.
///
/// The method must return the amount of tokens that are *not* used/accepted by this contract from the transferred
/// amount. Examples:
/// - The transferred amount was `500`, the contract completely takes it and must return `0`.
/// - The transferred amount was `500`, but this transfer call only needs `450` for the action passed in the `msg`
/// field, then the method must return `50`.
/// - The transferred amount was `500`, but the action in `msg` field has expired and the transfer must be
/// cancelled. The method must return `500` or panic.
///
/// Arguments:
/// - `sender_id` - the account ID that initiated the transfer.
/// - `amount` - the amount of tokens that were transferred to this account in a decimal string representation.
/// - `msg` - a string message that was passed with this transfer call.
///
/// Returns the amount of unused tokens that should be returned to sender, in a decimal string representation.
fn ft_on_transfer(
&mut self,
sender_id: AccountId,
Expand All @@ -328,8 +321,11 @@ pub trait FungibleTokenReceiver {
) -> PromiseOrValue<U128>;
}

#[ext_contract(ext_self)]
trait FungibleTokenResolver {
#[ext_contract(ext_ft_resolver)]
pub trait FungibleTokenResolver {
/// 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)
fn ft_resolve_transfer(
&mut self,
sender_id: AccountId,
Expand All @@ -338,6 +334,13 @@ trait FungibleTokenResolver {
) -> U128;
}

#[near_bindgen]
impl FungibleTokenMetadataProvider for Contract {
fn ft_metadata(&self) -> FungibleTokenMetadata {
self.internal_get_ft_metadata()
}
}

#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests {
use near_sdk::test_utils::{accounts, VMContextBuilder};
Expand All @@ -347,7 +350,7 @@ mod tests {

const OWNER_SUPPLY: Balance = 1_000_000_000_000_000_000_000_000_000_000;

fn get_context(predecessor_account_id: ValidAccountId) -> VMContextBuilder {
fn get_context(predecessor_account_id: AccountId) -> VMContextBuilder {
let mut builder = VMContextBuilder::new();
builder
.current_account_id(accounts(0))
Expand All @@ -366,7 +369,7 @@ mod tests {
.attached_deposit(1)
.predecessor_account_id(accounts(1))
.build());
contract.mint(&accounts(1).to_string(), OWNER_SUPPLY.into());
contract.mint(&accounts(1), OWNER_SUPPLY.into());

testing_env!(context.is_view(true).build());
assert_eq!(contract.ft_total_supply().0, OWNER_SUPPLY);
Expand All @@ -391,7 +394,7 @@ mod tests {
.attached_deposit(1)
.predecessor_account_id(accounts(2))
.build());
contract.mint(&accounts(2).to_string(), OWNER_SUPPLY.into());
contract.mint(&accounts(2), OWNER_SUPPLY.into());

testing_env!(context
.storage_usage(env::storage_usage())
Expand Down
24 changes: 11 additions & 13 deletions cheddar/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use near_contract_standards::storage_management::{
StorageBalance, StorageBalanceBounds, StorageManagement,
};
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::json_types::{ValidAccountId, U128};
use near_sdk::json_types::U128;
use near_sdk::{assert_one_yocto, env, log, near_bindgen, AccountId, Balance, Promise};

// The storage size in bytes for one account.
Expand Down Expand Up @@ -32,7 +32,7 @@ impl Contract {
)
.is_some()
{
env::panic("The account is already registered".as_bytes());
panic!("The account is already registered");
}
}

Expand Down Expand Up @@ -70,10 +70,7 @@ impl Contract {
}
Some((account_id, balance.near))
} else {
env::panic(
"Can't unregister the account with the positive balance without force"
.as_bytes(),
)
panic!("Can't unregister the account with the positive balance without force")
}
} else {
log!("The account {} is not registered", &account_id);
Expand All @@ -94,7 +91,7 @@ impl StorageManagement for Contract {
#[payable]
fn storage_deposit(
&mut self,
account_id: Option<ValidAccountId>,
account_id: Option<AccountId>,
registration_only: Option<bool>,
) -> StorageBalance {
let amount: Balance = env::attached_deposit();
Expand Down Expand Up @@ -137,15 +134,16 @@ impl StorageManagement for Contract {
if self.accounts.contains_key(&predecessor_account_id) {
match amount {
Some(amount) if amount.0 > 0 => {
env::panic(
"The amount is greater than the available storage balance".as_bytes(),
panic!(
"The amount is greater than the available storage balance",
);
}
_ => storage_balance(),
}
} else {
env::panic(
format!("The account {} is not registered", &predecessor_account_id).as_bytes(),
panic!(
"The account {} is not registered",
predecessor_account_id
);
}
}
Expand All @@ -162,8 +160,8 @@ impl StorageManagement for Contract {
}
}

fn storage_balance_of(&self, account_id: ValidAccountId) -> Option<StorageBalance> {
if self.accounts.contains_key(account_id.as_ref()) {
fn storage_balance_of(&self, account_id: AccountId) -> Option<StorageBalance> {
if self.accounts.contains_key(&account_id) {
Some(storage_balance())
} else {
None
Expand Down
Loading