Skip to content

Commit 652fa96

Browse files
committed
Introduce contract update via manager
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
1 parent fcecd2c commit 652fa96

31 files changed

Lines changed: 1171 additions & 184 deletions

Cargo.toml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ base64 = "0.21.3"
1616
sha256 = "1.3.0"
1717
tokio = { version = "1.28" }
1818
uint = "0.9.5"
19+
ed25519-dalek = { version = "2.1.0", features = ["rand_core"] }
1920

2021
near-workspaces = "0.9.0"
2122

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

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

30-
sweat-model = { git = "https://github.com/sweatco/sweat-near", rev = "f8709a20edeb06c8196306f5fe6025cae21a854e" }
31-
sweat-integration = { git = "https://github.com/sweatco/sweat-near", rev = "f8709a20edeb06c8196306f5fe6025cae21a854e" }
32+
sweat-model = { git = "https://github.com/sweatco/sweat-near", rev = "0ac66c4bd2f776130a0c98bc559d628c8bdf3fc9" }
33+
sweat-integration = { git = "https://github.com/sweatco/sweat-near", rev = "0ac66c4bd2f776130a0c98bc559d628c8bdf3fc9" }
34+
35+
multisig-model = { git = "https://github.com/sweatco/multisig", rev = "15f1ef7a6278a4d6ec8f1ac3c4b2ec8a07753b5c" }
36+
multisig-integration = { git = "https://github.com/sweatco/multisig", rev = "15f1ef7a6278a4d6ec8f1ac3c4b2ec8a07753b5c" }
37+
38+
near-self-update = { git = "https://github.com/sweatco/near-self-update.git", rev = "7064db3cdd924efc7fa7c00664920a2b482e7bcf" }

contract/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hodl-lockup"
3-
version = "1.0.0"
3+
version = "1.1.0"
44
authors = ["Sweat Economy"]
55
edition = "2021"
66

@@ -14,6 +14,7 @@ integration-test = []
1414
[dependencies]
1515

1616
near-sdk = { workspace = true }
17+
near-self-update = { workspace = true }
1718
near-contract-standards = { workspace = true }
1819

1920
model = { workspace = true }

contract/src/ft_token_receiver.rs

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,10 @@
1-
use model::{draft::DraftGroupIndex, lockup::LockupCreate};
1+
use model::ft_message::FtMessage;
22

33
use crate::{
4-
emit, env, log, near_bindgen, serde_json, AccountId, Contract, ContractExt, Deserialize, EventKind,
5-
FtLockupCreateLockup, FtLockupFundDraftGroup, FungibleTokenReceiver, PromiseOrValue, Serialize, GAS_EXT_CALL_COST,
6-
GAS_MIN_FOR_CONVERT, U128,
4+
emit, env, log, near_bindgen, serde_json, AccountId, Contract, ContractExt, EventKind, FtLockupCreateLockup,
5+
FtLockupFundDraftGroup, FungibleTokenReceiver, PromiseOrValue, GAS_EXT_CALL_COST, GAS_MIN_FOR_CONVERT, U128,
76
};
87

9-
#[derive(Serialize, Deserialize)]
10-
#[serde(crate = "near_sdk::serde")]
11-
pub struct DraftGroupFunding {
12-
pub draft_group_id: DraftGroupIndex,
13-
// use remaining gas to try converting drafts
14-
pub try_convert: Option<bool>,
15-
}
16-
17-
#[derive(Serialize, Deserialize)]
18-
#[serde(crate = "near_sdk::serde")]
19-
#[serde(untagged)]
20-
pub enum FtMessage {
21-
LockupCreate(LockupCreate),
22-
DraftGroupFunding(DraftGroupFunding),
23-
}
24-
258
#[near_bindgen]
269
impl FungibleTokenReceiver for Contract {
2710
fn ft_on_transfer(&mut self, sender_id: AccountId, amount: U128, msg: String) -> PromiseOrValue<U128> {
@@ -30,6 +13,7 @@ impl FungibleTokenReceiver for Contract {
3013
self.assert_deposit_whitelist(&sender_id);
3114

3215
let ft_message: FtMessage = serde_json::from_str(&msg).unwrap();
16+
3317
match ft_message {
3418
FtMessage::LockupCreate(lockup_create) => {
3519
let lockup = lockup_create.into_lockup(&sender_id);
@@ -70,11 +54,10 @@ impl FungibleTokenReceiver for Contract {
7054
}
7155
}
7256
}
73-
let event =
74-
FtLockupFundDraftGroup {
75-
id: draft_group_id,
76-
amount: amount.into(),
77-
};
57+
let event = FtLockupFundDraftGroup {
58+
id: draft_group_id,
59+
amount: amount.into(),
60+
};
7861
emit(EventKind::FtLockupFundDraftGroup(vec![event]));
7962
}
8063
}

contract/src/lib.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,17 @@ use near_sdk::{
2121
env, ext_contract, is_promise_success,
2222
json_types::{Base58CryptoHash, U128},
2323
log, near_bindgen,
24-
serde::{Deserialize, Serialize},
24+
serde::Serialize,
2525
serde_json, AccountId, BorshStorageKey, Gas, PanicOnDefault, Promise, PromiseOrValue,
2626
};
27+
use near_self_update::SelfUpdate;
2728

2829
pub mod callbacks;
2930
pub mod event;
3031
pub mod ft_token_receiver;
3132
pub mod internal;
3233

34+
mod migration;
3335
pub mod view;
3436

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

5456
#[near_bindgen]
55-
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
57+
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault, SelfUpdate)]
5658
pub struct Contract {
5759
pub token_account_id: TokenAccountId,
5860

@@ -74,6 +76,9 @@ pub struct Contract {
7476
pub drafts: LookupMap<DraftIndex, Draft>,
7577
pub next_draft_group_id: DraftGroupIndex,
7678
pub draft_groups: UnorderedMap<DraftGroupIndex, DraftGroup>,
79+
80+
/// The account ID authorized to perform sensitive operations on the contract.
81+
pub manager: AccountId,
7782
}
7883

7984
#[derive(BorshStorageKey, BorshSerialize)]
@@ -93,6 +98,7 @@ impl LockupApi for Contract {
9398
token_account_id: AccountId,
9499
deposit_whitelist: Vec<AccountId>,
95100
draft_operators_whitelist: Option<Vec<AccountId>>,
101+
manager: AccountId,
96102
) -> Self {
97103
let mut deposit_whitelist_set = UnorderedSet::new(StorageKey::DepositWhitelist);
98104
deposit_whitelist_set.extend(deposit_whitelist.clone().into_iter().map(Into::into));
@@ -131,6 +137,7 @@ impl LockupApi for Contract {
131137
drafts: LookupMap::new(StorageKey::Drafts),
132138
next_draft_group_id: 0,
133139
draft_groups: UnorderedMap::new(StorageKey::DraftGroups),
140+
manager,
134141
}
135142
}
136143

@@ -161,16 +168,15 @@ impl LockupApi for Contract {
161168
} else {
162169
let lockups_by_id: HashMap<LockupIndex, Lockup> =
163170
self.internal_get_account_lockups(&account_id).into_iter().collect();
164-
let amounts: HashMap<LockupIndex, WrappedBalance> =
165-
lockups_by_id
166-
.iter()
167-
.map(|(lockup_id, lockup)| {
168-
let unlocked_balance = lockup.schedule.unlocked_balance(current_timestamp_sec());
169-
let amount: WrappedBalance = (unlocked_balance - lockup.claimed_balance).into();
170-
171-
(*lockup_id, amount)
172-
})
173-
.collect();
171+
let amounts: HashMap<LockupIndex, WrappedBalance> = lockups_by_id
172+
.iter()
173+
.map(|(lockup_id, lockup)| {
174+
let unlocked_balance = lockup.schedule.unlocked_balance(current_timestamp_sec());
175+
let amount: WrappedBalance = (unlocked_balance - lockup.claimed_balance).into();
176+
177+
(*lockup_id, amount)
178+
})
179+
.collect();
174180
(amounts, lockups_by_id)
175181
};
176182

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

415-
emit(EventKind::FtLockupDiscardDraftGroup(vec![FtLockupDiscardDraftGroup { id: draft_group_id }]));
421+
emit(EventKind::FtLockupDiscardDraftGroup(vec![FtLockupDiscardDraftGroup {
422+
id: draft_group_id,
423+
}]));
416424
}
417425

418426
fn delete_drafts(&mut self, draft_ids: Vec<DraftIndex>) {

contract/src/migration.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use model::migration::OldState;
2+
use near_sdk::{collections::UnorderedSet, env, env::log_str, near_bindgen, AccountId};
3+
4+
use crate::{Contract, ContractExt, StorageKey};
5+
6+
#[near_bindgen]
7+
impl Contract {
8+
#[private]
9+
#[init(ignore_state)]
10+
pub fn migrate(manager: AccountId) -> Self {
11+
log_str("Migrate");
12+
13+
let old_state: OldState = env::state_read().expect("Failed to read old state");
14+
15+
Contract {
16+
token_account_id: old_state.token_account_id,
17+
lockups: old_state.lockups,
18+
account_lockups: old_state.account_lockups,
19+
deposit_whitelist: old_state.deposit_whitelist,
20+
draft_operators_whitelist: UnorderedSet::new(StorageKey::DraftOperatorsWhitelist),
21+
next_draft_id: old_state.next_draft_id,
22+
drafts: old_state.drafts,
23+
next_draft_group_id: old_state.next_draft_group_id,
24+
draft_groups: old_state.draft_groups,
25+
manager,
26+
}
27+
}
28+
}

integration-tests/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,23 @@ edition = "2021"
88
name = "integration_tests"
99

1010
[dependencies]
11+
base58 = "0.2.0"
12+
13+
rand = { workspace = true }
1114
tokio = { workspace = true }
1215
anyhow = { workspace = true }
1316
async-trait = { workspace = true }
17+
ed25519-dalek = { workspace = true }
1418

1519
near-sdk = { workspace = true }
1620
near-workspaces = { workspace = true }
1721

1822
model = { workspace = true }
1923
integration-utils = { workspace = true }
24+
helper-contract = { workspace = true }
2025

2126
sweat-model = { workspace = true }
2227
sweat-integration = { workspace = true }
2328

29+
multisig-model = { workspace = true }
30+
multisig-integration = { workspace = true }

integration-tests/src/context.rs

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,123 @@
22

33
use anyhow::Result;
44
use async_trait::async_trait;
5-
use integration_utils::integration_contract::IntegrationContract;
6-
use near_workspaces::Account;
5+
use helper_contract::{
6+
api::HelperApiIntegration,
7+
interface::{HelperContract, HELPER_CONTRACT},
8+
};
9+
use integration_utils::{integration_contract::IntegrationContract, misc::ToNear};
10+
use model::lockup_api::LockupApiIntegration;
11+
use multisig_integration::{Multisig, MULTISIG};
12+
use multisig_model::api::MultisigApiIntegration;
13+
use near_workspaces::{types::NearToken, Account};
14+
use sweat_integration::{SweatFt, FT_CONTRACT};
15+
use sweat_model::{StorageManagementIntegration, SweatApiIntegration};
716

8-
use crate::lockup_interface::{LockupContract, LOCKUP_CONTRACT};
17+
use crate::lockup_interface::{GetContractAccount, LockupContract};
18+
19+
pub const LOCKUP_CONTRACT: &str = "ft_lockup_original";
920

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

1223
#[async_trait]
1324
pub trait IntegrationContext {
1425
async fn manager(&mut self) -> Result<Account>;
26+
async fn bob(&mut self) -> Result<Account>;
1527
async fn alice(&mut self) -> Result<Account>;
16-
async fn fee(&mut self) -> Result<Account>;
1728
fn lockup(&self) -> LockupContract<'_>;
29+
fn multisig(&self) -> Multisig<'_>;
30+
fn ft_contract(&self) -> SweatFt<'_>;
31+
fn helper(&self) -> HelperContract<'_>;
1832
}
1933

2034
#[async_trait]
2135
impl IntegrationContext for Context {
2236
async fn manager(&mut self) -> Result<Account> {
23-
self.account("manager").await
37+
let name = "manager";
38+
39+
if !self.accounts.contains_key(name) {
40+
let root_account = self.worker.dev_create_account().await?;
41+
42+
let account = root_account
43+
.create_subaccount(name)
44+
.initial_balance(NearToken::from_near(50))
45+
.transact()
46+
.await?
47+
.into_result()?;
48+
49+
self.accounts.insert(name.to_string(), account);
50+
}
51+
52+
Ok(self.accounts.get(name).unwrap().clone())
2453
}
2554

26-
async fn alice(&mut self) -> Result<Account> {
27-
self.account("alice").await
55+
async fn bob(&mut self) -> Result<Account> {
56+
self.account("bob").await
2857
}
2958

30-
async fn fee(&mut self) -> Result<Account> {
31-
self.account("fee").await
59+
async fn alice(&mut self) -> Result<Account> {
60+
self.account("alice").await
3261
}
3362

3463
fn lockup(&self) -> LockupContract<'_> {
3564
LockupContract::with_contract(&self.contracts[LOCKUP_CONTRACT])
3665
}
66+
67+
fn multisig(&self) -> Multisig<'_> {
68+
Multisig::with_contract(&self.contracts[MULTISIG])
69+
}
70+
71+
fn ft_contract(&self) -> SweatFt<'_> {
72+
SweatFt::with_contract(&self.contracts[FT_CONTRACT])
73+
}
74+
75+
fn helper(&self) -> HelperContract<'_> {
76+
HelperContract::new(&self.contracts[HELPER_CONTRACT])
77+
}
3778
}
3879

3980
pub(crate) async fn prepare_contract() -> Result<Context> {
40-
let context = Context::new(&[LOCKUP_CONTRACT], "build-integration".into()).await?;
41-
//context.lockup().new().await?;
81+
let mut context = Context::new(
82+
&[LOCKUP_CONTRACT, MULTISIG, FT_CONTRACT, HELPER_CONTRACT],
83+
"build-integration".into(),
84+
)
85+
.await?;
86+
87+
let manager = context.manager().await?;
88+
89+
context
90+
.ft_contract()
91+
.new(".u.sweat.testnet".to_string().into())
92+
.call()
93+
.await?;
94+
context.ft_contract().add_oracle(&manager.to_near()).call().await?;
95+
96+
context.multisig().new(0).call().await?;
97+
98+
context
99+
.ft_contract()
100+
.tge_mint(&manager.to_near(), NearToken::from_near(100).as_near().into())
101+
.call()
102+
.await?;
103+
104+
context
105+
.ft_contract()
106+
.storage_deposit(context.lockup().contract_account().into(), None)
107+
.call()
108+
.await?;
109+
110+
context
111+
.lockup()
112+
.new(
113+
context.ft_contract().contract_account(),
114+
vec![manager.to_near()],
115+
Some(vec![manager.to_near()]),
116+
manager.to_near(),
117+
)
118+
.call()
119+
.await?;
120+
121+
context.helper().new().result().await?;
122+
42123
Ok(context)
43124
}

integration-tests/src/happy_flow.rs

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)