Skip to content
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

feat: upgrade factory from DAO #193

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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ neardev

**/nodemodules
**/*.lock.json

.DS_Store
140 changes: 140 additions & 0 deletions scripts/upgrade_factory_from_dao.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/bin/bash
#### --------------------------------------------
#### NOTE: The following flows are supported in this file, for testing!
# - Create an UpgradeDAO via sputnikv2.testnet, funded with enough for 10 upgrades
# - Create an Upgradeable DAO via sputnikv2.testnet, for testing v2-v3 upgrade
# - UpgradeDAO proposal to store_blob on Upgradeable DAO
# - Upgradeable DAO proposal UpgradeSelf with hash from UpgradeDAO store_blob
# - Check code_hash on Upgradeable DAO
#### --------------------------------------------
set -e

# # TODO: Change to the official approved commit:
# COMMIT_V3=596f27a649c5df3310e945a37a41a957492c0322
# # git checkout $COMMIT_V3

# build the things
./build.sh

export NEAR_ENV=testnet
export FACTORY=testnet

if [ -z ${NEAR_ACCT+x} ]; then
# export NEAR_ACCT=sputnikv2.$FACTORY
export NEAR_ACCT=sputnikpm.$FACTORY
else
export NEAR_ACCT=$NEAR_ACCT
fi

# export FACTORY_ACCOUNT_ID=sputnikv2.$FACTORY
export FACTORY_ACCOUNT_ID=factory_1.$NEAR_ACCT
# export DAO_ACCOUNT_ID=croncat.sputnikv2.$FACTORY
export MAX_GAS=300000000000000
export GAS_100_TGAS=100000000000000
export GAS_150_TGAS=150000000000000
export GAS_220_TGAS=220000000000000
export BOND_AMOUNT=1
export BYTE_STORAGE_COST=10000000000000000000000000


# #### --------------------------------------------
# #### New Factory for entire test
# #### --------------------------------------------
near create-account $FACTORY_ACCOUNT_ID --masterAccount $NEAR_ACCT --initialBalance 80
# #### --------------------------------------------

# #### --------------------------------------------
# #### Build and deploy factory
# - Build and Deploy factory contract by running the following command from your current directory _(`sputnik-dao-contract/sputnikdao-factory2`)_:
# - Initialize factory
# #### --------------------------------------------
near deploy $FACTORY_ACCOUNT_ID --wasmFile=res/sputnikdao_factory2.wasm --accountId $FACTORY_ACCOUNT_ID
near call $FACTORY_ACCOUNT_ID new --accountId $FACTORY_ACCOUNT_ID --gas 100000000000000
# #### --------------------------------------------


#### --------------------------------------------
#### Deploy DAO that can upgrade factory
#### --------------------------------------------
export COUNCIL='["'$NEAR_ACCT'"]'
export TIMESTAMP=$(date +"%s")
export DAO_NAME=upgrademe-1-$TIMESTAMP
export DAO_ARGS=`echo '{"config": {"name": "'$DAO_NAME'", "purpose": "A dao that can upgrade a factory", "metadata":""}, "policy": '$COUNCIL'}' | base64`
near call $FACTORY_ACCOUNT_ID create "{\"name\": \"$DAO_NAME\", \"args\": \"$DAO_ARGS\"}" --accountId $FACTORY_ACCOUNT_ID --gas $GAS_150_TGAS --amount 12
export GENDAO=$DAO_NAME.$FACTORY_ACCOUNT_ID
#### --------------------------------------------


#### --------------------------------------------
#### Quick sanity check on getters
#### --------------------------------------------
near view $FACTORY_ACCOUNT_ID get_dao_list
#### --------------------------------------------

#### --------------------------------------------
#### Set owner of factory as DAO
#### --------------------------------------------
near call $FACTORY_ACCOUNT_ID set_owner '{"owner_id":"'$GENDAO'"}' --accountId $FACTORY_ACCOUNT_ID
#### --------------------------------------------

#### --------------------------------------------
#### Modify factory, rebuild, and create proposal to store
#### --------------------------------------------
# Store the code data
export FACTORYCODE='cat sputnikdao_factory2.wasm | base64'
# - most likely need to use near-cli-rs due to wasm string size limit
# Store blob in DAO
echo '{ "proposal": { "description": "Store upgrade", "kind": { "FunctionCall": { "receiver_id": "'$GENDAO'", "actions": [ { "method_name": "store_blob", "args": "'$(eval $FACTORYCODE)'", "deposit": "'$BYTE_STORAGE_COST'", "gas": "'$GAS_150_TGAS'" } ]}}}}' | base64 | pbcopy
# near call add_proposal using near-cli-rs due to string size limit with above base64 args
# Once proposal created on Genesis DAO that is owner of factory account now, vote on it so that it can act_proposal storing the new factory code and returning a hash
# Vote on proposal
near call $GENDAO act_proposal '{"id": 0, "action" :"VoteApprove"}' --accountId $CONTRACT_ID --gas $MAX_GAS
# Act proposal might fail due to exceeded pre paid gas limit but factory is stored
# Set factory hash
export FACTORY_HASH=""

#### --------------------------------------------
#### Create proposal to upgrade factory to new factory hash
#### --------------------------------------------
near call $GENDAO add_proposal '{
"proposal": {
"description": "Upgrade to new factory hash using local stored code",
"kind": {
"UpgradeRemote": {
"receiver_id": "spudnike.testnet",
"method_name": "upgrade_factory",
"hash": "'$FACTORY_HASH'"
}
}
}
}' --accountId $CONTRACT_ID --amount $BOND_AMOUNT --gas $MAX_GAS
# Vote on proposal
near call $GENDAO act_proposal '{"id": 1, "action" :"VoteApprove"}' --accountId $CONTRACT_ID --gas $MAX_GAS

# Factory should be pointing to new hash

# Optionally store factory metadata
export PROPOSALARGS=`echo '{"factory_hash": "'$FACTORY_HASH'", "metadata": {"version": [1,0], "commit_id": ""}, "set_default": true}' | base64`
near call $GENDAO add_proposal '{
"proposal": {
"description": "Store factory metadata",
"kind": {
"FunctionCall": {
"receiver_id": "'$CONTRACT_ID'",
"actions": [
{
"method_name": "store_factory_metadata",
"args": "'$PROPOSALARGS'",
"deposit": "'$BYTE_STORAGE_COST'",
"gas": "'$GAS_220_TGAS'"
}
]
}
}
}
}' --accountId $CONTRACT_ID --amount $BOND_AMOUNT --gas $MAX_GAS

# Vote on storing factory metadata

near call $GENDAO act_proposal '{"id": 1, "action" :"VoteApprove"}' --accountId $CONTRACT_ID --gas $MAX_GAS
#### --------------------------------------------
Binary file modified sputnikdao-factory2/res/sputnikdao_factory2.wasm
Binary file not shown.
47 changes: 47 additions & 0 deletions sputnikdao-factory2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const DAO_CONTRACT_NO_DATA: &str = "no data";
const GAS_STORE_CONTRACT_LEFTOVER: Gas = Gas(20_000_000_000_000);
const ON_REMOVE_CONTRACT_GAS: Gas = Gas(10_000_000_000_000);
const NO_DEPOSIT: Balance = 0;
const UPDATE_GAS_LEFTOVER: Gas = Gas(10_000_000_000_000);

#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
#[cfg_attr(not(target_arch = "wasm32"), derive(Clone, Debug))]
Expand Down Expand Up @@ -360,6 +361,21 @@ impl SputnikDAOFactory {
return deserialized_metadata.to_vec();
}

/// Should only be called by this contract on migration.
/// This is NOOP implementation. KEEP IT if you haven't changed contract state.
/// If you have changed state, you need to implement migration from old state (keep the old struct with different name to deserialize it first).
/// After migrate goes live on MainNet, return this implementation for next updates.
#[init(ignore_state)]
pub fn migrate() -> Self {
assert_eq!(
env::predecessor_account_id(),
env::current_account_id(),
"ERR_NOT_ALLOWED"
);
let this: SputnikDAOFactory = env::state_read().expect("ERR_CONTRACT_IS_NOT_INITIALIZED");
this
}

fn assert_owner(&self) {
assert_eq!(
self.get_owner(),
Expand Down Expand Up @@ -392,6 +408,37 @@ pub extern "C" fn store() {
);
}

/// Store new contract. Non serialized argument is the contract.
/// Returns base58 of the hash of the contract.
#[no_mangle]
pub extern "C" fn upgrade_factory() {
env::setup_panic_hook();
let contract: SputnikDAOFactory = env::state_read().expect("Contract is not initialized");
contract.assert_owner();

let current_id = env::current_account_id();

let input = env::input().expect("ERR_NO_INPUT");

// Create a promise toward given account.
let promise_id = env::promise_batch_create(&current_id);

// Deploy the contract code.
env::promise_batch_action_deploy_contract(promise_id, &input);

// Call promise to migrate the state.
// Batched together to fail upgrade if migration fails.
env::promise_batch_action_function_call(
promise_id,
"migrate",
&[],
NO_DEPOSIT,
env::prepaid_gas() - env::used_gas() - UPDATE_GAS_LEFTOVER,
);

env::promise_return(promise_id);
}

#[cfg(test)]
mod tests {
use near_sdk::test_utils::test_env::{alice, bob, carol};
Expand Down