Skip to content

Commit

Permalink
Merge pull request #14 from near-examples/add-factory
Browse files Browse the repository at this point in the history
Add factory to deploy auctions
  • Loading branch information
garikbesson authored Sep 17, 2024
2 parents 264bbd2 + 500834a commit bc1a27b
Show file tree
Hide file tree
Showing 9 changed files with 397 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/tests-factory.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Tests Factory
on: push
jobs:
workflows:
strategy:
matrix:
platform: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Install and test modules
run: |
cd ./factory
cargo test
33 changes: 33 additions & 0 deletions factory/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "contract"
description = "Factory Contract Example"
version = "0.1.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.
# Link to the repository will be available via `contract_source_metadata` view-function.
#repository = "https://github.com/xxx/xxx"

[lib]
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.5.0", features = ["unstable"] }

[dev-dependencies]
near-sdk = { version = "5.5.0", features = ["unit-testing"] }
near-workspaces = { version = "0.14.0", features = ["unstable"] }
tokio = { version = "1.12.0", features = ["full"] }
serde_json = "1"
chrono = "0.4.38"

[profile.release]
codegen-units = 1
# Tell `rustc` to optimize for small code size.
opt-level = "z"
lto = true
debug = false
panic = "abort"
# Opt into extra safety checks on arithmetic operations https://stackoverflow.com/a/64136471/249801
overflow-checks = true
4 changes: 4 additions & 0 deletions factory/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["wasm32-unknown-unknown"]
Binary file added factory/src/auction-contract/auction.wasm
Binary file not shown.
107 changes: 107 additions & 0 deletions factory/src/deploy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use near_sdk::json_types::{U128, U64};
use near_sdk::serde::Serialize;
use near_sdk::{env, log, near, AccountId, NearToken, Promise, PromiseError};

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

pub type TokenId = String;

#[derive(Serialize)]
#[serde(crate = "near_sdk::serde")]
struct AuctionInitArgs {
end_time: U64,
auctioneer: AccountId,
ft_contract: AccountId,
nft_contract: AccountId,
token_id: TokenId,
starting_price: U128,
}

#[near]
impl Contract {
#[payable]
pub fn deploy_new_auction(
&mut self,
name: String,
end_time: U64,
auctioneer: AccountId,
ft_contract: AccountId,
nft_contract: AccountId,
token_id: TokenId,
starting_price: U128,
) -> Promise {
// 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"
);

// Assert enough tokens are attached to create the account and deploy the contract
let attached = env::attached_deposit();

let code = self.code.clone().unwrap();
let contract_bytes = code.len() as u128;
let contract_storage_cost = NEAR_PER_STORAGE.saturating_mul(contract_bytes);
let minimum_needed = contract_storage_cost.saturating_add(NearToken::from_millinear(100));
assert!(
attached >= minimum_needed,
"Attach at least {minimum_needed} yⓃ"
);

let args = &AuctionInitArgs {
end_time,
auctioneer,
ft_contract,
nft_contract,
token_id,
starting_price,
};

let init_args = near_sdk::serde_json::to_vec(args).unwrap();

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

// Add callback
promise.then(
Self::ext(env::current_account_id()).deploy_new_auction_callback(
subaccount,
env::predecessor_account_id(),
attached,
),
)
}

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

log!(
"Error creating {}, returning {}yⓃ to {}",
account,
attached,
user
);
Promise::new(user).transfer(attached);
false
}
}
31 changes: 31 additions & 0 deletions factory/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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(19)); // 10e19yⓃ
const AUCTION_CONTRACT: &[u8] = include_bytes!("./auction-contract/auction.wasm");
const TGAS: Gas = Gas::from_tgas(1);
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 `AUCTION_CONTRACT`
// However, this does not enable to update the stored code.
}

// Define the default, which automatically initializes the contract
impl Default for Contract {
fn default() -> Self {
Self {
code: LazyOption::new("code".as_bytes(), Some(AUCTION_CONTRACT.to_vec())),
}
}
}
19 changes: 19 additions & 0 deletions factory/src/manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use near_sdk::{env, near};

use crate::{Contract, ContractExt};

#[near]
impl Contract {
#[private]
pub fn update_auction_contract(&mut self) {
// This method receives the code to be stored in the contract directly
// from the contract's input. In this way, it avoids the overhead of
// deserializing parameters, which would consume a huge amount of GAS
self.code.set(env::input());
}

pub fn get_code(&self) -> &Vec<u8> {
// If a contract wants to update themselves, they can ask for the code needed
self.code.get().as_ref().unwrap()
}
}
Binary file added factory/tests/fungible_token.wasm
Binary file not shown.
Loading

0 comments on commit bc1a27b

Please sign in to comment.