diff --git a/w-near/Cargo.lock b/w-near-141/Cargo.lock similarity index 100% rename from w-near/Cargo.lock rename to w-near-141/Cargo.lock diff --git a/w-near/Cargo.toml b/w-near-141/Cargo.toml similarity index 100% rename from w-near/Cargo.toml rename to w-near-141/Cargo.toml diff --git a/w-near-141/README.md b/w-near-141/README.md new file mode 100644 index 00000000..70644910 --- /dev/null +++ b/w-near-141/README.md @@ -0,0 +1 @@ +# TBD diff --git a/w-near-141/build.sh b/w-near-141/build.sh new file mode 100755 index 00000000..f2dc4d0b --- /dev/null +++ b/w-near-141/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e +cd "`dirname $0`" +RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release +cp target/wasm32-unknown-unknown/release/w_near.wasm ./res/ diff --git a/w-near-141/res/w_near.wasm b/w-near-141/res/w_near.wasm new file mode 100755 index 00000000..675d2814 Binary files /dev/null and b/w-near-141/res/w_near.wasm differ diff --git a/w-near-141/src/fungible_token_core.rs b/w-near-141/src/fungible_token_core.rs new file mode 100644 index 00000000..e0f0d40b --- /dev/null +++ b/w-near-141/src/fungible_token_core.rs @@ -0,0 +1,205 @@ +use crate::*; +use near_sdk::json_types::ValidAccountId; +use near_sdk::{ext_contract, Gas, PromiseResult}; + +const GAS_FOR_RESOLVE_TRANSFER: Gas = 5_000_000_000_000; +const GAS_FOR_FT_TRANSFER_CALL: Gas = 25_000_000_000_000 + GAS_FOR_RESOLVE_TRANSFER; + +const NO_DEPOSIT: Balance = 0; + +pub trait FungibleTokenCore { + /// Transfers positive `amount` of tokens from the `env::predecessor_account_id` to `receiver_id`. + /// Both accounts must be registered with the contract for transfer to succeed. (See [NEP-145](https://github.com/near/NEPs/discussions/145)) + /// This method must to be able to accept attached deposits, and must not panic on attached deposit. + /// Exactly 1 yoctoNEAR must be attached. + /// See [the Security section](https://github.com/near/NEPs/issues/141#user-content-security) of the standard. + /// + /// Arguments: + /// - `receiver_id` - the account ID of the receiver. + /// - `amount` - the amount of tokens to transfer. Must be a positive number in decimal string representation. + /// - `memo` - an optional string field in a free form to associate a memo with this transfer. + fn ft_transfer(&mut self, receiver_id: ValidAccountId, amount: U128, memo: Option); + + /// Transfers positive `amount` of tokens from the `env::predecessor_account_id` to `receiver_id` account. Then + /// calls `ft_on_transfer` method on `receiver_id` contract and attaches a callback to resolve this transfer. + /// `ft_on_transfer` method must return the amount of tokens unused by the receiver contract, the remaining tokens + /// must be refunded to the `predecessor_account_id` at the resolve transfer callback. + /// + /// Token contract must pass all the remaining unused gas to the `ft_on_transfer` call. + /// + /// Malicious or invalid behavior by the receiver's contract: + /// - If the receiver contract promise fails or returns invalid value, the full transfer amount must be refunded. + /// - If the receiver contract overspent the tokens, and the `receiver_id` balance is lower than the required refund + /// amount, the remaining balance must be refunded. See [the Security section](https://github.com/near/NEPs/issues/141#user-content-security) of the standard. + /// + /// Both accounts must be registered with the contract for transfer to succeed. (See #145) + /// This method must to be able to accept attached deposits, and must not panic on attached deposit. Exactly 1 yoctoNEAR must be attached. See [the Security + /// section](https://github.com/near/NEPs/issues/141#user-content-security) of the standard. + /// + /// Arguments: + /// - `receiver_id` - the account ID of the receiver contract. This contract will be called. + /// - `amount` - the amount of tokens to transfer. Must be a positive number in a decimal string representation. + /// - `msg` - a string message that will be passed to `ft_on_transfer` contract call. + /// - `memo` - an optional string field in a free form to associate a memo with this transfer. + /// + /// Returns a promise which will result in the amount of tokens withdrawn from sender's account. + fn ft_transfer_call( + &mut self, + receiver_id: ValidAccountId, + amount: U128, + msg: String, + memo: Option, + ) -> Promise; + + /// Returns the total supply of the token in a decimal string representation. + fn ft_total_supply(&self) -> U128; + + /// Returns the balance of the account. If the account doesn't exist must returns `"0"`. + fn ft_balance_of(&self, account_id: ValidAccountId) -> U128; +} + +#[ext_contract(ext_fungible_token_receiver)] +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, amount: U128, msg: String) -> Promise; +} + +#[ext_contract(ext_self)] +trait FungibleTokenResolver { + fn ft_resolve_transfer( + &mut self, + sender_id: AccountId, + receiver_id: AccountId, + amount: U128, + ) -> U128; +} + +trait FungibleTokenResolver { + fn ft_resolve_transfer( + &mut self, + sender_id: AccountId, + receiver_id: AccountId, + amount: U128, + ) -> U128; +} + +#[near_bindgen] +impl FungibleTokenCore for Contract { + #[payable] + fn ft_transfer(&mut self, receiver_id: ValidAccountId, amount: U128, memo: Option) { + assert_one_yocto(); + let sender_id = env::predecessor_account_id(); + let amount = amount.into(); + self.internal_transfer(&sender_id, receiver_id.as_ref(), amount, memo); + } + + #[payable] + fn ft_transfer_call( + &mut self, + receiver_id: ValidAccountId, + amount: U128, + msg: String, + memo: Option, + ) -> Promise { + assert_one_yocto(); + let sender_id = env::predecessor_account_id(); + let amount = amount.into(); + self.internal_transfer(&sender_id, receiver_id.as_ref(), amount, memo); + // Initiating receiver's call and the callback + ext_fungible_token_receiver::ft_on_transfer( + sender_id.clone(), + amount.into(), + msg, + receiver_id.as_ref(), + NO_DEPOSIT, + env::prepaid_gas() - GAS_FOR_FT_TRANSFER_CALL, + ) + .then(ext_self::ft_resolve_transfer( + sender_id, + receiver_id.into(), + amount.into(), + &env::current_account_id(), + NO_DEPOSIT, + GAS_FOR_RESOLVE_TRANSFER, + )) + } + + fn ft_total_supply(&self) -> U128 { + self.total_supply.into() + } + + fn ft_balance_of(&self, account_id: ValidAccountId) -> U128 { + self.accounts.get(account_id.as_ref()).unwrap_or(0).into() + } +} + +#[near_bindgen] +impl FungibleTokenResolver for Contract { + fn ft_resolve_transfer( + &mut self, + sender_id: AccountId, + receiver_id: AccountId, + amount: U128, + ) -> U128 { + assert_self(); + 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 => unreachable!(), + 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); + self.accounts + .insert(&receiver_id, &(receiver_balance - refund_amount)); + + if let Some(sender_balance) = self.accounts.get(&sender_id) { + self.accounts + .insert(&sender_id, &(sender_balance + refund_amount)); + env::log( + format!( + "Refund {} from {} to {}", + refund_amount, receiver_id, sender_id + ) + .as_bytes(), + ); + return (amount - refund_amount).into(); + } else { + // Sender's account was deleted, so we need to burn tokens. + self.total_supply -= refund_amount; + env::log(b"The account of the sender was deleted"); + env::log(format!("Burn {}", refund_amount).as_bytes()); + } + } + } + amount.into() + } +} diff --git a/w-near-141/src/fungible_token_metadata.rs b/w-near-141/src/fungible_token_metadata.rs new file mode 100644 index 00000000..e79fd6c5 --- /dev/null +++ b/w-near-141/src/fungible_token_metadata.rs @@ -0,0 +1,31 @@ +use super::*; +use near_sdk::serde::Serialize; + +#[derive(Serialize)] +#[serde(crate = "near_sdk::serde")] +pub struct FungibleTokenMetadata { + version: String, + name: String, + symbol: String, + reference: String, + decimals: u8, +} + +pub trait FungibleTokenMetadataProvider { + fn ft_metadata() -> FungibleTokenMetadata; +} + +#[near_bindgen] +impl FungibleTokenMetadataProvider for Contract { + fn ft_metadata() -> FungibleTokenMetadata { + FungibleTokenMetadata { + version: String::from("0.1.0"), + name: String::from("Wrapped NEAR fungible token"), + symbol: String::from("wNEAR"), + reference: String::from( + "https://github.com/near/core-contracts/tree/master/w-near-141", + ), + decimals: 24, + } + } +} diff --git a/w-near-141/src/internal.rs b/w-near-141/src/internal.rs new file mode 100644 index 00000000..8bc1c15d --- /dev/null +++ b/w-near-141/src/internal.rs @@ -0,0 +1,63 @@ +use crate::*; + +pub(crate) fn assert_one_yocto() { + assert_eq!( + env::attached_deposit(), + 1, + "Requires attached deposit of exactly 1 yoctoNEAR" + ) +} + +pub(crate) fn assert_self() { + assert_eq!( + env::predecessor_account_id(), + env::current_account_id(), + "Method is private" + ); +} + +impl Contract { + pub(crate) fn internal_deposit(&mut self, account_id: &AccountId, amount: Balance) { + let balance = self + .accounts + .get(&account_id) + .expect("The account is not registered"); + if let Some(new_balance) = balance.checked_add(amount) { + self.accounts.insert(&account_id, &new_balance); + } else { + env::panic(b"Balance overflow"); + } + } + + pub(crate) fn internal_withdraw(&mut self, account_id: &AccountId, amount: Balance) { + let balance = self + .accounts + .get(&account_id) + .expect("The account is not registered"); + if let Some(new_balance) = balance.checked_sub(amount) { + self.accounts.insert(&account_id, &new_balance); + } else { + env::panic(b"The account doesn't have enough balance"); + } + } + + pub(crate) fn internal_transfer( + &mut self, + sender_id: &AccountId, + receiver_id: &AccountId, + amount: Balance, + memo: Option, + ) { + assert_ne!( + sender_id, receiver_id, + "Sender and receiver should be different" + ); + assert!(amount > 0, "The amount should be a positive number"); + self.internal_withdraw(sender_id, amount); + self.internal_deposit(receiver_id, amount); + env::log(format!("Transfer {} from {} to {}", amount, sender_id, receiver_id).as_bytes()); + if let Some(memo) = memo { + env::log(format!("Memo: {}", memo).as_bytes()); + } + } +} diff --git a/w-near-141/src/lib.rs b/w-near-141/src/lib.rs new file mode 100644 index 00000000..89187e1f --- /dev/null +++ b/w-near-141/src/lib.rs @@ -0,0 +1,987 @@ +/** +* wNear NEP-141 Token contract +* +* The aim of the contract is to enable the wrapping of the native NEAR token into a NEP-141 compatible token. +* It supports methods `near_deposit` and `near_withdraw` that wraps and unwraps NEAR tokens. +* They are effectively mint and burn underlying wNEAR tokens. +* +* lib.rs is the main entry point. +* fungible_token_core.rs implements NEP-146 standard +* storage_manager.rs implements NEP-145 standard for allocating storage per account +* fungible_token_metadata.rs implements NEP-148 standard for providing token-specific metadata. +* w_near.rs contains interfaces for depositing and withdrawing +* internal.rs contains internal methods for fungible token. +*/ +use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use near_sdk::collections::LookupMap; +use near_sdk::json_types::U128; +use near_sdk::{env, near_bindgen, AccountId, Balance, Promise, StorageUsage}; + +pub use crate::fungible_token_core::*; +pub use crate::fungible_token_metadata::*; +use crate::internal::*; +pub use crate::storage_manager::*; +pub use crate::w_near::*; + +mod fungible_token_core; +mod fungible_token_metadata; +mod internal; +mod storage_manager; +mod w_near; + +#[global_allocator] +static ALLOC: near_sdk::wee_alloc::WeeAlloc<'_> = near_sdk::wee_alloc::WeeAlloc::INIT; + +#[near_bindgen] +#[derive(BorshDeserialize, BorshSerialize)] +pub struct Contract { + /// AccountID -> Account balance. + pub accounts: LookupMap, + + /// Total supply of the all token. + pub total_supply: Balance, + + /// The storage size in bytes for one account. + pub account_storage_usage: StorageUsage, +} + +impl Default for Contract { + fn default() -> Self { + env::panic(b"Contract is not initialized"); + } +} + +#[near_bindgen] +impl Contract { + #[init] + pub fn new() -> Self { + assert!(!env::state_exists(), "Already initialized"); + let mut this = Self { + accounts: LookupMap::new(b"a".to_vec()), + total_supply: 0, + account_storage_usage: 0, + }; + let initial_storage_usage = env::storage_usage(); + let tmp_account_id = unsafe { String::from_utf8_unchecked(vec![b'a'; 64]) }; + this.accounts.insert(&tmp_account_id, &0u128); + this.account_storage_usage = env::storage_usage() - initial_storage_usage; + this.accounts.remove(&tmp_account_id); + this + } +} + +#[cfg(not(target_arch = "wasm32"))] +#[cfg(test)] +mod w_near_tests { + use near_sdk::MockedBlockchain; + use near_sdk::{testing_env, VMContext}; + + use super::*; + + const ZERO_U128: Balance = 0u128; + + fn alice() -> AccountId { + "alice.near".to_string() + } + fn bob() -> AccountId { + "bob.near".to_string() + } + fn carol() -> AccountId { + "carol.near".to_string() + } + fn w_near() -> AccountId { + "w_near.near".to_string() + } + fn invalid_account_id() -> AccountId { + "".to_string() + } + + fn get_context(predecessor_account_id: AccountId) -> VMContext { + VMContext { + current_account_id: w_near(), + signer_account_id: bob(), + signer_account_pk: vec![0, 1, 2], + predecessor_account_id, + input: vec![], + block_index: 0, + block_timestamp: 0, + account_balance: 1000 * 10u128.pow(24), + account_locked_balance: 0, + storage_usage: 10u64.pow(6), + attached_deposit: 0, + prepaid_gas: 10u64.pow(18), + random_seed: vec![0, 1, 2], + is_view: false, + output_data_receivers: vec![], + epoch_height: 0, + } + } + + #[test] + fn contract_creation_with_new() { + testing_env!(get_context(carol())); + //let total_supply = 1_000_000_000_000_000u128; + let contract = FungibleToken::new(); + assert_eq!(contract.get_total_supply().0, ZERO_U128); + assert_eq!(contract.get_balance(alice()).0, ZERO_U128); + assert_eq!(contract.get_balance(bob()).0, ZERO_U128); + assert_eq!(contract.get_balance(carol()).0, ZERO_U128); + } + + #[test] + #[should_panic(expected = "Contract should be initialized before usage.")] + fn default_fails() { + testing_env!(get_context(carol())); + let _contract = FungibleToken::default(); + } + + #[test] + fn deposit() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount + (133 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + contract.deposit(deposit_amount.into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + } + + #[test] + fn deposit_to_bob_from_carol() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount + (133 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + contract.deposit_to(bob(), deposit_amount.into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, 0); + assert_eq!(contract.get_balance(bob()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + } + + #[test] + #[should_panic(expected = "Deposit amount must be greater than zero")] + fn deposit_fails_when_amount_is_zero() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.deposit_to(bob(), ZERO_U128.into()); + } + + #[test] + #[should_panic(expected = "New owner's account ID is invalid")] + fn deposit_to_fails_when_recipient_is_invalid() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.deposit_to(invalid_account_id(), (5u128).into()); + } + + #[test] + #[should_panic(expected = "Invalid transfer to this contract")] + fn deposit_to_fails_when_recipient_is_w_near_contract() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.deposit_to(w_near(), (5u128).into()); + } + + #[test] + #[should_panic( + expected = "The required attached deposit is 13300001000000000000000, but the given attached deposit is is 13300000000000000000000" + )] + fn deposit_to_fails_when_the_required_deposit_is_not_attached() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = 133 * STORAGE_PRICE_PER_BYTE; // attach required storage but not deposit + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + contract.deposit(deposit_amount.into()); + } + + #[test] + fn simple_deposit_and_withdrawal() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount.clone() + (133 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + contract.deposit(deposit_amount.clone().into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + + contract.withdraw(deposit_amount.clone().into()); + + assert_eq!(contract.get_balance(carol()).0, 0); + assert_eq!(contract.get_total_supply().0, 0); + } + + #[test] + fn simple_deposit_by_carol_and_withdrawal_to_alice() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount.clone() + (133 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + contract.deposit(deposit_amount.clone().into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + + contract.withdraw_to(alice(), deposit_amount.clone().into()); + + // TODO: check alice near balance has increased + assert_eq!(contract.get_balance(carol()).0, 0); + assert_eq!(contract.get_total_supply().0, 0); + } + + #[test] + #[should_panic(expected = "Withdrawal amount must be greater than zero")] + fn withdraw_fails_when_withdrawal_amount_is_zero() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.withdraw(ZERO_U128.into()); + } + + #[test] + #[should_panic(expected = "New owner's account ID is invalid")] + fn withdraw_to_fails_when_recipient_is_invalid() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.withdraw_to(invalid_account_id(), (5u128).into()); + } + + #[test] + #[should_panic(expected = "Invalid transfer to this contract")] + fn withdraw_to_fails_when_recipient_is_w_near_contract() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.withdraw_to(w_near(), (5u128).into()); + } + + #[test] + #[should_panic(expected = "Burning more than the account balance")] + fn withdraw_to_fails_when_carol_tries_to_withdraw_more_than_her_w_near_balance() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount.clone() + (133 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + contract.deposit(deposit_amount.clone().into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + + contract.withdraw((deposit_amount.clone() + 1).into()); + } + + #[test] + #[should_panic(expected = "Withdrawal amount must be greater than zero")] + fn withdraw_from_fails_when_the_withdrawal_amount_is_zero() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.withdraw_from(alice(), carol(), ZERO_U128.into()); + } + + #[test] + #[should_panic(expected = "New owner's account ID is invalid")] + fn withdraw_from_fails_when_the_recipient_is_invalid() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.withdraw_from(alice(), invalid_account_id(), (5u128).into()); + } + + #[test] + #[should_panic(expected = "Invalid transfer to this contract")] + fn withdraw_from_fails_when_the_recipient_is_the_w_near_contract() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.withdraw_from(alice(), w_near(), (5u128).into()); + } + + #[test] + #[should_panic(expected = "The new owner should be different from the current owner")] + fn withdraw_from_fails_when_the_owner_and_recipient_are_the_same() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.withdraw_from(alice(), alice(), (5u128).into()); + } + + #[test] + #[should_panic(expected = "Not enough allowance")] + fn withdraw_from_fails_when_the_escrow_account_does_not_have_enough_allowance() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + context.attached_deposit = 0; + testing_env!(context.clone()); + + contract.withdraw_from(alice(), bob(), (5u128).into()); + } + + #[test] + #[should_panic(expected = "Burning more than the account balance")] + fn withdraw_from_fails_when_trying_to_withdraw_more_than_owners_balance() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount.clone() + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // get some wNear for carol + contract.deposit(deposit_amount.clone().into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + + // give bob allowance to withdraw some tokens + assert_eq!(contract.get_allowance(carol(), bob()), ZERO_U128.into()); + + contract.inc_allowance(bob(), std::u128::MAX.into()); + + assert_eq!( + contract.get_allowance(carol(), bob()), + std::u128::MAX.into() + ); + + // switch to a context with bob + let mut context = get_context(bob()); + testing_env!(context.clone()); + context.attached_deposit = 1000 * STORAGE_PRICE_PER_BYTE; + testing_env!(context.clone()); + + contract.withdraw_from(carol(), bob(), (deposit_amount.clone() + 1).into()); + } + + #[test] + fn withdraw_from_with_correct_allowance_should_be_successful() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount.clone() + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // get some wNear for carol + contract.deposit(deposit_amount.clone().into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + + // give bob allowance to withdraw some tokens + assert_eq!(contract.get_allowance(carol(), bob()), ZERO_U128.into()); + + let withdrawal_amount = deposit_amount.clone() / 2; + contract.inc_allowance(bob(), withdrawal_amount.clone().into()); + + assert_eq!( + contract.get_allowance(carol(), bob()), + withdrawal_amount.clone().into() + ); + + // switch to a context with bob + let mut context = get_context(bob()); + testing_env!(context.clone()); + context.attached_deposit = 1000 * STORAGE_PRICE_PER_BYTE; + testing_env!(context.clone()); + + assert_eq!(contract.get_balance(bob()).0, ZERO_U128.into()); + + contract.withdraw_from(carol(), bob(), withdrawal_amount.clone().into()); + + //todo: check bob's near balance + assert_eq!(contract.get_balance(bob()).0, ZERO_U128.into()); + assert_eq!(contract.get_allowance(carol(), bob()), ZERO_U128.into()); + assert_eq!( + contract.get_balance(carol()).0, + withdrawal_amount.clone().into() + ); + assert_eq!( + contract.get_total_supply().0, + withdrawal_amount.clone().into() + ); + } + + #[test] + fn withdraw_from_with_infinite_allowance_should_not_reduce_allowance() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount.clone() + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // get some wNear for carol + contract.deposit(deposit_amount.clone().into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + + // give bob allowance to withdraw some tokens + assert_eq!(contract.get_allowance(carol(), bob()), ZERO_U128.into()); + + let withdrawal_amount = deposit_amount.clone() / 2; + contract.inc_allowance(bob(), std::u128::MAX.into()); + + assert_eq!( + contract.get_allowance(carol(), bob()), + std::u128::MAX.into() + ); + + // switch to a context with bob + let mut context = get_context(bob()); + testing_env!(context.clone()); + context.attached_deposit = 1000 * STORAGE_PRICE_PER_BYTE; + testing_env!(context.clone()); + + assert_eq!(contract.get_balance(bob()).0, ZERO_U128.into()); + + contract.withdraw_from(carol(), bob(), withdrawal_amount.clone().into()); + + //todo: check bob's near balance + assert_eq!(contract.get_balance(bob()).0, ZERO_U128.into()); + assert_eq!( + contract.get_allowance(carol(), bob()), + std::u128::MAX.into() + ); + assert_eq!( + contract.get_balance(carol()).0, + withdrawal_amount.clone().into() + ); + assert_eq!( + contract.get_total_supply().0, + withdrawal_amount.clone().into() + ); + } + + #[test] + fn transfer_with_infinite_allowance_should_not_reduce_allowance() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount.clone() + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // get some wNear for carol + contract.deposit(deposit_amount.clone().into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + + // give bob allowance to withdraw some tokens + assert_eq!(contract.get_allowance(carol(), bob()), ZERO_U128.into()); + + contract.inc_allowance(bob(), std::u128::MAX.into()); + + assert_eq!( + contract.get_allowance(carol(), bob()), + std::u128::MAX.into() + ); + + // switch to a context with bob + let mut context = get_context(bob()); + testing_env!(context.clone()); + context.attached_deposit = 1000 * STORAGE_PRICE_PER_BYTE; + testing_env!(context.clone()); + + assert_eq!(contract.get_balance(bob()).0, ZERO_U128.into()); + + let transfer_amount = deposit_amount.clone() / 2; + contract.transfer_from(carol(), bob(), transfer_amount.clone().into()); + + assert_eq!( + contract.get_balance(bob()).0, + transfer_amount.clone().into() + ); + assert_eq!( + contract.get_allowance(carol(), bob()), + std::u128::MAX.into() + ); + assert_eq!( + contract.get_balance(carol()).0, + transfer_amount.clone().into() + ); + assert_eq!(contract.get_total_supply().0, deposit_amount.clone().into()); + } + + #[test] + #[should_panic(expected = "Burning more than the account balance")] + fn withdraw_from_with_infinite_allowance_should_not_withdraw_more_than_balance() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount.clone() + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // get some wNear for carol + contract.deposit(deposit_amount.clone().into()); + + //assert_eq!(contract.get_near_balance().0, 0); + + // TODO: check contract balance == deposit amount + assert_eq!(contract.get_balance(carol()).0, deposit_amount); + assert_eq!(contract.get_total_supply().0, deposit_amount); + + // give bob allowance to withdraw some tokens + assert_eq!(contract.get_allowance(carol(), bob()), ZERO_U128.into()); + + contract.inc_allowance(bob(), std::u128::MAX.into()); + + assert_eq!( + contract.get_allowance(carol(), bob()), + std::u128::MAX.into() + ); + + // switch to a context with bob + let mut context = get_context(bob()); + testing_env!(context.clone()); + context.attached_deposit = 1000 * STORAGE_PRICE_PER_BYTE; + testing_env!(context.clone()); + + assert_eq!(contract.get_balance(bob()).0, ZERO_U128.into()); + + contract.withdraw_from(carol(), bob(), deposit_amount.clone().into()); + contract.withdraw_from(carol(), bob(), deposit_amount.clone().into()); + } + + #[test] + fn transfer_after_deposit() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + // get some wNear tokens + contract.deposit(deposit_amount.into()); + + let transfer_amount = 1_000_000_000_000_000u128 / 3; + contract.transfer(bob(), transfer_amount.into()); + context.storage_usage = env::storage_usage(); + context.account_balance = env::account_balance(); + + context.is_view = true; + context.attached_deposit = 0; + testing_env!(context.clone()); + assert_eq!( + contract.get_balance(carol()).0, + (1_000_000_000_000_000u128 - transfer_amount) + ); + assert_eq!(contract.get_balance(bob()).0, transfer_amount); + } + + #[test] + #[should_panic(expected = "The new owner should be different from the current owner")] + fn transfer_fail_self() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + // get some wNear tokens + contract.deposit(deposit_amount.into()); + + let transfer_amount = deposit_amount.clone() / 2; + contract.transfer(carol(), transfer_amount.into()); + } + + #[test] + #[should_panic(expected = "Invalid transfer to this contract")] + fn transfer_fail_to_contract() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let deposit_amount = 1_000_000_000_000_000u128; + context.attached_deposit = deposit_amount + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + // get some wNear tokens + contract.deposit(deposit_amount.into()); + + let transfer_amount = deposit_amount.clone() / 2; + contract.transfer(w_near(), transfer_amount.into()); + } + + #[test] + #[should_panic(expected = "Can not increment allowance for yourself")] + fn self_inc_allowance_fail() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + + let mut contract = FungibleToken::new(); + + context.attached_deposit = 1000 * STORAGE_PRICE_PER_BYTE; + testing_env!(context.clone()); + + contract.inc_allowance(carol(), (5).into()); + } + + #[test] + #[should_panic(expected = "Can not decrement allowance for yourself")] + fn self_dec_allowance_fail() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.attached_deposit = 1000 * STORAGE_PRICE_PER_BYTE; + testing_env!(context.clone()); + contract.dec_allowance(carol(), (10).into()); + } + + #[test] + fn saturating_dec_allowance() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.attached_deposit = STORAGE_PRICE_PER_BYTE * 1000; + testing_env!(context.clone()); + contract.dec_allowance(bob(), (1_000_000_000_000_000u128 / 2).into()); + assert_eq!(contract.get_allowance(carol(), bob()), 0.into()) + } + + #[test] + fn saturating_inc_allowance() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.attached_deposit = STORAGE_PRICE_PER_BYTE * 1000; + testing_env!(context.clone()); + let max_u128 = std::u128::MAX; + contract.inc_allowance(bob(), max_u128.into()); + contract.inc_allowance(bob(), max_u128.into()); + assert_eq!( + contract.get_allowance(carol(), bob()), + std::u128::MAX.into() + ) + } + + #[test] + #[should_panic( + expected = "The required attached deposit is 25700000000000000000000, but the given attached deposit is is 0" + )] + fn self_allowance_fail_no_deposit() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.attached_deposit = 0; + testing_env!(context.clone()); + contract.inc_allowance(bob(), 5.into()); + } + + #[test] + fn carol_escrows_to_bob_transfers_to_alice() { + // Acting as carol + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + context.is_view = true; + testing_env!(context.clone()); + assert_eq!(contract.get_total_supply().0, 0); + + let deposit_amount = 1_000_000_000_000_000u128; + let allowance = deposit_amount.clone() / 3; + let transfer_amount = allowance / 3; + context.is_view = false; + + context.attached_deposit = deposit_amount + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + // get some wNear tokens + contract.deposit(deposit_amount.into()); + + contract.inc_allowance(bob(), allowance.into()); + context.storage_usage = env::storage_usage(); + context.account_balance = env::account_balance(); + + context.is_view = true; + context.attached_deposit = 0; + testing_env!(context.clone()); + assert_eq!(contract.get_allowance(carol(), bob()).0, allowance); + + // Acting as bob now + context.is_view = false; + context.attached_deposit = STORAGE_PRICE_PER_BYTE * 1000; + context.predecessor_account_id = bob(); + testing_env!(context.clone()); + contract.transfer_from(carol(), alice(), transfer_amount.into()); + context.storage_usage = env::storage_usage(); + context.account_balance = env::account_balance(); + + context.is_view = true; + context.attached_deposit = 0; + testing_env!(context.clone()); + assert_eq!( + contract.get_balance(carol()).0, + deposit_amount.clone() - transfer_amount + ); + assert_eq!(contract.get_balance(alice()).0, transfer_amount); + assert_eq!( + contract.get_allowance(carol(), bob()).0, + allowance - transfer_amount + ); + } + + #[test] + fn self_allowance_set_for_refund() { + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + let initial_balance = context.account_balance; + let initial_storage = context.storage_usage; + context.attached_deposit = STORAGE_PRICE_PER_BYTE * 1000; + testing_env!(context.clone()); + let allowance_amount = 1_000_000_000_000_000u128; + contract.inc_allowance(bob(), (allowance_amount.clone() / 2).into()); + context.storage_usage = env::storage_usage(); + context.account_balance = env::account_balance(); + assert_eq!( + context.account_balance, + initial_balance + + Balance::from(context.storage_usage - initial_storage) * STORAGE_PRICE_PER_BYTE + ); + + let initial_balance = context.account_balance; + let initial_storage = context.storage_usage; + testing_env!(context.clone()); + context.attached_deposit = 0; + testing_env!(context.clone()); + contract.dec_allowance(bob(), (allowance_amount.clone() / 2).into()); + context.storage_usage = env::storage_usage(); + context.account_balance = env::account_balance(); + assert!(context.storage_usage < initial_storage); + assert!(context.account_balance < initial_balance); + assert_eq!( + context.account_balance, + initial_balance + - Balance::from(initial_storage - context.storage_usage) * STORAGE_PRICE_PER_BYTE + ); + } + + #[test] + fn carol_escrows_to_bob_locks_and_transfers_to_alice() { + // Acting as carol + let mut context = get_context(carol()); + testing_env!(context.clone()); + let mut contract = FungibleToken::new(); + context.storage_usage = env::storage_usage(); + + context.is_view = true; + testing_env!(context.clone()); + assert_eq!(contract.get_total_supply().0, 0); + + let deposit_amount = 1_000_000_000_000_000u128; + let allowance = deposit_amount.clone() / 3; + let transfer_amount = allowance / 3; + context.is_view = false; + + context.attached_deposit = deposit_amount + (1000 * STORAGE_PRICE_PER_BYTE); + testing_env!(context.clone()); + + // get some wNear tokens + contract.deposit(deposit_amount.into()); + + contract.inc_allowance(bob(), allowance.into()); + context.storage_usage = env::storage_usage(); + context.account_balance = env::account_balance(); + + context.is_view = true; + context.attached_deposit = 0; + testing_env!(context.clone()); + assert_eq!(contract.get_allowance(carol(), bob()).0, allowance); + assert_eq!(contract.get_balance(carol()).0, deposit_amount.clone()); + + // Acting as bob now + context.is_view = false; + context.attached_deposit = STORAGE_PRICE_PER_BYTE * 1000; + context.predecessor_account_id = bob(); + testing_env!(context.clone()); + contract.transfer_from(carol(), alice(), transfer_amount.into()); + context.storage_usage = env::storage_usage(); + context.account_balance = env::account_balance(); + + context.is_view = true; + context.attached_deposit = 0; + testing_env!(context.clone()); + assert_eq!( + contract.get_balance(carol()).0, + (deposit_amount.clone() - transfer_amount) + ); + assert_eq!(contract.get_balance(alice()).0, transfer_amount); + assert_eq!( + contract.get_allowance(carol(), bob()).0, + allowance - transfer_amount + ); + } +} diff --git a/w-near-141/src/storage_manager.rs b/w-near-141/src/storage_manager.rs new file mode 100644 index 00000000..97287dde --- /dev/null +++ b/w-near-141/src/storage_manager.rs @@ -0,0 +1,94 @@ +use super::*; +use near_sdk::json_types::{ValidAccountId, U128}; +use near_sdk::serde::Serialize; + +/// Price per 1 byte of storage from mainnet config after `0.18` release and protocol version `42`. +/// It's 10 times lower than the genesis price. +const STORAGE_PRICE_PER_BYTE: Balance = 10_000_000_000_000_000_000; + +#[derive(Serialize)] +#[serde(crate = "near_sdk::serde")] +pub struct AccountStorageBalance { + total: U128, + available: U128, +} + +pub trait StorageManager { + fn storage_deposit(&mut self, account_id: Option) -> AccountStorageBalance; + + fn storage_withdraw(&mut self, amount: U128) -> AccountStorageBalance; + + fn storage_minimum_balance(&self) -> U128; + + fn storage_balance_of(&self, account_id: ValidAccountId) -> AccountStorageBalance; +} + +#[near_bindgen] +impl StorageManager for Contract { + #[payable] + fn storage_deposit(&mut self, account_id: Option) -> AccountStorageBalance { + let amount = env::attached_deposit(); + assert_eq!( + amount, + self.storage_minimum_balance().0, + "Requires attached deposit of the exact storage minimum balance" + ); + let account_id = account_id + .map(|a| a.into()) + .unwrap_or_else(|| env::predecessor_account_id()); + if self.accounts.insert(&account_id, &0).is_some() { + env::panic(b"The account is already registered"); + } + AccountStorageBalance { + total: amount.into(), + available: amount.into(), + } + } + + #[payable] + fn storage_withdraw(&mut self, amount: U128) -> AccountStorageBalance { + assert_one_yocto(); + let amount: Balance = amount.into(); + assert_eq!( + amount, + self.storage_minimum_balance().0, + "The withdrawal amount should be the exact storage minimum balance" + ); + let account_id = env::predecessor_account_id(); + if let Some(balance) = self.accounts.remove(&account_id) { + if balance > 0 { + env::panic(b"The account has positive token balance"); + } else { + Promise::new(account_id).transfer(amount + 1); + AccountStorageBalance { + total: 0.into(), + available: 0.into(), + } + } + } else { + env::panic(b"The account is not registered"); + } + } + + fn storage_minimum_balance(&self) -> U128 { + (Balance::from(self.account_storage_usage) * STORAGE_PRICE_PER_BYTE).into() + } + + fn storage_balance_of(&self, account_id: ValidAccountId) -> AccountStorageBalance { + if let Some(balance) = self.accounts.get(account_id.as_ref()) { + AccountStorageBalance { + total: self.storage_minimum_balance(), + available: if balance > 0 { + 0.into() + } else { + self.storage_minimum_balance() + }, + } + } else { + AccountStorageBalance { + total: 0.into(), + available: 0.into(), + } + } + } +} diff --git a/w-near-141/src/w_near.rs b/w-near-141/src/w_near.rs new file mode 100644 index 00000000..cbe9f83e --- /dev/null +++ b/w-near-141/src/w_near.rs @@ -0,0 +1,36 @@ +use crate::*; + +#[near_bindgen] +impl Contract { + /// Deposit NEAR to mint wNEAR tokens to the predecessor account in this contract. + /// Requirements: + /// * The predecessor account should be registered. + /// * Requires positive attached deposit. + #[payable] + pub fn near_deposit(&mut self) { + let amount = env::attached_deposit(); + assert!(amount > 0, "Requires positive attached deposit"); + let account_id = env::predecessor_account_id(); + self.internal_deposit(&account_id, amount); + self.total_supply += amount; + env::log(format!("Deposit {} NEAR to {}", amount, account_id).as_bytes()); + } + + /// Withdraws wNEAR and send NEAR back to the predecessor account. + /// Requirements: + /// * The predecessor account should be registered. + /// * `amount` must be a positive integer. + /// * The predecessor account should have at least the `amount` of wNEAR tokens. + /// * Requires attached deposit of exactly 1 yoctoNEAR. + #[payable] + pub fn near_withdraw(&mut self, amount: U128) -> Promise { + assert_one_yocto(); + let account_id = env::predecessor_account_id(); + let amount = amount.into(); + self.internal_withdraw(&account_id, amount); + self.total_supply -= amount; + env::log(format!("Withdraw {} NEAR from {}", amount, account_id).as_bytes()); + // Transferring NEAR and refunding 1 yoctoNEAR. + Promise::new(account_id).transfer(amount + 1) + } +} diff --git a/w-near-nep-21/Cargo.lock b/w-near-nep-21/Cargo.lock new file mode 100644 index 00000000..ccd7b977 --- /dev/null +++ b/w-near-nep-21/Cargo.lock @@ -0,0 +1,476 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "borsh" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f3fd65922359a7c6e791bc9b2bba1b977ea0c0b96a528ac48007f535fb4184" +dependencies = [ + "borsh-derive", +] + +[[package]] +name = "borsh-derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2d74755d937d261d5e9bdef87e0addfbc1ace0214f7776f21532d6e97325356" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9f966cb7a42c8ed83546ef481bc1d1dec888fe5f84a4737d5c2094a483e41e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df2543b56ebc2b4493e70d024ebde2cbb48d97bf7b1a16318eff30bd02669b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bs58" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "indexmap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "libc" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "near-rpc-error-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa8dbf8437a28ac40fcb85859ab0d0b8385013935b000c7a51ae79631dd74d9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "near-rpc-error-macro" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6111d713e90c7c551dee937f4a06cb9ea2672243455a4454cc7566387ba2d9" +dependencies = [ + "near-rpc-error-core", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "near-runtime-fees" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8732466a66a346685e00eed22355d10e54741dd66253ca7b89b7ffdcb17202e" +dependencies = [ + "num-rational", + "serde", +] + +[[package]] +name = "near-runtime-utils" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd4d93a20f7ac0472731fdf00726d1292c25ae96ea784edfa05d61ac77b7448" + +[[package]] +name = "near-sdk" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963610ae7ce6729de19c8fe0da230f878bf3a04de022aa2045ff7c5fa2b13095" +dependencies = [ + "base64", + "borsh", + "bs58", + "near-runtime-fees", + "near-sdk-macros", + "near-vm-logic", + "serde", + "serde_json", + "wee_alloc", +] + +[[package]] +name = "near-sdk-core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1552953310b5d7a6caaba67b0d65799c773c90fe5a06443fc55b0ace77e96eb5" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "near-sdk-macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c36b52b39323ee871f222243e20c7bbeb3dfe677d397baa32853e91229cd88bd" +dependencies = [ + "near-sdk-core", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "near-vm-errors" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f19df432b87ae827e1b9dbe557c51cfee24fbc992e72dc5b23d61bbe44c43133" +dependencies = [ + "borsh", + "near-rpc-error-macro", + "serde", +] + +[[package]] +name = "near-vm-logic" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3700022c10e0a6cceb6c380952a4e94a582b83da91392eb37ed15c8a20e06304" +dependencies = [ + "base64", + "bs58", + "byteorder", + "near-runtime-fees", + "near-runtime-utils", + "near-vm-errors", + "serde", + "sha2", + "sha3", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" +dependencies = [ + "block-buffer", + "byte-tools", + "digest", + "keccak", + "opaque-debug", +] + +[[package]] +name = "syn" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8833e20724c24de12bbaba5ad230ea61c3eafb05b881c7c9d3cfe8638b187e68" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "w_near" +version = "0.1.0" +dependencies = [ + "near-sdk", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/w-near-nep-21/Cargo.toml b/w-near-nep-21/Cargo.toml new file mode 100644 index 00000000..e4cf1c1c --- /dev/null +++ b/w-near-nep-21/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "w_near" +version = "0.1.0" +authors = ["Near Inc ", "Vincent "] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +near-sdk = "2.0.0" + +[profile.release] +codegen-units=1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +overflow-checks = true diff --git a/w-near/README.md b/w-near-nep-21/README.md similarity index 100% rename from w-near/README.md rename to w-near-nep-21/README.md diff --git a/w-near/build.sh b/w-near-nep-21/build.sh similarity index 100% rename from w-near/build.sh rename to w-near-nep-21/build.sh diff --git a/w-near/flags.sh b/w-near-nep-21/flags.sh similarity index 100% rename from w-near/flags.sh rename to w-near-nep-21/flags.sh diff --git a/w-near/res/w_near.wasm b/w-near-nep-21/res/w_near.wasm similarity index 100% rename from w-near/res/w_near.wasm rename to w-near-nep-21/res/w_near.wasm diff --git a/w-near/simple-build.sh b/w-near-nep-21/simple-build.sh similarity index 100% rename from w-near/simple-build.sh rename to w-near-nep-21/simple-build.sh diff --git a/w-near/src/fungible_token.rs b/w-near-nep-21/src/fungible_token.rs similarity index 100% rename from w-near/src/fungible_token.rs rename to w-near-nep-21/src/fungible_token.rs diff --git a/w-near/src/fungible_token_internal.rs b/w-near-nep-21/src/fungible_token_internal.rs similarity index 100% rename from w-near/src/fungible_token_internal.rs rename to w-near-nep-21/src/fungible_token_internal.rs diff --git a/w-near/src/lib.rs b/w-near-nep-21/src/lib.rs similarity index 100% rename from w-near/src/lib.rs rename to w-near-nep-21/src/lib.rs diff --git a/w-near/src/w_near.rs b/w-near-nep-21/src/w_near.rs similarity index 100% rename from w-near/src/w_near.rs rename to w-near-nep-21/src/w_near.rs diff --git a/w-near/src/w_near_internal.rs b/w-near-nep-21/src/w_near_internal.rs similarity index 100% rename from w-near/src/w_near_internal.rs rename to w-near-nep-21/src/w_near_internal.rs