Skip to content

Commit be93396

Browse files
initial repo setup, no real logic yet
1 parent 3cd1a0f commit be93396

14 files changed

+490
-0
lines changed

.cargo/config

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
rustflags = ["-C", "link-args=-s"]

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ Cargo.lock
88

99
# These are backup files generated by rustfmt
1010
**/*.rs.bk
11+
12+
.idea

Cargo.toml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[workspace]
2+
members = [
3+
"treasury"
4+
]
5+
6+
[profile.release]
7+
codegen-units = 1
8+
# Tell `rustc` to optimize for small code size.
9+
opt-level = "z"
10+
lto = true
11+
debug = true
12+
panic = "abort"
13+
# Opt into extra safety checks on arithmetic operations https://stackoverflow.com/a/64136471/249801
14+
overflow-checks = true

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# treasury
22
Autonomous treasury for enabling DAOs or any type of near account to manage multiple funds to accrue interest across different vehicles
3+

build.sh

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
set -e
3+
4+
if [ -d "res" ]; then
5+
echo ""
6+
else
7+
mkdir res
8+
fi
9+
10+
cd "`dirname $0`"
11+
12+
if [ -z "$KEEP_NAMES" ]; then
13+
export RUSTFLAGS='-C link-arg=-s'
14+
else
15+
export RUSTFLAGS=''
16+
fi
17+
18+
cargo build --all --target wasm32-unknown-unknown --release
19+
cp target/wasm32-unknown-unknown/release/*.wasm ./res/

rust-toolchain

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.51.0

test.sh

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/bash
2+
./build.sh
3+
cargo test -- --nocapture

treasury/.cargo/config

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
rustflags = ["-C", "link-args=-s"]

treasury/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "treasury"
3+
version = "0.1.0"
4+
authors = ["cron.cat", "@trevorjtclarke"]
5+
edition = "2018"
6+
7+
[lib]
8+
crate-type = ["cdylib", "rlib"]
9+
10+
[dependencies]
11+
near-sdk = "3.1.0"
12+
near-contract-standards = "3.2.0"
13+
14+
[dev-dependencies]
15+
near-primitives-core = "0.4.0"

treasury/src/lib.rs

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use near_sdk::{
2+
assert_one_yocto,
3+
borsh::{self, BorshDeserialize, BorshSerialize},
4+
collections::{LookupMap, TreeMap, UnorderedMap, Vector},
5+
env,
6+
json_types::{Base64VecU8, ValidAccountId, U128, U64},
7+
log, near_bindgen,
8+
serde::{Deserialize, Serialize},
9+
serde_json::json,
10+
AccountId, Balance, BorshStorageKey, Gas, PanicOnDefault, Promise, StorageUsage,
11+
};
12+
13+
mod owner;
14+
mod storage_impl;
15+
mod utils;
16+
mod views;
17+
18+
near_sdk::setup_alloc!();
19+
20+
// Balance & Fee Definitions
21+
pub const ONE_NEAR: u128 = 1_000_000_000_000_000_000_000_000;
22+
pub const GAS_BASE_PRICE: Balance = 100_000_000;
23+
pub const GAS_BASE_FEE: Gas = 3_000_000_000_000;
24+
pub const STAKE_BALANCE_MIN: u128 = 10 * ONE_NEAR;
25+
26+
27+
// #[derive(BorshStorageKey, BorshSerialize)]
28+
// pub enum StorageKeys {
29+
// Tasks,
30+
// Agents,
31+
// Slots,
32+
// AgentsActive,
33+
// AgentsPending,
34+
// }
35+
36+
#[near_bindgen]
37+
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
38+
pub struct Contract {
39+
// Runtime
40+
paused: bool,
41+
owner_id: AccountId,
42+
43+
// Storage
44+
// agent_storage_usage: StorageUsage,
45+
}
46+
47+
#[near_bindgen]
48+
impl Contract {
49+
/// ```bash
50+
/// near call cron.testnet new --accountId cron.testnet
51+
/// ```
52+
#[init]
53+
pub fn new() -> Self {
54+
let mut this = Contract {
55+
paused: false,
56+
owner_id: env::signer_account_id(),
57+
};
58+
// this.measure_account_storage_usage();
59+
this
60+
}
61+
62+
// /// Measure the storage an agent will take and need to provide
63+
// fn measure_account_storage_usage(&mut self) {
64+
// let initial_storage_usage = env::storage_usage();
65+
// // Create a temporary, dummy entry and measure the storage used.
66+
// let tmp_account_id = "a".repeat(64);
67+
// let tmp_agent = Agent {
68+
// status: agent::AgentStatus::Pending,
69+
// payable_account_id: tmp_account_id.clone(),
70+
// balance: U128::from(0),
71+
// total_tasks_executed: U128::from(0),
72+
// last_missed_slot: 0,
73+
// };
74+
// self.agents.insert(&tmp_account_id, &tmp_agent);
75+
// self.agent_storage_usage = env::storage_usage() - initial_storage_usage;
76+
// // Remove the temporary entry.
77+
// self.agents.remove(&tmp_account_id);
78+
// }
79+
}
80+
81+
// #[cfg(test)]
82+
// mod tests {
83+
// use super::*;
84+
// use near_sdk::json_types::ValidAccountId;
85+
// use near_sdk::test_utils::{accounts, VMContextBuilder};
86+
// use near_sdk::{testing_env, MockedBlockchain};
87+
88+
// const BLOCK_START_BLOCK: u64 = 52_201_040;
89+
// const BLOCK_START_TS: u64 = 1_624_151_503_447_000_000;
90+
91+
// fn get_context(predecessor_account_id: ValidAccountId) -> VMContextBuilder {
92+
// let mut builder = VMContextBuilder::new();
93+
// builder
94+
// .current_account_id(accounts(0))
95+
// .signer_account_id(predecessor_account_id.clone())
96+
// .signer_account_pk(b"ed25519:4ZhGmuKTfQn9ZpHCQVRwEr4JnutL8Uu3kArfxEqksfVM".to_vec())
97+
// .predecessor_account_id(predecessor_account_id)
98+
// .block_index(BLOCK_START_BLOCK)
99+
// .block_timestamp(BLOCK_START_TS);
100+
// builder
101+
// }
102+
103+
// #[test]
104+
// fn test_contract_new() {
105+
// let mut context = get_context(accounts(1));
106+
// testing_env!(context.build());
107+
// let contract = Contract::new();
108+
// testing_env!(context.is_view(true).build());
109+
// assert!(contract.get_tasks(None, None, None).is_empty());
110+
// }
111+
// }

treasury/src/owner.rs

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use crate::*;
2+
3+
#[near_bindgen]
4+
impl Contract {
5+
/// Changes core configurations
6+
/// Should only be updated by owner -- in best case DAO based :)
7+
pub fn update_settings(
8+
&mut self,
9+
owner_id: Option<AccountId>,
10+
) {
11+
assert_eq!(
12+
self.owner_id,
13+
env::predecessor_account_id(),
14+
"Must be owner"
15+
);
16+
17+
// BE CAREFUL!
18+
if let Some(owner_id) = owner_id {
19+
self.owner_id = owner_id;
20+
}
21+
}
22+
}
23+
24+
// #[cfg(test)]
25+
// mod tests {
26+
// use super::*;
27+
// use near_sdk::json_types::ValidAccountId;
28+
// use near_sdk::test_utils::{accounts, VMContextBuilder};
29+
// use near_sdk::{testing_env, MockedBlockchain};
30+
31+
// const BLOCK_START_BLOCK: u64 = 52_201_040;
32+
// const BLOCK_START_TS: u64 = 1_624_151_503_447_000_000;
33+
34+
// fn get_context(predecessor_account_id: ValidAccountId) -> VMContextBuilder {
35+
// let mut builder = VMContextBuilder::new();
36+
// builder
37+
// .current_account_id(accounts(0))
38+
// .signer_account_id(predecessor_account_id.clone())
39+
// .signer_account_pk(b"ed25519:4ZhGmuKTfQn9ZpHCQVRwEr4JnutL8Uu3kArfxEqksfVM".to_vec())
40+
// .predecessor_account_id(predecessor_account_id)
41+
// .block_index(BLOCK_START_BLOCK)
42+
// .block_timestamp(BLOCK_START_TS);
43+
// builder
44+
// }
45+
46+
// #[test]
47+
// #[should_panic(expected = "Must be owner")]
48+
// fn test_update_settings_fail() {
49+
// let mut context = get_context(accounts(1));
50+
// testing_env!(context.build());
51+
// let mut contract = Contract::new();
52+
// testing_env!(context.is_view(true).build());
53+
// assert_eq!(contract.slot_granularity, SLOT_GRANULARITY);
54+
55+
// testing_env!(context
56+
// .is_view(false)
57+
// .signer_account_id(accounts(3))
58+
// .predecessor_account_id(accounts(3))
59+
// .build());
60+
// contract.update_settings(None, Some(10), None, None, None, None, None, None);
61+
// }
62+
63+
// #[test]
64+
// fn test_update_settings() {
65+
// let mut context = get_context(accounts(1));
66+
// testing_env!(context.build());
67+
// let mut contract = Contract::new();
68+
// testing_env!(context.is_view(true).build());
69+
// assert_eq!(contract.slot_granularity, SLOT_GRANULARITY);
70+
71+
// testing_env!(context.is_view(false).build());
72+
// contract.update_settings(None, Some(10), Some(true), None, None, None, None, None);
73+
// testing_env!(context.is_view(true).build());
74+
// assert_eq!(contract.slot_granularity, 10);
75+
// assert_eq!(contract.paused, true);
76+
// }
77+
78+
// #[test]
79+
// fn test_update_settings_agent_ratio() {
80+
// let mut context = get_context(accounts(1));
81+
// testing_env!(context.build());
82+
// let mut contract = Contract::new();
83+
// testing_env!(context.is_view(true).build());
84+
// assert_eq!(contract.slot_granularity, SLOT_GRANULARITY);
85+
86+
// testing_env!(context.is_view(false).build());
87+
// contract.update_settings(
88+
// None,
89+
// None,
90+
// Some(true),
91+
// None,
92+
// None,
93+
// None,
94+
// Some(vec![U64(2), U64(5)]),
95+
// None,
96+
// );
97+
// testing_env!(context.is_view(true).build());
98+
// assert_eq!(contract.agent_task_ratio[0], 2);
99+
// assert_eq!(contract.agent_task_ratio[1], 5);
100+
// assert_eq!(contract.paused, true);
101+
// }
102+
// }

treasury/src/storage_impl.rs

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use crate::Contract;
2+
use near_contract_standards::storage_management::{
3+
StorageBalance, StorageBalanceBounds, StorageManagement,
4+
};
5+
use near_sdk::json_types::{ValidAccountId, U128};
6+
use near_sdk::{assert_one_yocto, env, log, AccountId, Balance, Promise};
7+
8+
impl Contract {
9+
fn internal_storage_balance_of(&self, account_id: &AccountId) -> Option<StorageBalance> {
10+
if self.agents.contains_key(account_id) {
11+
// The "available" balance is always zero because the storage isn't
12+
// variable for this contract.
13+
Some(StorageBalance {
14+
total: self.storage_balance_bounds().min,
15+
available: 0.into(),
16+
})
17+
} else {
18+
None
19+
}
20+
}
21+
}
22+
23+
impl StorageManagement for Contract {
24+
// `registration_only` doesn't affect the implementation here, as there's no need to add additional
25+
// storage, so there's only one balance to attach.
26+
#[allow(unused_variables)]
27+
fn storage_deposit(
28+
&mut self,
29+
account_id: Option<ValidAccountId>,
30+
registration_only: Option<bool>,
31+
) -> StorageBalance {
32+
self.register_agent(account_id.clone());
33+
let account_id = account_id
34+
.map(|a| a.into())
35+
.unwrap_or_else(|| env::predecessor_account_id());
36+
self.internal_storage_balance_of(&account_id).unwrap()
37+
}
38+
39+
/// While storage_withdraw normally allows the caller to retrieve `available` balance, this
40+
/// contract sets storage_balance_bounds.min = storage_balance_bounds.max,
41+
/// which means available balance will always be 0. So this implementation:
42+
/// * panics if `amount > 0`
43+
/// * never transfers Ⓝ to caller
44+
/// * returns a `storage_balance` struct if `amount` is 0
45+
fn storage_withdraw(&mut self, amount: Option<U128>) -> StorageBalance {
46+
assert_one_yocto();
47+
let predecessor = env::predecessor_account_id();
48+
if let Some(storage_balance) = self.internal_storage_balance_of(&predecessor) {
49+
match amount {
50+
Some(amount) if amount.0 > 0 => {
51+
let panic_msg = format!("The amount is greater than the available storage balance. Remember there's a minimum balance needed for an agent's storage. That minimum is {}. To unregister an agent, use the 'unregister_agent' or 'storage_unregister' with the 'force' option.", self.agent_storage_usage);
52+
env::panic(panic_msg.as_bytes());
53+
}
54+
_ => storage_balance,
55+
}
56+
} else {
57+
env::panic(format!("The account {} is not registered", &predecessor).as_bytes());
58+
}
59+
}
60+
61+
fn storage_unregister(&mut self, force: Option<bool>) -> bool {
62+
assert_one_yocto();
63+
let account_id = env::predecessor_account_id();
64+
let force = force.unwrap_or(false);
65+
if let Some(agent) = self.agents.get(&account_id) {
66+
let balance = agent.balance.0;
67+
if balance == 0 || force {
68+
self.remove_agent(account_id.clone());
69+
70+
// We add 1 to reimburse for the 1 yoctoⓃ used to call this method
71+
Promise::new(account_id).transfer(balance + 1);
72+
log!(
73+
"Agent has been removed and refunded the storage cost of {}",
74+
balance + 1
75+
);
76+
true
77+
} else {
78+
env::panic(b"Can't unregister the agent with the positive balance. Must use the 'force' parameter if desired.")
79+
}
80+
} else {
81+
log!("The agent {} is not registered", &account_id);
82+
false
83+
}
84+
}
85+
86+
fn storage_balance_bounds(&self) -> StorageBalanceBounds {
87+
let required_storage_balance =
88+
Balance::from(self.agent_storage_usage) * env::storage_byte_cost();
89+
StorageBalanceBounds {
90+
min: required_storage_balance.into(),
91+
max: Some(required_storage_balance.into()),
92+
}
93+
}
94+
95+
fn storage_balance_of(&self, account_id: ValidAccountId) -> Option<StorageBalance> {
96+
self.internal_storage_balance_of(account_id.as_ref())
97+
}
98+
}

0 commit comments

Comments
 (0)