Skip to content

Token farm #6

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

Open
wants to merge 4 commits into
base: main
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
9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "contract"
name = "token-factory"
description = "Factory Contract Example"
version = "0.1.0"
version = "1.0.0"
edition = "2021"
# TODO: Fill out the repository field to help NEAR ecosystem tools to discover your project.
# NEP-0330 is automatically implemented for all contracts built with https://github.com/near/cargo-near.
Expand All @@ -13,10 +13,11 @@ crate-type = ["cdylib", "rlib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
near-sdk = { version = "5.1.0", features = ["unstable"] }
near-sdk = { version = "5.3.0", features = ["unstable"] }
near-contract-standards = "5.3.0"

[dev-dependencies]
near-sdk = { version = "5.1.0", features = ["unit-testing"] }
near-sdk = { version = "5.3.0", features = ["unit-testing"] }
near-workspaces = { version = "0.10.0", features = ["unstable"] }
tokio = { version = "1.12.0", features = ["full"] }
serde_json = "1"
Expand Down
133 changes: 74 additions & 59 deletions src/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,87 +1,102 @@
use near_sdk::serde::Serialize;
use near_sdk::{env, log, near, AccountId, NearToken, Promise, PromiseError, PublicKey};
use near_contract_standards::fungible_token::metadata::FungibleTokenMetadata;
use near_sdk::{borsh, env, json_types::U128, log, near, require, serde_json::json, AccountId, NearToken, Promise, PromiseError};

use crate::{Contract, ContractExt, NEAR_PER_STORAGE, NO_DEPOSIT, TGAS};
use crate::{Contract, ContractExt, FT_CONTRACT, NO_DEPOSIT, TGAS};

#[derive(Serialize)]
#[serde(crate = "near_sdk::serde")]
struct DonationInitArgs {
beneficiary: AccountId,
type TokenId = String;

const EXTRA_BYTES: usize = 10000;

#[near(serializers = [json, borsh])]
pub struct TokenArgs {
owner_id: AccountId,
total_supply: U128,
metadata: FungibleTokenMetadata,
}

pub fn is_valid_token_id(token_id: &TokenId) -> bool {
for c in token_id.as_bytes() {
match c {
b'0'..=b'9' | b'a'..=b'z' => (),
_ => return false,
}
}
true
}

#[near]
impl Contract {
pub fn get_required(&self, args: &TokenArgs) -> NearToken {
env::storage_byte_cost().saturating_mul(
(FT_CONTRACT.len() + EXTRA_BYTES + borsh::to_vec(args).unwrap().len())
.try_into()
.unwrap(),
)
}

#[payable]
pub fn create_factory_subaccount_and_deploy(
&mut self,
name: String,
beneficiary: AccountId,
public_key: Option<PublicKey>,
) -> Promise {
pub fn create_token(&mut self, args: TokenArgs) -> Promise {
args.metadata.assert_valid();
let token_id = args.metadata.symbol.to_ascii_lowercase();

require!(is_valid_token_id(&token_id), "Invalid Symbol");

// Assert the sub-account is valid
let current_account = env::current_account_id().to_string();
let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap();
assert!(
env::is_valid_account_id(subaccount.as_bytes()),
"Invalid subaccount"
let token_account_id = format!("{}.{}", token_id, env::current_account_id());
require!(
env::is_valid_account_id(token_account_id.as_bytes()),
"Token Account ID is invalid"
);

// Assert enough tokens are attached to create the account and deploy the contract
let attached = env::attached_deposit();
let required = self.get_required(&args);

let code = self.code.clone().unwrap();
let contract_bytes = code.len() as u128;
let minimum_needed = NEAR_PER_STORAGE.saturating_mul(contract_bytes);
assert!(
attached >= minimum_needed,
"Attach at least {minimum_needed} yⓃ"
require!(
attached >= required,
format!("Attach at least {required} yⓃ")
);

let init_args = near_sdk::serde_json::to_vec(&DonationInitArgs { beneficiary }).unwrap();
let init_args = near_sdk::serde_json::to_vec(&args).unwrap();

let user = env::predecessor_account_id();
let callback_args = json!({ "user": user, "deposit": attached })
.to_string()
.into_bytes()
.to_vec();

let mut promise = Promise::new(subaccount.clone())
Promise::new(token_account_id.parse().unwrap())
.create_account()
.transfer(attached)
.deploy_contract(code)
.deploy_contract(FT_CONTRACT.to_vec())
.function_call(
"init".to_owned(),
"new".to_owned(),
init_args,
NO_DEPOSIT,
TGAS.saturating_mul(5),
);

// Add full access key is the user passes one
if let Some(pk) = public_key {
promise = promise.add_full_access_key(pk);
}

// Add callback
promise.then(
Self::ext(env::current_account_id()).create_factory_subaccount_and_deploy_callback(
subaccount,
env::predecessor_account_id(),
attached,
),
)
TGAS.saturating_mul(50),
)
.then(Promise::new(env::current_account_id()).function_call(
"create_callback".to_string(),
callback_args,
NearToken::from_near(0),
TGAS.saturating_mul(30),
))
}

#[private]
pub fn create_factory_subaccount_and_deploy_callback(
&mut self,
account: AccountId,
pub fn create_callback(
&self,
user: AccountId,
attached: NearToken,
#[callback_result] create_deploy_result: Result<(), PromiseError>,
deposit: NearToken,
#[callback_result] call_result: Result<(), PromiseError>,
) -> bool {
if let Ok(_result) = create_deploy_result {
log!(format!("Correctly created and deployed to {account}"));
return true;
};

log!(format!(
"Error creating {account}, returning {attached}yⓃ to {user}"
));
Promise::new(user).transfer(attached);
false
match call_result {
Ok(_) => true,
Err(e) => {
log!("Error creating token: {:?}", e);
Promise::new(user).transfer(deposit);
false
}
}
}
}
Binary file removed src/donation-contract/donation.wasm
Binary file not shown.
Binary file added src/ft-contract/ft.wasm
Binary file not shown.
18 changes: 3 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
// Find all our documentation at https://docs.near.org
use near_sdk::store::LazyOption;
use near_sdk::{near, Gas, NearToken};

mod deploy;
mod manager;

const NEAR_PER_STORAGE: NearToken = NearToken::from_yoctonear(10u128.pow(18)); // 10e18yⓃ
const DEFAULT_CONTRACT: &[u8] = include_bytes!("./donation-contract/donation.wasm");
const FT_CONTRACT: &[u8] = include_bytes!("./ft-contract/ft.wasm");
const TGAS: Gas = Gas::from_tgas(1); // 10e12yⓃ
const NO_DEPOSIT: NearToken = NearToken::from_near(0); // 0yⓃ

// Define the contract structure
#[near(contract_state)]
pub struct Contract {
// Since a contract is something big to store, we use LazyOptions
// this way it is not deserialized on each method call
code: LazyOption<Vec<u8>>,
// Please note that it is much more efficient to **not** store this
// code in the state, and directly use `DEFAULT_CONTRACT`
// However, this does not enable to update the stored code.
}
pub struct Contract {}

// Define the default, which automatically initializes the contract
impl Default for Contract {
fn default() -> Self {
Self {
code: LazyOption::new("code".as_bytes(), Some(DEFAULT_CONTRACT.to_vec())),
}
Self {}
}
}
19 changes: 0 additions & 19 deletions src/manager.rs

This file was deleted.

Loading
Loading