Skip to content

Commit

Permalink
Introduce contract update via manager
Browse files Browse the repository at this point in the history
generating and adding public keys for signing

deploying contracts to named accounts

testing simple update call

added test for multisig redeploy

debugging promise call

fixed exceeded gas error, added full update test

added lockup test, migrated to new integration utils

adding lockups

added helper contract dependency

deploy and migrate to multisig wip

update with multisig integration test
  • Loading branch information
VladasZ committed Dec 19, 2023
1 parent fcecd2c commit 652fa96
Show file tree
Hide file tree
Showing 31 changed files with 1,171 additions and 184 deletions.
15 changes: 11 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ base64 = "0.21.3"
sha256 = "1.3.0"
tokio = { version = "1.28" }
uint = "0.9.5"
ed25519-dalek = { version = "2.1.0", features = ["rand_core"] }

near-workspaces = "0.9.0"

Expand All @@ -24,8 +25,14 @@ model = { path = "model" }
near-sdk = { git = "https://github.com/sweatco/near-sdk-rs", rev = "8c48b26cc48d969c1e5f3162141fe9c824fccecd" }
near-contract-standards = { git = "https://github.com/sweatco/near-sdk-rs", rev = "8c48b26cc48d969c1e5f3162141fe9c824fccecd" }

integration-trait = { git = "https://github.com/sweatco/integration-trait.git", rev = "8dd8b63cef5e60448629a8903ea43b642bbf9f45" }
integration-utils = { git = "https://github.com/sweatco/integration-utils.git", rev = "0b3c2faf0db31cdb3481be4d35dbb48b62a98618" }
integration-trait = { git = "https://github.com/sweatco/integration-utils.git", rev = "9a455faf70702e285eea39ae69a73a4d123b523f" }
integration-utils = { git = "https://github.com/sweatco/integration-utils.git", rev = "9a455faf70702e285eea39ae69a73a4d123b523f" }
helper-contract = { git = "https://github.com/sweatco/integration-utils.git", rev = "9a455faf70702e285eea39ae69a73a4d123b523f" }

sweat-model = { git = "https://github.com/sweatco/sweat-near", rev = "f8709a20edeb06c8196306f5fe6025cae21a854e" }
sweat-integration = { git = "https://github.com/sweatco/sweat-near", rev = "f8709a20edeb06c8196306f5fe6025cae21a854e" }
sweat-model = { git = "https://github.com/sweatco/sweat-near", rev = "0ac66c4bd2f776130a0c98bc559d628c8bdf3fc9" }
sweat-integration = { git = "https://github.com/sweatco/sweat-near", rev = "0ac66c4bd2f776130a0c98bc559d628c8bdf3fc9" }

multisig-model = { git = "https://github.com/sweatco/multisig", rev = "15f1ef7a6278a4d6ec8f1ac3c4b2ec8a07753b5c" }
multisig-integration = { git = "https://github.com/sweatco/multisig", rev = "15f1ef7a6278a4d6ec8f1ac3c4b2ec8a07753b5c" }

near-self-update = { git = "https://github.com/sweatco/near-self-update.git", rev = "7064db3cdd924efc7fa7c00664920a2b482e7bcf" }
3 changes: 2 additions & 1 deletion contract/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hodl-lockup"
version = "1.0.0"
version = "1.1.0"
authors = ["Sweat Economy"]
edition = "2021"

Expand All @@ -14,6 +14,7 @@ integration-test = []
[dependencies]

near-sdk = { workspace = true }
near-self-update = { workspace = true }
near-contract-standards = { workspace = true }

model = { workspace = true }
Expand Down
33 changes: 8 additions & 25 deletions contract/src/ft_token_receiver.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,10 @@
use model::{draft::DraftGroupIndex, lockup::LockupCreate};
use model::ft_message::FtMessage;

use crate::{
emit, env, log, near_bindgen, serde_json, AccountId, Contract, ContractExt, Deserialize, EventKind,
FtLockupCreateLockup, FtLockupFundDraftGroup, FungibleTokenReceiver, PromiseOrValue, Serialize, GAS_EXT_CALL_COST,
GAS_MIN_FOR_CONVERT, U128,
emit, env, log, near_bindgen, serde_json, AccountId, Contract, ContractExt, EventKind, FtLockupCreateLockup,
FtLockupFundDraftGroup, FungibleTokenReceiver, PromiseOrValue, GAS_EXT_CALL_COST, GAS_MIN_FOR_CONVERT, U128,
};

#[derive(Serialize, Deserialize)]
#[serde(crate = "near_sdk::serde")]
pub struct DraftGroupFunding {
pub draft_group_id: DraftGroupIndex,
// use remaining gas to try converting drafts
pub try_convert: Option<bool>,
}

#[derive(Serialize, Deserialize)]
#[serde(crate = "near_sdk::serde")]
#[serde(untagged)]
pub enum FtMessage {
LockupCreate(LockupCreate),
DraftGroupFunding(DraftGroupFunding),
}

#[near_bindgen]
impl FungibleTokenReceiver for Contract {
fn ft_on_transfer(&mut self, sender_id: AccountId, amount: U128, msg: String) -> PromiseOrValue<U128> {
Expand All @@ -30,6 +13,7 @@ impl FungibleTokenReceiver for Contract {
self.assert_deposit_whitelist(&sender_id);

let ft_message: FtMessage = serde_json::from_str(&msg).unwrap();

match ft_message {
FtMessage::LockupCreate(lockup_create) => {
let lockup = lockup_create.into_lockup(&sender_id);
Expand Down Expand Up @@ -70,11 +54,10 @@ impl FungibleTokenReceiver for Contract {
}
}
}
let event =
FtLockupFundDraftGroup {
id: draft_group_id,
amount: amount.into(),
};
let event = FtLockupFundDraftGroup {
id: draft_group_id,
amount: amount.into(),
};
emit(EventKind::FtLockupFundDraftGroup(vec![event]));
}
}
Expand Down
34 changes: 21 additions & 13 deletions contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,17 @@ use near_sdk::{
env, ext_contract, is_promise_success,
json_types::{Base58CryptoHash, U128},
log, near_bindgen,
serde::{Deserialize, Serialize},
serde::Serialize,
serde_json, AccountId, BorshStorageKey, Gas, PanicOnDefault, Promise, PromiseOrValue,
};
use near_self_update::SelfUpdate;

pub mod callbacks;
pub mod event;
pub mod ft_token_receiver;
pub mod internal;

mod migration;
pub mod view;

use crate::{
Expand All @@ -52,7 +54,7 @@ const GAS_EXT_CALL_COST: Gas = Gas(10_000_000_000_000);
const GAS_MIN_FOR_CONVERT: Gas = Gas(15_000_000_000_000);

#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault, SelfUpdate)]
pub struct Contract {
pub token_account_id: TokenAccountId,

Expand All @@ -74,6 +76,9 @@ pub struct Contract {
pub drafts: LookupMap<DraftIndex, Draft>,
pub next_draft_group_id: DraftGroupIndex,
pub draft_groups: UnorderedMap<DraftGroupIndex, DraftGroup>,

/// The account ID authorized to perform sensitive operations on the contract.
pub manager: AccountId,
}

#[derive(BorshStorageKey, BorshSerialize)]
Expand All @@ -93,6 +98,7 @@ impl LockupApi for Contract {
token_account_id: AccountId,
deposit_whitelist: Vec<AccountId>,
draft_operators_whitelist: Option<Vec<AccountId>>,
manager: AccountId,
) -> Self {
let mut deposit_whitelist_set = UnorderedSet::new(StorageKey::DepositWhitelist);
deposit_whitelist_set.extend(deposit_whitelist.clone().into_iter().map(Into::into));
Expand Down Expand Up @@ -131,6 +137,7 @@ impl LockupApi for Contract {
drafts: LookupMap::new(StorageKey::Drafts),
next_draft_group_id: 0,
draft_groups: UnorderedMap::new(StorageKey::DraftGroups),
manager,
}
}

Expand Down Expand Up @@ -161,16 +168,15 @@ impl LockupApi for Contract {
} else {
let lockups_by_id: HashMap<LockupIndex, Lockup> =
self.internal_get_account_lockups(&account_id).into_iter().collect();
let amounts: HashMap<LockupIndex, WrappedBalance> =
lockups_by_id
.iter()
.map(|(lockup_id, lockup)| {
let unlocked_balance = lockup.schedule.unlocked_balance(current_timestamp_sec());
let amount: WrappedBalance = (unlocked_balance - lockup.claimed_balance).into();

(*lockup_id, amount)
})
.collect();
let amounts: HashMap<LockupIndex, WrappedBalance> = lockups_by_id
.iter()
.map(|(lockup_id, lockup)| {
let unlocked_balance = lockup.schedule.unlocked_balance(current_timestamp_sec());
let amount: WrappedBalance = (unlocked_balance - lockup.claimed_balance).into();

(*lockup_id, amount)
})
.collect();
(amounts, lockups_by_id)
};

Expand Down Expand Up @@ -412,7 +418,9 @@ impl LockupApi for Contract {
self.draft_groups.insert(&draft_group_id as _, &draft_group);
}

emit(EventKind::FtLockupDiscardDraftGroup(vec![FtLockupDiscardDraftGroup { id: draft_group_id }]));
emit(EventKind::FtLockupDiscardDraftGroup(vec![FtLockupDiscardDraftGroup {
id: draft_group_id,
}]));
}

fn delete_drafts(&mut self, draft_ids: Vec<DraftIndex>) {
Expand Down
28 changes: 28 additions & 0 deletions contract/src/migration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use model::migration::OldState;
use near_sdk::{collections::UnorderedSet, env, env::log_str, near_bindgen, AccountId};

use crate::{Contract, ContractExt, StorageKey};

#[near_bindgen]
impl Contract {
#[private]
#[init(ignore_state)]
pub fn migrate(manager: AccountId) -> Self {
log_str("Migrate");

let old_state: OldState = env::state_read().expect("Failed to read old state");

Contract {
token_account_id: old_state.token_account_id,
lockups: old_state.lockups,
account_lockups: old_state.account_lockups,
deposit_whitelist: old_state.deposit_whitelist,
draft_operators_whitelist: UnorderedSet::new(StorageKey::DraftOperatorsWhitelist),
next_draft_id: old_state.next_draft_id,
drafts: old_state.drafts,
next_draft_group_id: old_state.next_draft_group_id,
draft_groups: old_state.draft_groups,
manager,
}
}
}
7 changes: 7 additions & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@ edition = "2021"
name = "integration_tests"

[dependencies]
base58 = "0.2.0"

rand = { workspace = true }
tokio = { workspace = true }
anyhow = { workspace = true }
async-trait = { workspace = true }
ed25519-dalek = { workspace = true }

near-sdk = { workspace = true }
near-workspaces = { workspace = true }

model = { workspace = true }
integration-utils = { workspace = true }
helper-contract = { workspace = true }

sweat-model = { workspace = true }
sweat-integration = { workspace = true }

multisig-model = { workspace = true }
multisig-integration = { workspace = true }
103 changes: 92 additions & 11 deletions integration-tests/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,123 @@

use anyhow::Result;
use async_trait::async_trait;
use integration_utils::integration_contract::IntegrationContract;
use near_workspaces::Account;
use helper_contract::{
api::HelperApiIntegration,
interface::{HelperContract, HELPER_CONTRACT},
};
use integration_utils::{integration_contract::IntegrationContract, misc::ToNear};
use model::lockup_api::LockupApiIntegration;
use multisig_integration::{Multisig, MULTISIG};
use multisig_model::api::MultisigApiIntegration;
use near_workspaces::{types::NearToken, Account};
use sweat_integration::{SweatFt, FT_CONTRACT};
use sweat_model::{StorageManagementIntegration, SweatApiIntegration};

use crate::lockup_interface::{LockupContract, LOCKUP_CONTRACT};
use crate::lockup_interface::{GetContractAccount, LockupContract};

pub const LOCKUP_CONTRACT: &str = "ft_lockup_original";

pub type Context = integration_utils::context::Context<near_workspaces::network::Sandbox>;

#[async_trait]
pub trait IntegrationContext {
async fn manager(&mut self) -> Result<Account>;
async fn bob(&mut self) -> Result<Account>;
async fn alice(&mut self) -> Result<Account>;
async fn fee(&mut self) -> Result<Account>;
fn lockup(&self) -> LockupContract<'_>;
fn multisig(&self) -> Multisig<'_>;
fn ft_contract(&self) -> SweatFt<'_>;
fn helper(&self) -> HelperContract<'_>;
}

#[async_trait]
impl IntegrationContext for Context {
async fn manager(&mut self) -> Result<Account> {
self.account("manager").await
let name = "manager";

if !self.accounts.contains_key(name) {
let root_account = self.worker.dev_create_account().await?;

let account = root_account
.create_subaccount(name)
.initial_balance(NearToken::from_near(50))
.transact()
.await?
.into_result()?;

self.accounts.insert(name.to_string(), account);
}

Ok(self.accounts.get(name).unwrap().clone())
}

async fn alice(&mut self) -> Result<Account> {
self.account("alice").await
async fn bob(&mut self) -> Result<Account> {
self.account("bob").await
}

async fn fee(&mut self) -> Result<Account> {
self.account("fee").await
async fn alice(&mut self) -> Result<Account> {
self.account("alice").await
}

fn lockup(&self) -> LockupContract<'_> {
LockupContract::with_contract(&self.contracts[LOCKUP_CONTRACT])
}

fn multisig(&self) -> Multisig<'_> {
Multisig::with_contract(&self.contracts[MULTISIG])
}

fn ft_contract(&self) -> SweatFt<'_> {
SweatFt::with_contract(&self.contracts[FT_CONTRACT])
}

fn helper(&self) -> HelperContract<'_> {
HelperContract::new(&self.contracts[HELPER_CONTRACT])
}
}

pub(crate) async fn prepare_contract() -> Result<Context> {
let context = Context::new(&[LOCKUP_CONTRACT], "build-integration".into()).await?;
//context.lockup().new().await?;
let mut context = Context::new(
&[LOCKUP_CONTRACT, MULTISIG, FT_CONTRACT, HELPER_CONTRACT],
"build-integration".into(),
)
.await?;

let manager = context.manager().await?;

context
.ft_contract()
.new(".u.sweat.testnet".to_string().into())
.call()
.await?;
context.ft_contract().add_oracle(&manager.to_near()).call().await?;

context.multisig().new(0).call().await?;

context
.ft_contract()
.tge_mint(&manager.to_near(), NearToken::from_near(100).as_near().into())
.call()
.await?;

context
.ft_contract()
.storage_deposit(context.lockup().contract_account().into(), None)
.call()
.await?;

context
.lockup()
.new(
context.ft_contract().contract_account(),
vec![manager.to_near()],
Some(vec![manager.to_near()]),
manager.to_near(),
)
.call()
.await?;

context.helper().new().result().await?;

Ok(context)
}
12 changes: 0 additions & 12 deletions integration-tests/src/happy_flow.rs

This file was deleted.

Loading

0 comments on commit 652fa96

Please sign in to comment.