diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2bb36ab10..c781ec805 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -88,8 +88,8 @@ jobs: - name: Install binaryen run: | set -e - curl -L https://github.com/WebAssembly/binaryen/releases/download/version_105/binaryen-version_105-x86_64-linux.tar.gz | tar xzf - - echo "`pwd`/binaryen-version_105/bin" >> $GITHUB_PATH + curl -L https://github.com/WebAssembly/binaryen/releases/download/version_119/binaryen-version_119-x86_64-linux.tar.gz | tar xzf - + echo "`pwd`/binaryen-version_119/bin" >> $GITHUB_PATH # triggers all build.rs steps - name: Trigger build.rs steps run: | diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2d68a1a50..0cb30f627 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: tests: strategy: matrix: - platform: [ubuntu-latest, macos-latest] + platform: [ubuntu-22.04, macos-13] runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v2 @@ -16,6 +16,15 @@ jobs: with: toolchain: stable target: wasm32-unknown-unknown + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.17.0 + - name: Setup dependencies + if: startsWith(matrix.platform, 'macos') + run: | + brew install llvm@14 + echo /usr/local/opt/llvm@14/bin >> $GITHUB_PATH - name: Build env: IS_GITHUB_ACTION: true diff --git a/Cargo.lock b/Cargo.lock index 6738d48fe..eca201b69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,9 +84,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.52" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" dependencies = [ "proc-macro2", "quote", @@ -361,12 +361,13 @@ dependencies = [ [[package]] name = "cached_proc_macro" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4230b8d9f5db741004bfaef172c5b2dbf0eb94f105204cc6147a220080daaa85" +checksum = "25a685ba39b57a91a53d149dcbef854f50fbe204d1ff6081ea0bec3529a0c456" dependencies = [ + "async-mutex", "cached_proc_macro_types", - "darling", + "darling 0.10.2", "quote", "syn", ] @@ -712,35 +713,70 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.1" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core 0.10.2", + "darling_macro 0.10.2", +] + +[[package]] +name = "darling" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c" +dependencies = [ + "darling_core 0.12.4", + "darling_macro 0.12.4", +] + +[[package]] +name = "darling_core" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ - "darling_core", - "darling_macro", + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn", ] [[package]] name = "darling_core" -version = "0.13.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324" +checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn", ] [[package]] name = "darling_macro" -version = "0.13.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ - "darling_core", + "darling_core 0.10.2", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" +dependencies = [ + "darling_core 0.12.4", "quote", "syn", ] @@ -848,20 +884,20 @@ dependencies = [ [[package]] name = "enumset" -version = "1.0.8" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6216d2c19a6fb5f29d1ada1dc7bc4367a8cbf0fa4af5cf12e07b5bbdde6b5b2c" +checksum = "fbd795df6708a599abf1ee10eacc72efd052b7a5f70fdf0715e4d5151a6db9c3" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.5.5" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6451128aa6655d880755345d085494cf7561a6bee7c8dc821e5d77e6d267ecd4" +checksum = "e19c52f9ec503c8a68dc04daf71a04b07e690c32ab1a8b68e33897f255269d47" dependencies = [ - "darling", + "darling 0.12.4", "proc-macro2", "quote", "syn", @@ -2593,12 +2629,6 @@ dependencies = [ "semver 1.0.6", ] -[[package]] -name = "rustversion" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" - [[package]] name = "ryu" version = "1.0.9" @@ -2634,9 +2664,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" dependencies = [ "serde_derive", ] @@ -2662,9 +2692,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" dependencies = [ "proc-macro2", "quote", @@ -2685,22 +2715,21 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.12.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1e6ec4d8950e5b1e894eac0d360742f3b1407a6078a604a731c4b3f49cefbc" +checksum = "b44be9227e214a0420707c9ca74c2d4991d9955bae9415a8f93f05cebf561be5" dependencies = [ - "rustversion", "serde", "serde_with_macros", ] [[package]] name = "serde_with_macros" -version = "1.5.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" +checksum = "e48b35457e9d855d3dc05ef32a73e0df1e2c0fd72c38796a4ee909160c8eeec2" dependencies = [ - "darling", + "darling 0.12.4", "proc-macro2", "quote", "syn", @@ -2779,7 +2808,7 @@ dependencies = [ [[package]] name = "sputnikdao-factory2" -version = "0.2.0" +version = "0.2.1" dependencies = [ "near-sdk", "near-sdk-sim", @@ -2787,7 +2816,7 @@ dependencies = [ [[package]] name = "sputnikdao2" -version = "2.0.0" +version = "2.3.1" dependencies = [ "hex", "near-contract-standards", @@ -2811,6 +2840,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + [[package]] name = "strsim" version = "0.10.0" @@ -2846,9 +2881,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.87" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e59d925cf59d8151f25a3bedf97c9c157597c9df7324d32d68991cc399ed08b" +checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" dependencies = [ "proc-macro2", "quote", diff --git a/scripts/deploy_factory_gasfix_testnet.sh b/scripts/deploy_factory_gasfix_testnet.sh new file mode 100755 index 000000000..c9aaf5f1e --- /dev/null +++ b/scripts/deploy_factory_gasfix_testnet.sh @@ -0,0 +1,288 @@ +#!/bin/bash +set -e + +# TODO: Change to the official approved commit: +# COMMIT_V3=596f27a649c5df3310e945a37a41a957492c0322 +COMMIT_V3=TBD_SEE_COMMIT_ONCE_LIVE +# 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 +else + export NEAR_ACCT=$NEAR_ACCT +fi + +# export FACTORY_ACCOUNT_ID=daofactory.$NEAR_ACCT +export FACTORY_ACCOUNT_ID=$NEAR_ACCT +export MAX_GAS=300000000000000 +export GAS_100_TGAS=100000000000000 +export GAS_150_TGAS=150000000000000 +export GAS_220_TGAS=220000000000000 +BOND_AMOUNT=1 +BYTE_STORAGE_COST=6000000000000000000000000 +COMMIT_V2= +V2_CODE_HASH=8RMeZ5cXDap6TENxaJKtigRYf3n139iHmTRe8ZUNey6N +COMMIT_V2A=UNOFFICIAL_SCRIPT_DATA +V2A_CODE_HASH=8LN56HLNjvwtiNb6pRVNSTMtPgJYqGjAgkVSHRiK5Wfv +COMMIT_V3=640495ba572345ca356376989738fbd5462e1ff8 +V3_CODE_HASH=783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 + +# NOTE: Only needed if deploying NEW factory +# #### -------------------------------------------- +# #### Account & Data management for setup +# #### -------------------------------------------- +# # near call $FACTORY_ACCOUNT_ID delete_contract '{"code_hash":"6SQymHtmezR3u9zAtLBQdb8RWCXxwxnigqSH2mRTxecB"}' --accountId $FACTORY_ACCOUNT_ID --gas $GAS_100_TGAS +# # near delete $FACTORY_ACCOUNT_ID $NEAR_ACCT +# near create-account $FACTORY_ACCOUNT_ID --masterAccount $NEAR_ACCT --initialBalance 80 +# #### -------------------------------------------- + + +# # NOTE: Only needed if deploying NEW factory +# #### -------------------------------------------- +# #### Grab the factory v2 code data +# #### -------------------------------------------- +# http --json post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=query \ +# params:='{"request_type":"view_code","finality":"final","account_id":"'sputnikv2.$FACTORY'"}' \ +# | jq -r .result.code_base64 \ +# | base64 --decode > sputnikdao_factory2_original.wasm + +# # Deploy the previous version to allow accurate testing +# near deploy --wasmFile sputnikdao_factory2_original.wasm --accountId $FACTORY_ACCOUNT_ID --initFunction new --initArgs '{}' --initGas $MAX_GAS +# #### -------------------------------------------- + + + +#### -------------------------------------------- +#### Upgrade the factory +#### NOTE: Make sure you've built on the right commit! +#### -------------------------------------------- +near deploy --wasmFile sputnikdao-factory2/res/sputnikdao_factory2.wasm --accountId $FACTORY_ACCOUNT_ID --force +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Grab the DAO v2 code data & store it in factory +#### -------------------------------------------- +http --json post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=query \ +params:='{"request_type":"view_code","finality":"final","account_id":"genesis.sputnikv2.testnet"}' \ +| jq -r .result.code_base64 \ +| base64 --decode > sputnikdao2_original.wasm + +# Store the code data +V2_BYTES='cat sputnikdao2_original.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V2_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v2_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V2_CODE_HASH=$(eval "tail -1 v2_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V2 CODE HASH: $V2_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V2_CODE_HASH'", "metadata": {"version": [2,0], "commit_id": "c2cf1553b070d04eed8f659571440b27d398c588"}, "set_default": false}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Get DAO v2a code data & store it in factory +#### Keep this around for gas-fixes version +#### NOTE: This doesnt really fix the upgrade path post neard 1.26.0 - those v2 DAOs will be stuck +#### -------------------------------------------- +# Store the code data +V2A_BYTES='cat sputnikdao2-gasfix/res/sputnikdao2_gasfix.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V2A_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v2a_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V2A_CODE_HASH=$(eval "tail -1 v2a_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V2A CODE HASH: $V2A_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V2A_CODE_HASH'", "metadata": {"version": [2,1], "commit_id": "'$COMMIT_V2A'"}, "set_default": true}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + + + +#### -------------------------------------------- +#### Get DAO v3 code data & store it in factory +#### -------------------------------------------- +# Store the code data +V3_BYTES='cat sputnikdao2/res/sputnikdao2.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V3_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v3_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V3_CODE_HASH=$(eval "tail -1 v3_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V3 CODE HASH: $V3_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V3_CODE_HASH'", "metadata": {"version": [3,0], "commit_id": "'$COMMIT_V3'"}, "set_default": false}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + + +# #### -------------------------------------------- +# #### Sanity check the new metadata +# #### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_contracts_metadata +# #### -------------------------------------------- + + +#### -------------------------------------------- +#### Deploy a v2 gasfix DAO & Some proposals +#### -------------------------------------------- +COUNCIL='["'$NEAR_ACCT'"]' +TIMESTAMP=$(date +"%s") +DAO_NAME=sputnik-v2-gasfix-$TIMESTAMP +DAO_ARGS=`echo '{"config": {"name": "'$DAO_NAME'", "purpose": "Sputnik GasFix v2 DAO '$TIMESTAMP'", "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 14 +DAO_ACCOUNT_ID=$DAO_NAME.$FACTORY_ACCOUNT_ID + +# some sample payouts +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "13370000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "10000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "20000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +# approve some, leave some +near call $DAO_ACCOUNT_ID act_proposal '{"id": 0, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +near call $DAO_ACCOUNT_ID act_proposal '{"id": 1, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DAO_ACCOUNT_ID get_proposal '{"id": 0}' +near view $DAO_ACCOUNT_ID get_proposal '{"id": 2}' +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Quick sanity check on getters +#### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_dao_list +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Upgradeable DAO Proposal +#### -------------------------------------------- +# 783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 + +UPGRADE_PROPOSAL_ARGS=`echo '{"code_hash":"783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5"}' | base64` +# propose UpgradeSelf using the code_hash from store_blob +near call $DAO_ACCOUNT_ID add_proposal '{ + "proposal": { + "description": "Upgrade to v3 DAO code using upgrade contract via factory", + "kind": { + "FunctionCall": { + "receiver_id": "'$FACTORY_ACCOUNT_ID'", + "actions": [ + { + "method_name": "store_contract_self", + "args": "'$UPGRADE_PROPOSAL_ARGS'", + "deposit": "'$BYTE_STORAGE_COST'", + "gas": "'$GAS_220_TGAS'" + } + ] + } + } + } +}' --accountId $NEAR_ACCT --amount $BOND_AMOUNT --gas $MAX_GAS +# approve +near call $DAO_ACCOUNT_ID act_proposal '{"id": 3, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DAO_ACCOUNT_ID get_proposal '{"id": 3}' +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Upgrade a v2a DAO to v3 +#### -------------------------------------------- +V3_CODE_HASH=$(eval "tail -1 v3_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "Upgrade V3 CODE HASH: $V3_CODE_HASH" +# some sample payouts +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Upgrade to v3", "kind": { "UpgradeSelf": { "hash": "'$V3_CODE_HASH'" } } } }' --accountId $NEAR_ACCT --amount 1 +# approve some, leave some +near call $DAO_ACCOUNT_ID act_proposal '{"id": 4, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DAO_ACCOUNT_ID get_proposal '{"id": 4}' +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Remove cached blob DAO Proposal +#### -------------------------------------------- +# 783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 + +REMOVE_PROPOSAL_ARGS=`echo '{"code_hash":"783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5"}' | base64` +# propose UpgradeSelf using the code_hash from store_blob +near call $DAO_ACCOUNT_ID add_proposal '{ + "proposal": { + "description": "Remove DAO upgrade contract local code blob via factory", + "kind": { + "FunctionCall": { + "receiver_id": "'$FACTORY_ACCOUNT_ID'", + "actions": [ + { + "method_name": "remove_contract_self", + "args": "'$REMOVE_PROPOSAL_ARGS'", + "deposit": "0", + "gas": "'$GAS_220_TGAS'" + } + ] + } + } + } +}' --accountId $NEAR_ACCT --amount $BOND_AMOUNT --gas $MAX_GAS +# approve +near call $DAO_ACCOUNT_ID act_proposal '{"id": 5, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DAO_ACCOUNT_ID get_proposal '{"id": 5}' +#### -------------------------------------------- + + + + +#### -------------------------------------------- +#### Deploy a v3 DAO & Some proposals +#### -------------------------------------------- +COUNCIL='["'$NEAR_ACCT'"]' +TIMESTAMP=$(date +"%s") +DAO_NAME=sputnikdao-dev-v3-$TIMESTAMP +DAO_ARGS=`echo '{"config": {"name": "'$DAO_NAME'", "purpose": "Sputnik Dev v3 DAO '$TIMESTAMP'", "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 +DEMO_DAO_ACCOUNT=$DAO_NAME.$FACTORY_ACCOUNT_ID + +# Quick check for v3 DAO +near view $DEMO_DAO_ACCOUNT get_available_amount + +# some sample payouts +near call $DEMO_DAO_ACCOUNT add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "1000000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $DEMO_DAO_ACCOUNT add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "2000000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $DEMO_DAO_ACCOUNT add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "3000000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +# approve some, leave some +near call $DEMO_DAO_ACCOUNT act_proposal '{"id": 0, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +near call $DEMO_DAO_ACCOUNT act_proposal '{"id": 1, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DEMO_DAO_ACCOUNT get_proposal '{"id": 0}' +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Sanity check v3 dao all worked +#### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_dao_list +#### -------------------------------------------- + + +# #### -------------------------------------------- +# cleanup local files! +# #### -------------------------------------------- +rm sputnikdao2_original.wasm +rm sputnikdao_factory2_original.wasm +rm v2_code_hash_result.txt +rm v2a_code_hash_result.txt +rm v3_code_hash_result.txt + +echo "Dev Factory Deploy & Test Complete" \ No newline at end of file diff --git a/scripts/deploy_factory_mainnet.sh b/scripts/deploy_factory_mainnet.sh new file mode 100755 index 000000000..305c512c6 --- /dev/null +++ b/scripts/deploy_factory_mainnet.sh @@ -0,0 +1,167 @@ +#!/bin/bash +set -e + +# build the things +./build.sh + +export NEAR_ENV=mainnet +export FACTORY=near + +if [ -z ${NEAR_ACCT+x} ]; then + export NEAR_ACCT=daofactory.$FACTORY +else + export NEAR_ACCT=$NEAR_ACCT +fi + +export FACTORY_ACCOUNT_ID=$NEAR_ACCT +# export FACTORY_ACCOUNT_ID=subfactory.$NEAR_ACCT +export MAX_GAS=300000000000000 +export GAS_100_TGAS=100000000000000 +export GAS_150_TGAS=150000000000000 +export GAS_220_TGAS=220000000000000 +BOND_AMOUNT=1 +BYTE_STORAGE_COST=6000000000000000000000000 +COMMIT_V2=c2cf1553b070d04eed8f659571440b27d398c588 +V2_CODE_HASH=8RMeZ5cXDap6TENxaJKtigRYf3n139iHmTRe8ZUNey6N +COMMIT_V2A=UNOFFICIAL_SCRIPT_DATA +V2A_CODE_HASH=8LN56HLNjvwtiNb6pRVNSTMtPgJYqGjAgkVSHRiK5Wfv +COMMIT_V3=640495ba572345ca356376989738fbd5462e1ff8 +V3_CODE_HASH=783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 + +# #### -------------------------------------------- +# #### Account & Data management for setup +# #### -------------------------------------------- +# near call $FACTORY_ACCOUNT_ID delete_contract '{"code_hash":"'$V3_CODE_HASH'"}' --accountId $FACTORY_ACCOUNT_ID --gas $GAS_100_TGAS +# near delete $FACTORY_ACCOUNT_ID $NEAR_ACCT +# near create-account $FACTORY_ACCOUNT_ID --masterAccount $NEAR_ACCT --initialBalance 62 +# #### -------------------------------------------- + + +#### -------------------------------------------- +#### Grab the factory v2 code data +#### -------------------------------------------- +http --json post https://rpc.mainnet.near.org jsonrpc=2.0 id=dontcare method=query \ +params:='{"request_type":"view_code","finality":"final","account_id":"'sputnik-dao.$FACTORY'"}' \ +| jq -r .result.code_base64 \ +| base64 --decode > sputnikdao_factory2_original.wasm + +# Deploy the previous version to allow accurate testing +near deploy --wasmFile sputnikdao_factory2_original.wasm --accountId $FACTORY_ACCOUNT_ID --initFunction new --initArgs '{}' --initGas $MAX_GAS +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Deploy a v2 DAO & Some proposals +#### -------------------------------------------- +COUNCIL='["'$NEAR_ACCT'"]' +TIMESTAMP=$(date +"%s") +DAO_NAME=sputnikdao-v2-$TIMESTAMP +DAO_ARGS=`echo '{"config": {"name": "'$DAO_NAME'", "purpose": "Upgrade V2 DAO '$TIMESTAMP'", "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 +DAO_ACCOUNT_ID=$DAO_NAME.$FACTORY_ACCOUNT_ID + +# some sample payouts +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "13370000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "10000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "20000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +# approve some, leave some +near call $DAO_ACCOUNT_ID act_proposal '{"id": 0, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +near call $DAO_ACCOUNT_ID act_proposal '{"id": 1, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DAO_ACCOUNT_ID get_proposal '{"id": 0}' +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Quick sanity check on getters +#### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_dao_list +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Upgrade the factory +#### NOTE: Make sure you've built on the right commit! +#### -------------------------------------------- +near deploy --wasmFile sputnikdao-factory2/res/sputnikdao_factory2.wasm --accountId $FACTORY_ACCOUNT_ID --force +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Get DAO v2a code data & store it in factory +#### Keep this around for gas-fixes version +#### NOTE: This doesnt really fix the upgrade path post neard 1.26.0 - those v2 DAOs will be stuck +#### -------------------------------------------- +# Store the code data +V2A_BYTES='cat sputnikdao2-gasfix/res/sputnikdao2_gasfix.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V2A_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v2a_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V2A_CODE_HASH=$(eval "tail -1 v2a_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V2A CODE HASH: $V2A_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V2A_CODE_HASH'", "metadata": {"version": [2,1], "commit_id": "'$COMMIT_V2A'"}, "set_default": true}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Get DAO v3 code data & store it in factory +#### -------------------------------------------- +# Store the code data +V3_BYTES='cat sputnikdao2/res/sputnikdao2.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V3_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 6 > v3_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V3_CODE_HASH=$(eval "tail -1 v3_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V3 CODE HASH: $V3_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V3_CODE_HASH'", "metadata": {"version": [3,0], "commit_id": "'$COMMIT_V3'"}, "set_default": false}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Sanity check the new metadata & DAO +#### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_contracts_metadata +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Deploy a v2a DAO & Some proposals +#### -------------------------------------------- +COUNCIL='["'$NEAR_ACCT'"]' +TIMESTAMP=$(date +"%s") +DAO_NAME=sputnikdao-v2a-$TIMESTAMP +DAO_ARGS=`echo '{"config": {"name": "'$DAO_NAME'", "purpose": "Upgrade Gasfix DAO '$TIMESTAMP'", "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 +GASDAO_ACCOUNT_ID=$DAO_NAME.$FACTORY_ACCOUNT_ID + +# some sample payouts +near call $GASDAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "13370000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $GASDAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "10000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $GASDAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "20000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +# approve some, leave some +near call $GASDAO_ACCOUNT_ID act_proposal '{"id": 0, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +near call $GASDAO_ACCOUNT_ID act_proposal '{"id": 1, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $GASDAO_ACCOUNT_ID get_proposal '{"id": 0}' +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Quick sanity check on getters +#### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_dao_list +#### -------------------------------------------- + + +# #### -------------------------------------------- +# cleanup local files! +# #### -------------------------------------------- +rm sputnikdao2_original.wasm +rm sputnikdao_factory2_original.wasm +rm v2_code_hash_result.txt +rm v2a_code_hash_result.txt +rm v3_code_hash_result.txt + +echo "Mainnet Factory Deploy & Test Complete" diff --git a/scripts/deploy_factory_dev.sh b/scripts/deploy_factory_testnet.sh similarity index 60% rename from scripts/deploy_factory_dev.sh rename to scripts/deploy_factory_testnet.sh index 3d087dd4a..feab43f75 100755 --- a/scripts/deploy_factory_dev.sh +++ b/scripts/deploy_factory_testnet.sh @@ -1,21 +1,9 @@ #!/bin/bash -#### -------------------------------------------- -#### NOTE: The following flows are supported in this file, for testing! -# - deploy factory v2 -# - create new DAO -# - create 1-3 proposals -# - upgrade to factory v3 -# - submit DAO code v2 -# - submit DAO metadata v2 -# - submit DAO code v3 -# - submit DAO metadata v3 -# - upgrade DAO to v3 -# - check if proposals return -#### -------------------------------------------- set -e # TODO: Change to the official approved commit: -COMMIT_V3=596f27a649c5df3310e945a37a41a957492c0322 +# COMMIT_V3=596f27a649c5df3310e945a37a41a957492c0322 +COMMIT_V3=TBD_SEE_COMMIT_ONCE_LIVE # git checkout $COMMIT_V3 # build the things @@ -25,40 +13,47 @@ export NEAR_ENV=testnet export FACTORY=testnet if [ -z ${NEAR_ACCT+x} ]; then - # export NEAR_ACCT=sputnikv2.$FACTORY - export NEAR_ACCT=sputnikpm.$FACTORY + export NEAR_ACCT=sputnikv2.$FACTORY else export NEAR_ACCT=$NEAR_ACCT fi -export FACTORY_ACCOUNT_ID=factory_1.$NEAR_ACCT -# export DAO_ACCOUNT_ID=croncat.sputnikv2.$FACTORY -# export DAO_ACCOUNT_ID=sputnikdao-dev-v2-1645228499.factory3.sputnikpm.testnet +export FACTORY_ACCOUNT_ID=sputnikv2.$NEAR_ACCT export MAX_GAS=300000000000000 export GAS_100_TGAS=100000000000000 export GAS_150_TGAS=150000000000000 +BOND_AMOUNT=1 +BYTE_STORAGE_COST=6000000000000000000000000 +COMMIT_V2=c2cf1553b070d04eed8f659571440b27d398c588 +V2_CODE_HASH=8RMeZ5cXDap6TENxaJKtigRYf3n139iHmTRe8ZUNey6N +COMMIT_V2A=UNOFFICIAL_SCRIPT_DATA +V2A_CODE_HASH=8LN56HLNjvwtiNb6pRVNSTMtPgJYqGjAgkVSHRiK5Wfv +COMMIT_V3=640495ba572345ca356376989738fbd5462e1ff8 +V3_CODE_HASH=783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 -#### -------------------------------------------- -#### Account & Data management for setup -#### -------------------------------------------- -# near call $FACTORY_ACCOUNT_ID delete_contract '{"code_hash":"6SQymHtmezR3u9zAtLBQdb8RWCXxwxnigqSH2mRTxecB"}' --accountId $FACTORY_ACCOUNT_ID --gas $GAS_100_TGAS -# near delete $FACTORY_ACCOUNT_ID $NEAR_ACCT -near create-account $FACTORY_ACCOUNT_ID --masterAccount $NEAR_ACCT --initialBalance 40 -#### -------------------------------------------- +# NOTE: Only needed if deploying NEW factory +# #### -------------------------------------------- +# #### Account & Data management for setup +# #### -------------------------------------------- +# # near call $FACTORY_ACCOUNT_ID delete_contract '{"code_hash":"6SQymHtmezR3u9zAtLBQdb8RWCXxwxnigqSH2mRTxecB"}' --accountId $FACTORY_ACCOUNT_ID --gas $GAS_100_TGAS +# # near delete $FACTORY_ACCOUNT_ID $NEAR_ACCT +# near create-account $FACTORY_ACCOUNT_ID --masterAccount $NEAR_ACCT --initialBalance 80 +# #### -------------------------------------------- -#### -------------------------------------------- -#### Grab the factory v2 code data -#### -------------------------------------------- -http --json post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=query \ -params:='{"request_type":"view_code","finality":"final","account_id":"'sputnikv2.$FACTORY'"}' \ -| jq -r .result.code_base64 \ -| base64 --decode > sputnikdao_factory2_original.wasm +# NOTE: Only needed if deploying NEW factory +# #### -------------------------------------------- +# #### Grab the factory v2 code data +# #### -------------------------------------------- +# http --json post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=query \ +# params:='{"request_type":"view_code","finality":"final","account_id":"'sputnikv2.$FACTORY'"}' \ +# | jq -r .result.code_base64 \ +# | base64 --decode > sputnikdao_factory2_original.wasm -# Deploy the previous version to allow accurate testing -near deploy --wasmFile sputnikdao_factory2_original.wasm --accountId $FACTORY_ACCOUNT_ID --initFunction new --initArgs '{}' --initGas $MAX_GAS -#### -------------------------------------------- +# # Deploy the previous version to allow accurate testing +# near deploy --wasmFile sputnikdao_factory2_original.wasm --accountId $FACTORY_ACCOUNT_ID --initFunction new --initArgs '{}' --initGas $MAX_GAS +# #### -------------------------------------------- @@ -70,17 +65,17 @@ TIMESTAMP=$(date +"%s") DAO_NAME=sputnikdao-dev-v2-$TIMESTAMP DAO_ARGS=`echo '{"config": {"name": "'$DAO_NAME'", "purpose": "Sputnik Dev v2 DAO '$TIMESTAMP'", "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 10 -DEMO_DAO_ACCOUNT=$DAO_NAME.$FACTORY_ACCOUNT_ID +DAO_ACCOUNT_ID=$DAO_NAME.$FACTORY_ACCOUNT_ID # some sample payouts -near call $DEMO_DAO_ACCOUNT add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "1337000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 -near call $DEMO_DAO_ACCOUNT add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "1000000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 -near call $DEMO_DAO_ACCOUNT add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "2000000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "1337000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "1000000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Sample payment", "kind": { "Transfer": { "token_id": "", "receiver_id": "'$NEAR_ACCT'", "amount": "2000000000000000000000000" } } } }' --accountId $NEAR_ACCT --amount 1 # approve some, leave some -near call $DEMO_DAO_ACCOUNT act_proposal '{"id": 0, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS -near call $DEMO_DAO_ACCOUNT act_proposal '{"id": 1, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +near call $DAO_ACCOUNT_ID act_proposal '{"id": 0, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +near call $DAO_ACCOUNT_ID act_proposal '{"id": 1, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS # quick check all is good -near view $DEMO_DAO_ACCOUNT get_proposal '{"id": 0}' +near view $DAO_ACCOUNT_ID get_proposal '{"id": 0}' #### -------------------------------------------- @@ -112,7 +107,7 @@ params:='{"request_type":"view_code","finality":"final","account_id":"'$DAO_ACCO # Store the code data V2_BYTES='cat sputnikdao2_original.wasm | base64' -near call $FACTORY_ACCOUNT_ID store $(eval "$V2_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $GAS_100_TGAS --amount 10 > v2_code_hash_result.txt +near call $FACTORY_ACCOUNT_ID store $(eval "$V2_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v2_code_hash_result.txt # Update the factory metadata # Get the response code hash! @@ -122,6 +117,23 @@ near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V2_CODE_ #### -------------------------------------------- +#### -------------------------------------------- +#### Get DAO v2a code data & store it in factory +#### Keep this around for gas-fixes version +#### NOTE: This doesnt really fix the upgrade path post neard 1.26.0 - those v2 DAOs will be stuck +#### -------------------------------------------- +# Store the code data +V2A_BYTES='cat sputnikdao2-gasfix/res/sputnikdao2-gasfix.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V2A_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v2a_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V2A_CODE_HASH=$(eval "tail -1 v2a_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V2A CODE HASH: $V2A_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V2A_CODE_HASH'", "metadata": {"version": [2,1], "commit_id": "'$COMMIT_V2A'"}, "set_default": true}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + #### -------------------------------------------- #### Sanity check the new metadata @@ -136,7 +148,7 @@ near view $FACTORY_ACCOUNT_ID get_contracts_metadata #### -------------------------------------------- # Store the code data V3_BYTES='cat sputnikdao2/res/sputnikdao2.wasm | base64' -near call $FACTORY_ACCOUNT_ID store $(eval "$V3_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $GAS_100_TGAS --amount 10 > v3_code_hash_result.txt +near call $FACTORY_ACCOUNT_ID store $(eval "$V3_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v3_code_hash_result.txt # Update the factory metadata # Get the response code hash! @@ -152,7 +164,6 @@ near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V3_CODE_ #### -------------------------------------------- near view $FACTORY_ACCOUNT_ID get_contracts_metadata # Check a v2 DAO -near view $DEMO_DAO_ACCOUNT get_proposal '{"id": 0}' near view $DAO_ACCOUNT_ID get_proposal '{"id": 0}' near view $DAO_ACCOUNT_ID get_proposal '{"id": 2}' #### -------------------------------------------- @@ -160,17 +171,44 @@ near view $DAO_ACCOUNT_ID get_proposal '{"id": 2}' #### -------------------------------------------- -#### Upgrade a v2 DAO +#### Upgrade a v2 DAO to v2a +#### -------------------------------------------- +V2A_CODE_HASH=$(eval "tail -1 v2a_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "Upgrade V2A CODE HASH: $V2A_CODE_HASH" +# some sample payouts +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Upgrade to v2a", "kind": { "UpgradeSelf": { "hash": "'$V2A_CODE_HASH'" } } } }' --accountId $NEAR_ACCT --amount 1 +# approve some, leave some +near call $DAO_ACCOUNT_ID act_proposal '{"id": 3, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DAO_ACCOUNT_ID get_proposal '{"id": 0}' +near view $DAO_ACCOUNT_ID get_proposal '{"id": 3}' +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Sanity check the new metadata & DAO +#### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_contracts_metadata +# Check a v2 DAO +near view $DAO_ACCOUNT_ID get_proposal '{"id": 0}' +near view $DAO_ACCOUNT_ID get_proposal '{"id": 2}' +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Upgrade a v2a DAO to v3 #### -------------------------------------------- V3_CODE_HASH=$(eval "tail -1 v3_code_hash_result.txt | sed 's/^.//;s/.$//'") echo "Upgrade V3 CODE HASH: $V3_CODE_HASH" # some sample payouts near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Upgrade to v3", "kind": { "UpgradeSelf": { "hash": "'$V3_CODE_HASH'" } } } }' --accountId $NEAR_ACCT --amount 1 # approve some, leave some -near call $DAO_ACCOUNT_ID act_proposal '{"id": 3, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +near call $DAO_ACCOUNT_ID act_proposal '{"id": 4, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS # quick check all is good near view $DAO_ACCOUNT_ID get_proposal '{"id": 0}' -near view $DAO_ACCOUNT_ID get_proposal '{"id": 3}' +near view $DAO_ACCOUNT_ID get_proposal '{"id": 4}' #### -------------------------------------------- @@ -182,7 +220,7 @@ COUNCIL='["'$NEAR_ACCT'"]' TIMESTAMP=$(date +"%s") DAO_NAME=sputnikdao-dev-v3-$TIMESTAMP DAO_ARGS=`echo '{"config": {"name": "'$DAO_NAME'", "purpose": "Sputnik Dev v3 DAO '$TIMESTAMP'", "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 10 +near call $FACTORY_ACCOUNT_ID create "{\"name\": \"$DAO_NAME\", \"args\": \"$DAO_ARGS\"}" --accountId $FACTORY_ACCOUNT_ID --gas $GAS_150_TGAS --amount 12 DEMO_DAO_ACCOUNT=$DAO_NAME.$FACTORY_ACCOUNT_ID # Quick check for v3 DAO diff --git a/scripts/upgrade_dao_direct_dev.sh b/scripts/upgrade_dao_direct_testnet.sh similarity index 93% rename from scripts/upgrade_dao_direct_dev.sh rename to scripts/upgrade_dao_direct_testnet.sh index ea8df3068..9d6ef6201 100755 --- a/scripts/upgrade_dao_direct_dev.sh +++ b/scripts/upgrade_dao_direct_testnet.sh @@ -15,16 +15,12 @@ export NEAR_ENV=testnet export FACTORY=testnet if [ -z ${NEAR_ACCT+x} ]; then - # export NEAR_ACCT=sputnikv2.$FACTORY - export NEAR_ACCT=sputnikpm.$FACTORY + export NEAR_ACCT=sputnikv2.$FACTORY else export NEAR_ACCT=$NEAR_ACCT fi -# export FACTORY_ACCOUNT_ID=sputnikv2.$FACTORY -export FACTORY_ACCOUNT_ID=factory13.$NEAR_ACCT -# export DAO_ACCOUNT_ID=croncat.sputnikv2.$FACTORY -# export DAO_ACCOUNT_ID=sputnikdao-dev-v2-1645228499.factory3.sputnikpm.testnet +export FACTORY_ACCOUNT_ID=sputnikv2.$FACTORY export MAX_GAS=300000000000000 export GAS_100_TGAS=100000000000000 export GAS_150_TGAS=150000000000000 diff --git a/scripts/upgrade_dao_proposal_mainnet.sh b/scripts/upgrade_dao_proposal_mainnet.sh new file mode 100755 index 000000000..5c9f17904 --- /dev/null +++ b/scripts/upgrade_dao_proposal_mainnet.sh @@ -0,0 +1,117 @@ +#!/bin/bash +set -e + +# build the things +./build.sh + +export NEAR_ENV=mainnet +export FACTORY=near + +if [ -z ${NEAR_ACCT+x} ]; then + export NEAR_ACCT=sputnik-dao.$FACTORY +else + export NEAR_ACCT=$NEAR_ACCT +fi + +export FACTORY_ACCOUNT_ID=sputnik-dao.$FACTORY +# export FACTORY_ACCOUNT_ID=subfactory.$NEAR_ACCT +export MAX_GAS=300000000000000 +export GAS_100_TGAS=100000000000000 +export GAS_150_TGAS=150000000000000 +export GAS_220_TGAS=220000000000000 +BOND_AMOUNT=1 +BYTE_STORAGE_COST=6000000000000000000000000 +COMMIT_V3=640495ba572345ca356376989738fbd5462e1ff8 +V3_CODE_HASH=783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 + +# IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!!!!! +# Change this to YOUR dao +DAO_ACCOUNT_ID=sputnikdao-.$FACTORY_ACCOUNT_ID +# ALSO!!!!!!!!!!!! +# CHANGE ALL THE proposal IDs!!!!! Your DAO could have other proposals, you need to change to use the next ID + + +#### -------------------------------------------- +#### Sanity check the new metadata & DAO +#### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_contracts_metadata +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Upgradeable DAO Proposal +#### -------------------------------------------- +# 783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 + +UPGRADE_PROPOSAL_ARGS=`echo '{"code_hash":"783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5"}' | base64` +# propose UpgradeSelf using the code_hash from store_blob +near call $DAO_ACCOUNT_ID add_proposal '{ + "proposal": { + "description": "Upgrade to v3 DAO code using upgrade contract via factory", + "kind": { + "FunctionCall": { + "receiver_id": "'$FACTORY_ACCOUNT_ID'", + "actions": [ + { + "method_name": "store_contract_self", + "args": "'$UPGRADE_PROPOSAL_ARGS'", + "deposit": "'$BYTE_STORAGE_COST'", + "gas": "'$GAS_220_TGAS'" + } + ] + } + } + } +}' --accountId $NEAR_ACCT --amount $BOND_AMOUNT --gas $MAX_GAS +# approve +near call $DAO_ACCOUNT_ID act_proposal '{"id": 1, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DAO_ACCOUNT_ID get_proposal '{"id": 1}' +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Upgrade a v2 DAO to v3 +#### -------------------------------------------- +# 783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 +echo "Upgrade V3 CODE HASH: $V3_CODE_HASH" +# some sample payouts +near call $DAO_ACCOUNT_ID add_proposal '{"proposal": { "description": "Upgrade to v3", "kind": { "UpgradeSelf": { "hash": "'$V3_CODE_HASH'" } } } }' --accountId $NEAR_ACCT --amount 1 +# approve some, leave some +near call $DAO_ACCOUNT_ID act_proposal '{"id": 2, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DAO_ACCOUNT_ID get_proposal '{"id": 2}' +#### -------------------------------------------- + +#### -------------------------------------------- +#### Remove cached blob DAO Proposal +#### -------------------------------------------- +# 783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 + +REMOVE_PROPOSAL_ARGS=`echo '{"code_hash":"783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5"}' | base64` +# propose UpgradeSelf using the code_hash from store_blob +near call $DAO_ACCOUNT_ID add_proposal '{ + "proposal": { + "description": "Remove DAO upgrade contract local code blob via factory", + "kind": { + "FunctionCall": { + "receiver_id": "'$FACTORY_ACCOUNT_ID'", + "actions": [ + { + "method_name": "remove_contract_self", + "args": "'$REMOVE_PROPOSAL_ARGS'", + "deposit": "0", + "gas": "'$GAS_220_TGAS'" + } + ] + } + } + } +}' --accountId $NEAR_ACCT --amount $BOND_AMOUNT --gas $MAX_GAS +# approve +near call $DAO_ACCOUNT_ID act_proposal '{"id": 3, "action" :"VoteApprove"}' --accountId $NEAR_ACCT --gas $MAX_GAS +# quick check all is good +near view $DAO_ACCOUNT_ID get_proposal '{"id": 3}' +#### -------------------------------------------- + +echo "Mainnet DAO Upgrade Complete" diff --git a/scripts/upgrade_dao_proposal_dev.sh b/scripts/upgrade_dao_proposal_testnet.sh similarity index 90% rename from scripts/upgrade_dao_proposal_dev.sh rename to scripts/upgrade_dao_proposal_testnet.sh index c6e3fc875..11cd38272 100755 --- a/scripts/upgrade_dao_proposal_dev.sh +++ b/scripts/upgrade_dao_proposal_testnet.sh @@ -9,10 +9,6 @@ #### -------------------------------------------- set -e -# # TODO: Change to the official approved commit: -# COMMIT_V3=596f27a649c5df3310e945a37a41a957492c0322 -# # git checkout $COMMIT_V3 - # build the things ./build.sh @@ -20,23 +16,24 @@ export NEAR_ENV=testnet export FACTORY=testnet if [ -z ${NEAR_ACCT+x} ]; then - # export NEAR_ACCT=sputnikv2.$FACTORY - export NEAR_ACCT=sputnikpm.$FACTORY + export NEAR_ACCT=sputnikv2.$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 FACTORY_ACCOUNT_ID=sputnikv2.$FACTORY export MAX_GAS=300000000000000 export GAS_100_TGAS=100000000000000 export GAS_150_TGAS=150000000000000 export GAS_220_TGAS=220000000000000 BOND_AMOUNT=1 BYTE_STORAGE_COST=6000000000000000000000000 -COMMIT_V3=596f27a649c5df3310e945a37a41a957492c0322 -V3_CODE_HASH=FRc1X7yrgGEnjVEauMMuPTQJmzDdp3ZDfxjomkrLexzq +COMMIT_V2=c2cf1553b070d04eed8f659571440b27d398c588 +V2_CODE_HASH=8RMeZ5cXDap6TENxaJKtigRYf3n139iHmTRe8ZUNey6N +COMMIT_V2A=UNOFFICIAL_SCRIPT_DATA +V2A_CODE_HASH=8LN56HLNjvwtiNb6pRVNSTMtPgJYqGjAgkVSHRiK5Wfv +COMMIT_V3=640495ba572345ca356376989738fbd5462e1ff8 +V3_CODE_HASH=783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 # #### -------------------------------------------- @@ -137,9 +134,9 @@ near view $FACTORY_ACCOUNT_ID get_contracts_metadata #### -------------------------------------------- #### Upgradeable DAO Proposal #### -------------------------------------------- -# FRc1X7yrgGEnjVEauMMuPTQJmzDdp3ZDfxjomkrLexzq +# 783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 -UPGRADE_PROPOSAL_ARGS=`echo '{"code_hash":"FRc1X7yrgGEnjVEauMMuPTQJmzDdp3ZDfxjomkrLexzq"}' | base64` +UPGRADE_PROPOSAL_ARGS=`echo '{"code_hash":"783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5"}' | base64` # propose UpgradeSelf using the code_hash from store_blob near call $UPGRDADEME_ACCOUNT add_proposal '{ "proposal": { @@ -169,7 +166,7 @@ near view $UPGRDADEME_ACCOUNT get_proposal '{"id": 0}' #### -------------------------------------------- #### Upgradeable DAO Proposal #### -------------------------------------------- -V3_CODE_HASH=FRc1X7yrgGEnjVEauMMuPTQJmzDdp3ZDfxjomkrLexzq +V3_CODE_HASH=783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 # propose UpgradeSelf using the code_hash from store_blob near call $UPGRDADEME_ACCOUNT add_proposal '{ "proposal": { @@ -192,9 +189,9 @@ near view $UPGRDADEME_ACCOUNT get_proposal '{"id": 1}' #### -------------------------------------------- #### Remove cached blob DAO Proposal #### -------------------------------------------- -# FRc1X7yrgGEnjVEauMMuPTQJmzDdp3ZDfxjomkrLexzq +# 783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 -REMOVE_PROPOSAL_ARGS=`echo '{"code_hash":"FRc1X7yrgGEnjVEauMMuPTQJmzDdp3ZDfxjomkrLexzq"}' | base64` +REMOVE_PROPOSAL_ARGS=`echo '{"code_hash":"783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5"}' | base64` # propose UpgradeSelf using the code_hash from store_blob near call $UPGRDADEME_ACCOUNT add_proposal '{ "proposal": { diff --git a/scripts/upgrade_fa_dao_direct_mainnet.sh b/scripts/upgrade_fa_dao_direct_mainnet.sh new file mode 100755 index 000000000..0355e3206 --- /dev/null +++ b/scripts/upgrade_fa_dao_direct_mainnet.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -e + +# build the things +./build.sh + +export NEAR_ENV=mainnet +export FACTORY=near + +if [ -z ${NEAR_ACCT+x} ]; then + export NEAR_ACCT=sputnik-dao.$FACTORY +else + export NEAR_ACCT=$NEAR_ACCT +fi + +export FACTORY_ACCOUNT_ID=sputnik-dao.$FACTORY +export MAX_GAS=300000000000000 + +# Create a backup of signer key just so we can be paranoid and careful +cp ~/.near-credentials/$NEAR_ENV/$NEAR_ACCT.json ~/.near-credentials/$NEAR_ENV/$NEAR_ACCT-backup.json + +# Loop All accounts and deploy v2 gas fixes +# NOTE: If this fails, the full access key could be wrong. +# NOTE: If this fails, the account might not have enough funds for new code storage. +accounts=("collabs" "wiki" "openshards" "codame" "pixeltoken" "curators" "multicall" "marmaj" "hype" "mochi" "news" "famjam" "genesis" "hak" "peter" "nearweek" "thekindao" "shrm" "skyward" "metapool" "prod_dev" "pulse" "stardust" "city-nodes" "audit" "simplegames" "auctionhouse" "wyosky" "grain-lang" "jascha" "swarming" "roketo" "rarity" "information" "millionaire-raccoons-dao" "aurora" "mindful" "maximum-viable-potential" "raritydao" "pulse-markets" "nearprotocoltamil" "terrans" "now-fund-this" "rucommunity" "cpgtest" "nyc-sports" "learnnear" "lisboa-node" "jinn" "transform" "art" "nearsighted" "kindessgrocerycoop" "devco" "hfq" "catalygraphy" "nftbuzz" "yaway" "localmakermart" "nihilism_fulltime" "flymoon" "blackvirtualmap" "abra" "nft") +for (( e=0; e<=${#accounts[@]} - 1; e++ )) +do + DAO_ACCOUNT_ID="${accounts[e]}.${FACTORY_ACCOUNT_ID}" + echo "Upgrading: ${DAO_ACCOUNT_ID}" + + # Copy the signing key temporarily so near-cli can get tricked into the right signer + cp ~/.near-credentials/$NEAR_ENV/$NEAR_ACCT.json ~/.near-credentials/$NEAR_ENV/$DAO_ACCOUNT_ID.json + + # FOR DEPLOYING v2 with the gas fix + near deploy --wasmFile sputnikdao2-gasfix/res/sputnikdao2_gasfix.wasm --accountId $DAO_ACCOUNT_ID --initGas $MAX_GAS --force + # # FOR DEPLOYING v3 directly (SHOULD BE AVOIDED IF POSSIBLE!) + # near deploy --wasmFile sputnikdao2/res/sputnikdao2.wasm --accountId $FACTORY_ACCOUNT_ID --initGas $MAX_GAS --force + + echo "Deployed ${DAO_ACCOUNT_ID}: Go to https://explorer.near.org/accounts/${DAO_ACCOUNT_ID} and check the code_hash" + + # Remove the dao account fake signer key + rm ~/.near-credentials/$NEAR_ENV/$DAO_ACCOUNT_ID.json +done diff --git a/scripts/upgrade_fa_dao_direct_testnet.sh b/scripts/upgrade_fa_dao_direct_testnet.sh new file mode 100755 index 000000000..50dc22754 --- /dev/null +++ b/scripts/upgrade_fa_dao_direct_testnet.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -e + +# build the things +./build.sh + +export NEAR_ENV=testnet +export FACTORY=testnet + +if [ -z ${NEAR_ACCT+x} ]; then + export NEAR_ACCT=sputnikv2.$FACTORY +else + export NEAR_ACCT=$NEAR_ACCT +fi + +export FACTORY_ACCOUNT_ID=sputnikv2.$FACTORY +export MAX_GAS=300000000000000 + +# Create a backup of signer key just so we can be paranoid and careful +cp ~/.near-credentials/$NEAR_ENV/$NEAR_ACCT.json ~/.near-credentials/$NEAR_ENV/$NEAR_ACCT-backup.json + +# Loop All accounts and deploy v2 gas fixes +# NOTE: If this fails, the full access key could be wrong. +# NOTE: If this fails, the account might not have enough funds for new code storage. +accounts=("nearnear" "dodo") +for (( e=0; e<=${#accounts[@]} - 1; e++ )) +do + DAO_ACCOUNT_ID="${accounts[e]}.${FACTORY_ACCOUNT_ID}" + echo "Upgrading: ${DAO_ACCOUNT_ID}" + + # Copy the signing key temporarily so near-cli can get tricked into the right signer + cp ~/.near-credentials/$NEAR_ENV/$NEAR_ACCT.json ~/.near-credentials/$NEAR_ENV/$DAO_ACCOUNT_ID.json + + # FOR DEPLOYING v2 with the gas fix + near deploy --wasmFile sputnikdao2-gasfix/res/sputnikdao2_gasfix.wasm --accountId $DAO_ACCOUNT_ID --initGas $MAX_GAS --force + # # FOR DEPLOYING v3 directly (SHOULD BE AVOIDED IF POSSIBLE!) + # near deploy --wasmFile sputnikdao2/res/sputnikdao2.wasm --accountId $FACTORY_ACCOUNT_ID --initGas $MAX_GAS --force + + echo "Deployed ${DAO_ACCOUNT_ID}: Go to https://explorer.testnet.near.org/accounts/${DAO_ACCOUNT_ID} and check the code_hash" + + # Remove the dao account fake signer key + rm ~/.near-credentials/$NEAR_ENV/$DAO_ACCOUNT_ID.json +done diff --git a/scripts/upgrade_factory_mainnet.sh b/scripts/upgrade_factory_mainnet.sh new file mode 100755 index 000000000..707e09dc6 --- /dev/null +++ b/scripts/upgrade_factory_mainnet.sh @@ -0,0 +1,115 @@ +#!/bin/bash +set -e + +# build the things +# ./build.sh + +export NEAR_ENV=mainnet +export FACTORY=near + +if [ -z ${NEAR_ACCT+x} ]; then + export NEAR_ACCT=sputnik-dao.$FACTORY +else + export NEAR_ACCT=$NEAR_ACCT +fi + +export FACTORY_ACCOUNT_ID=$NEAR_ACCT +export DAO_ACCOUNT_ID=genesis.$FACTORY_ACCOUNT_ID +export MAX_GAS=300000000000000 +export GAS_100_TGAS=100000000000000 +export GAS_150_TGAS=150000000000000 +BOND_AMOUNT=1 +BYTE_STORAGE_COST=6000000000000000000000000 +COMMIT_V2=c2cf1553b070d04eed8f659571440b27d398c588 +V2_CODE_HASH=8RMeZ5cXDap6TENxaJKtigRYf3n139iHmTRe8ZUNey6N +COMMIT_V2A=UNOFFICIAL_SCRIPT_DATA +V2A_CODE_HASH=8LN56HLNjvwtiNb6pRVNSTMtPgJYqGjAgkVSHRiK5Wfv +COMMIT_V3=640495ba572345ca356376989738fbd5462e1ff8 +V3_CODE_HASH=783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 + + +#### -------------------------------------------- +#### Grab the DAO v2 code data +#### -------------------------------------------- +http --json post https://rpc.mainnet.near.org jsonrpc=2.0 id=dontcare method=query \ +params:='{"request_type":"view_code","finality":"final","account_id":"'$DAO_ACCOUNT_ID'"}' \ +| jq -r .result.code_base64 \ +| base64 --decode > sputnikdao2_original.wasm +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Upgrade the factory +#### NOTE: Make sure you've built on the right commit! +#### -------------------------------------------- +near deploy --wasmFile sputnikdao-factory2/res/sputnikdao_factory2.wasm --accountId $FACTORY_ACCOUNT_ID --force +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### DAO v2 code data, store it in factory +#### -------------------------------------------- +# Store the code data +V2_BYTES='cat sputnikdao2_original.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V2_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v2_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V2_CODE_HASH=$(eval "tail -1 v2_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V2 CODE HASH: $V2_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V2_CODE_HASH'", "metadata": {"version": [2,0], "commit_id": "'$COMMIT_V2'"}, "set_default": false}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Get DAO v2a code data & store it in factory +#### Keep this around for gas-fixes version +#### NOTE: This doesnt really fix the upgrade path post neard 1.26.0 - those v2 DAOs will be stuck +#### -------------------------------------------- +# Store the code data +V2A_BYTES='cat sputnikdao2-gasfix/res/sputnikdao2_gasfix.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V2A_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v2a_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V2A_CODE_HASH=$(eval "tail -1 v2a_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V2A CODE HASH: $V2A_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V2A_CODE_HASH'", "metadata": {"version": [2,1], "commit_id": "'$COMMIT_V2A'"}, "set_default": false}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Get DAO v3 code data & store it in factory +#### -------------------------------------------- +# Store the code data +V3_BYTES='cat sputnikdao2/res/sputnikdao2.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V3_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v3_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V3_CODE_HASH=$(eval "tail -1 v3_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V3 CODE HASH: $V3_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V3_CODE_HASH'", "metadata": {"version": [3,0], "commit_id": "'$COMMIT_V3'"}, "set_default": true}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Sanity check the new metadata +#### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_contracts_metadata +near view $FACTORY_ACCOUNT_ID get_dao_list +#### -------------------------------------------- + + +# #### -------------------------------------------- +# cleanup local files! +# #### -------------------------------------------- +rm sputnikdao2_original.wasm +rm sputnikdao_factory2_original.wasm +rm v2_code_hash_result.txt +rm v2a_code_hash_result.txt +rm v3_code_hash_result.txt + +echo "MAINNET: Factory Upgrade Complete" diff --git a/scripts/upgrade_factory_testnet.sh b/scripts/upgrade_factory_testnet.sh new file mode 100755 index 000000000..914035d07 --- /dev/null +++ b/scripts/upgrade_factory_testnet.sh @@ -0,0 +1,115 @@ +#!/bin/bash +set -e + +# build the things +./build.sh + +export NEAR_ENV=testnet +export FACTORY=testnet + +if [ -z ${NEAR_ACCT+x} ]; then + export NEAR_ACCT=sputnikv2.$FACTORY +else + export NEAR_ACCT=$NEAR_ACCT +fi + +export FACTORY_ACCOUNT_ID=sputnikv2.$NEAR_ACCT +export DAO_ACCOUNT_ID=genesis.$FACTORY_ACCOUNT_ID +export MAX_GAS=300000000000000 +export GAS_100_TGAS=100000000000000 +export GAS_150_TGAS=150000000000000 +BOND_AMOUNT=1 +BYTE_STORAGE_COST=6000000000000000000000000 +COMMIT_V2=c2cf1553b070d04eed8f659571440b27d398c588 +V2_CODE_HASH=8RMeZ5cXDap6TENxaJKtigRYf3n139iHmTRe8ZUNey6N +COMMIT_V2A=UNOFFICIAL_SCRIPT_DATA +V2A_CODE_HASH=8LN56HLNjvwtiNb6pRVNSTMtPgJYqGjAgkVSHRiK5Wfv +COMMIT_V3=640495ba572345ca356376989738fbd5462e1ff8 +V3_CODE_HASH=783vth3Fg8MBBGGFmRqrytQCWBpYzUcmHoCq4Mo8QqF5 + + +#### -------------------------------------------- +#### Grab the DAO v2 code data & store it in factory +#### -------------------------------------------- +http --json post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=query \ +params:='{"request_type":"view_code","finality":"final","account_id":"'$DAO_ACCOUNT_ID'"}' \ +| jq -r .result.code_base64 \ +| base64 --decode > sputnikdao2_original.wasm +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Upgrade the factory +#### NOTE: Make sure you've built on the right commit! +#### -------------------------------------------- +near deploy --wasmFile sputnikdao-factory2/res/sputnikdao_factory2.wasm --accountId $FACTORY_ACCOUNT_ID --force +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Grab the DAO v2 code data & store it in factory +#### -------------------------------------------- +# Store the code data +V2_BYTES='cat sputnikdao2_original.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V2_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v2_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V2_CODE_HASH=$(eval "tail -1 v2_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V2 CODE HASH: $V2_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V2_CODE_HASH'", "metadata": {"version": [2,0], "commit_id": "'$COMMIT_V2'"}, "set_default": false}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Get DAO v2a code data & store it in factory +#### Keep this around for gas-fixes version +#### NOTE: This doesnt really fix the upgrade path post neard 1.26.0 - those v2 DAOs will be stuck +#### -------------------------------------------- +# Store the code data +V2A_BYTES='cat sputnikdao2-gasfix/res/sputnikdao2-gasfix.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V2A_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v2a_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V2A_CODE_HASH=$(eval "tail -1 v2a_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V2A CODE HASH: $V2A_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V2A_CODE_HASH'", "metadata": {"version": [2,1], "commit_id": "'$COMMIT_V2A'"}, "set_default": false}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + +#### -------------------------------------------- +#### Get DAO v3 code data & store it in factory +#### -------------------------------------------- +# Store the code data +V3_BYTES='cat sputnikdao2/res/sputnikdao2.wasm | base64' +near call $FACTORY_ACCOUNT_ID store $(eval "$V3_BYTES") --base64 --accountId $FACTORY_ACCOUNT_ID --gas $MAX_GAS --amount 10 > v3_code_hash_result.txt + +# Update the factory metadata +# Get the response code hash! +V3_CODE_HASH=$(eval "tail -1 v3_code_hash_result.txt | sed 's/^.//;s/.$//'") +echo "V3 CODE HASH: $V3_CODE_HASH" +near call $FACTORY_ACCOUNT_ID store_contract_metadata '{"code_hash": "'$V3_CODE_HASH'", "metadata": {"version": [3,0], "commit_id": "'$COMMIT_V3'"}, "set_default": true}' --accountId $FACTORY_ACCOUNT_ID +#### -------------------------------------------- + + + +#### -------------------------------------------- +#### Sanity check the new metadata +#### -------------------------------------------- +near view $FACTORY_ACCOUNT_ID get_contracts_metadata +near view $FACTORY_ACCOUNT_ID get_dao_list +#### -------------------------------------------- + + +# #### -------------------------------------------- +# cleanup local files! +# #### -------------------------------------------- +rm sputnikdao2_original.wasm +rm sputnikdao_factory2_original.wasm +rm v2_code_hash_result.txt +rm v2a_code_hash_result.txt +rm v3_code_hash_result.txt + +echo "TESTNET: Factory Upgrade Complete" \ No newline at end of file diff --git a/sputnik-staking/res/sputnik_staking.wasm b/sputnik-staking/res/sputnik_staking.wasm index 5d7052227..0311b3633 100755 Binary files a/sputnik-staking/res/sputnik_staking.wasm and b/sputnik-staking/res/sputnik_staking.wasm differ diff --git a/sputnikdao-factory2/Cargo.toml b/sputnikdao-factory2/Cargo.toml index 7695d0913..a8001aa07 100644 --- a/sputnikdao-factory2/Cargo.toml +++ b/sputnikdao-factory2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sputnikdao-factory2" -version = "0.2.0" +version = "0.2.1" authors = ["Illia Polosukhin "] edition = "2018" publish = false diff --git a/sputnikdao-factory2/build_docker.sh b/sputnikdao-factory2/build_docker.sh new file mode 100755 index 000000000..fa645c311 --- /dev/null +++ b/sputnikdao-factory2/build_docker.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Exit script as soon as a command fails. +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +NAME="build_sputnik_factory" + +if docker ps -a --format '{{.Names}}' | grep -Eq "^${NAME}\$"; then + echo "Container exists" +else +docker create \ + --mount type=bind,source=$DIR/..,target=/host \ + --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ + --name=$NAME \ + -w /host/sputnikdao-factory2 \ + -e RUSTFLAGS='-C link-arg=-s' \ + -it \ + nearprotocol/contract-builder \ + /bin/bash +fi + +docker start $NAME +docker exec -it $NAME /bin/bash -c "rustup toolchain install 1.56.0; rustup default 1.56.0; rustup target add wasm32-unknown-unknown; cargo build --target wasm32-unknown-unknown --release" + +mkdir -p res +cp $DIR/../target/wasm32-unknown-unknown/release/sputnikdao_factory2.wasm $DIR/res/sputnikdao_factory2.wasm + diff --git a/sputnikdao-factory2/res/sputnikdao_factory2.wasm b/sputnikdao-factory2/res/sputnikdao_factory2.wasm index e11c55200..8fcc3908e 100755 Binary files a/sputnikdao-factory2/res/sputnikdao_factory2.wasm and b/sputnikdao-factory2/res/sputnikdao_factory2.wasm differ diff --git a/sputnikdao-factory2/res/sputnikdao_factory2_v_2_0.wasm b/sputnikdao-factory2/res/sputnikdao_factory2_v_2_0.wasm new file mode 100755 index 000000000..2f8dac676 Binary files /dev/null and b/sputnikdao-factory2/res/sputnikdao_factory2_v_2_0.wasm differ diff --git a/sputnikdao-factory2/src/factory_manager.rs b/sputnikdao-factory2/src/factory_manager.rs index 406233e7b..3c9152adf 100644 --- a/sputnikdao-factory2/src/factory_manager.rs +++ b/sputnikdao-factory2/src/factory_manager.rs @@ -15,6 +15,10 @@ const ON_CREATE_CALL_GAS: Gas = Gas(10_000_000_000_000); /// Leftover gas after creating promise and calling update. const GAS_UPDATE_LEFTOVER: Gas = Gas(10_000_000_000_000); +/// Since Nightshade V2, the send_not_sir of action_function_call_per_byte increase to this value, please refer to: +/// https://github.com/near/nearcore/blob/0c2374993fc74b57faf2bcdf5c7c73a37e82b75a/core/parameters/res/runtime_configs/parameters.snap#L52 +pub const GAS_FUNCTION_CALL_PER_BYTE: u64 = 47_683_715; + const NO_DEPOSIT: Balance = 0; /// Factory manager that allows to store/load contracts by hash directly in the storage. @@ -68,6 +72,7 @@ impl FactoryManager { assert!(env::storage_has_key(&code_hash), "Contract doesn't exist"); // Load the hash from storage. let code = env::storage_read(&code_hash).expect("ERR_NO_HASH"); + let wasm_argument_gas = Gas(code.len() as u64 * GAS_FUNCTION_CALL_PER_BYTE); // Create a promise toward given account. let promise_id = env::promise_batch_create(&account_id); // Call `update` method, which should also handle migrations. @@ -76,7 +81,7 @@ impl FactoryManager { method_name, &code, NO_DEPOSIT, - env::prepaid_gas() - env::used_gas() - GAS_UPDATE_LEFTOVER, + env::prepaid_gas() - env::used_gas() - GAS_UPDATE_LEFTOVER - wasm_argument_gas, ); env::promise_return(promise_id); } diff --git a/sputnikdao-factory2/src/lib.rs b/sputnikdao-factory2/src/lib.rs index b1f028e1f..6cf7cb477 100644 --- a/sputnikdao-factory2/src/lib.rs +++ b/sputnikdao-factory2/src/lib.rs @@ -23,7 +23,7 @@ const DAO_CONTRACT_NO_DATA: &str = "no data"; // Gas & Costs for blob storage 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 ON_REMOVE_CONTRACT_GAS: Gas = Gas(20_000_000_000_000); const NO_DEPOSIT: Balance = 0; #[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize)] diff --git a/sputnikdao2/Cargo.toml b/sputnikdao2/Cargo.toml index 8f8c9b5c1..41a7a4f3b 100644 --- a/sputnikdao2/Cargo.toml +++ b/sputnikdao2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sputnikdao2" -version = "2.0.0" +version = "2.3.1" authors = ["Sputnik Devs "] edition = "2018" publish = false diff --git a/sputnikdao2/build_docker.sh b/sputnikdao2/build_docker.sh new file mode 100755 index 000000000..60a219f08 --- /dev/null +++ b/sputnikdao2/build_docker.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Exit script as soon as a command fails. +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +NAME="build_sputnik" + +if docker ps -a --format '{{.Names}}' | grep -Eq "^${NAME}\$"; then + echo "Container exists" +else +docker create \ + --mount type=bind,source=$DIR/..,target=/host \ + --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ + --name=$NAME \ + -w /host/sputnikdao2 \ + -e RUSTFLAGS='-C link-arg=-s' \ + -it \ + nearprotocol/contract-builder \ + /bin/bash +fi + +docker start $NAME +docker exec -it $NAME /bin/bash -c "rustup toolchain install 1.56.0; rustup default 1.56.0; rustup target add wasm32-unknown-unknown; cargo build --target wasm32-unknown-unknown --release" + +mkdir -p res +cp $DIR/../target/wasm32-unknown-unknown/release/sputnikdao2.wasm $DIR/res/sputnikdao2.wasm + diff --git a/sputnikdao2/package-lock.json b/sputnikdao2/package-lock.json new file mode 100644 index 000000000..62a010201 --- /dev/null +++ b/sputnikdao2/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "sputnikdao2", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/sputnikdao2/res/sputnikdao2.wasm b/sputnikdao2/res/sputnikdao2.wasm index c64845ff8..27ac48a5c 100755 Binary files a/sputnikdao2/res/sputnikdao2.wasm and b/sputnikdao2/res/sputnikdao2.wasm differ diff --git a/sputnikdao2/res/sputnikdao2_v_3_0.wasm b/sputnikdao2/res/sputnikdao2_v_3_0.wasm new file mode 100755 index 000000000..5f604fe0b Binary files /dev/null and b/sputnikdao2/res/sputnikdao2_v_3_0.wasm differ diff --git a/sputnikdao2/src/upgrade.rs b/sputnikdao2/src/upgrade.rs index 74323bd3b..a01fa24ee 100644 --- a/sputnikdao2/src/upgrade.rs +++ b/sputnikdao2/src/upgrade.rs @@ -11,8 +11,11 @@ const UPDATE_GAS_LEFTOVER: Gas = Gas(10_000_000_000_000); const FACTORY_UPDATE_GAS_LEFTOVER: Gas = Gas(15_000_000_000_000); const NO_DEPOSIT: Balance = 0; -pub const GAS_FOR_UPGRADE_SELF_DEPLOY: Gas = Gas(15_000_000_000_000); -pub const GAS_FOR_UPGRADE_REMOTE_DEPLOY: Gas = Gas(15_000_000_000_000); +pub const GAS_FOR_UPGRADE_SELF_PROMISE_CREATION: Gas = Gas(15_000_000_000_000); +pub const GAS_FOR_UPGRADE_REMOTE_PROMISE_CREATION: Gas = Gas(15_000_000_000_000); +/// Since Nightshade V2, the send_not_sir of action_function_call_per_byte increase to this value, please refer to: +/// https://github.com/near/nearcore/blob/0c2374993fc74b57faf2bcdf5c7c73a37e82b75a/core/parameters/res/runtime_configs/parameters.snap#L52 +pub const GAS_FUNCTION_CALL_PER_BYTE: u64 = 47_683_715; /// Info about factory that deployed this contract and if auto-update is allowed. #[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize)] @@ -123,18 +126,19 @@ pub(crate) fn upgrade_self(hash: &[u8]) { "migrate", &[], NO_DEPOSIT, - env::prepaid_gas() - env::used_gas() - GAS_FOR_UPGRADE_SELF_DEPLOY, + env::prepaid_gas() - env::used_gas() - GAS_FOR_UPGRADE_SELF_PROMISE_CREATION, ); } pub(crate) fn upgrade_remote(receiver_id: &AccountId, method_name: &str, hash: &[u8]) { let input = env::storage_read(hash).expect("ERR_NO_HASH"); let promise_id = env::promise_batch_create(receiver_id); + let wasm_argument_gas = Gas(input.len() as u64 * GAS_FUNCTION_CALL_PER_BYTE); env::promise_batch_action_function_call( promise_id, method_name, &input, NO_DEPOSIT, - env::prepaid_gas() - env::used_gas() - GAS_FOR_UPGRADE_REMOTE_DEPLOY, + env::prepaid_gas() - env::used_gas() - GAS_FOR_UPGRADE_REMOTE_PROMISE_CREATION - wasm_argument_gas, ); } diff --git a/sputnikdao2/tests-ava/README.md b/sputnikdao2/tests-ava/README.md index ed6210b13..f9ec2aee4 100644 --- a/sputnikdao2/tests-ava/README.md +++ b/sputnikdao2/tests-ava/README.md @@ -1,12 +1,11 @@ -These tests use [near-workspaces-ava](https://github.com/near/workspaces-js/tree/main/packages/ava): delightful, deterministic local testing for NEAR smart contracts. +These tests use [near-workspaces](https://github.com/near/near-workspaces-js): delightful, deterministic local testing for NEAR smart contracts. You will need to install [NodeJS](https://nodejs.dev/). Then you can use the `scripts` defined in [package.json](./package.json): npm run test -If you want to run `near-workspaces-ava` or `ava` directly, you can use [npx](https://nodejs.dev/learn/the-npx-nodejs-package-runner): +If you want to run `ava` directly, you can use [npx](https://nodejs.dev/learn/the-npx-nodejs-package-runner): - npx near-workspaces-ava --help npx ava --help To run only one test file: diff --git a/sputnikdao2/tests-ava/__tests__/bounties.ava.ts b/sputnikdao2/tests-ava/__tests__/bounties.ava.ts index 6cc5cca89..974386f4c 100644 --- a/sputnikdao2/tests-ava/__tests__/bounties.ava.ts +++ b/sputnikdao2/tests-ava/__tests__/bounties.ava.ts @@ -1,5 +1,4 @@ import { - Workspace, BN, NearAccount, captureError, @@ -7,11 +6,12 @@ import { tGas, ONE_NEAR, NEAR, -} from 'near-workspaces-ava'; +} from 'near-workspaces'; import { - workspace, initStaking, initTestToken, + initWorkspace, + Proposal, STORAGE_PER_BYTE, } from './utils'; import { @@ -27,25 +27,29 @@ import { voteApprove, } from './utils'; -workspace.test('Bounty workflow', async (test, { alice, root, dao }) => { +const test = initWorkspace(); + +test('Bounty workflow', async (t) => { + const { alice, root, dao } = t.context.accounts; const testToken = await initTestToken(root); const proposalId = await proposeBounty(alice, dao, testToken); await voteOnBounty(root, dao, proposalId); await claimBounty(alice, dao, proposalId); const proposal = await dao.view('get_bounty_claims', { account_id: alice }); - test.log('Claims before bounty_done:'); - test.log(await dao.view('get_bounty_claims', { account_id: alice })); + t.log('Claims before bounty_done:'); + t.log(await dao.view('get_bounty_claims', { account_id: alice })); await doneBounty(alice, alice, dao, proposalId); - test.log('Claims after bounty_done:'); - test.log(await dao.view('get_bounty_claims', { account_id: alice })); - test.log('The proposal before act_proposal, voting on the bounty:'); - test.log(await dao.view('get_proposal', { id: proposalId + 1 })); + t.log('Claims after bounty_done:'); + t.log(await dao.view('get_bounty_claims', { account_id: alice })); + t.log('The proposal before act_proposal, voting on the bounty:'); + t.log(await dao.view('get_proposal', { id: proposalId + 1 })); await voteOnBounty(root, dao, proposalId + 1); - test.log('The proposal after act_proposal, voting on the bounty:'); - test.log(await dao.view('get_proposal', { id: proposalId + 1 })); + t.log('The proposal after act_proposal, voting on the bounty:'); + t.log(await dao.view('get_proposal', { id: proposalId + 1 })); }); -workspace.test('Bounty claim', async (test, { alice, root, dao }) => { +test('Bounty claim', async (t) => { + const { alice, root, dao } = t.context.accounts; const testToken = await initTestToken(root); const proposalId = await proposeBounty(alice, dao, testToken); @@ -53,7 +57,7 @@ workspace.test('Bounty claim', async (test, { alice, root, dao }) => { let errorString1 = await captureError( async () => await claimBounty(alice, dao, proposalId), ); - test.regex(errorString1, /ERR_NO_BOUNTY/); + t.regex(errorString1, /ERR_NO_BOUNTY/); await voteOnBounty(root, dao, proposalId); @@ -74,7 +78,7 @@ workspace.test('Bounty claim', async (test, { alice, root, dao }) => { }, ), ); - test.regex(errorString2_1, /ERR_BOUNTY_WRONG_BOND/); + t.regex(errorString2_1, /ERR_BOUNTY_WRONG_BOND/); //If we attach less than needed: let errorString2_2 = await captureError( async () => @@ -90,7 +94,7 @@ workspace.test('Bounty claim', async (test, { alice, root, dao }) => { }, ), ); - test.regex(errorString2_2, /ERR_BOUNTY_WRONG_BOND/); + t.regex(errorString2_2, /ERR_BOUNTY_WRONG_BOND/); //Should panic in case of wrong deadline let errorString3 = await captureError( @@ -107,12 +111,12 @@ workspace.test('Bounty claim', async (test, { alice, root, dao }) => { }, ), ); - test.regex(errorString3, /ERR_BOUNTY_WRONG_DEADLINE/); + t.regex(errorString3, /ERR_BOUNTY_WRONG_DEADLINE/); await claimBounty(alice, dao, proposalId); //Should increase number of claims - test.is( + t.is( await dao.view('get_bounty_number_of_claims', { id: proposalId }), 1, ); @@ -121,12 +125,12 @@ workspace.test('Bounty claim', async (test, { alice, root, dao }) => { let bounty: any = await dao.view('get_bounty_claims', { account_id: alice, }); - test.is(bounty[0].bounty_id, 0); - test.is(bounty[0].deadline, DEADLINE); - test.is(bounty[0].completed, false); + t.is(bounty[0].bounty_id, 0); + t.is(bounty[0].deadline, DEADLINE); + t.is(bounty[0].completed, false); await claimBounty(alice, dao, proposalId); - test.is( + t.is( await dao.view('get_bounty_number_of_claims', { id: proposalId }), 2, ); @@ -134,12 +138,12 @@ workspace.test('Bounty claim', async (test, { alice, root, dao }) => { let bounty2: any = await dao.view('get_bounty_claims', { account_id: alice, }); - test.is(bounty2[1].bounty_id, 0); - test.is(bounty2[1].deadline, DEADLINE); - test.is(bounty2[1].completed, false); + t.is(bounty2[1].bounty_id, 0); + t.is(bounty2[1].deadline, DEADLINE); + t.is(bounty2[1].completed, false); await claimBounty(alice, dao, proposalId); - test.is( + t.is( await dao.view('get_bounty_number_of_claims', { id: proposalId }), 3, ); @@ -159,23 +163,24 @@ workspace.test('Bounty claim', async (test, { alice, root, dao }) => { }, ), ); - test.regex(errorString4, /ERR_BOUNTY_ALL_CLAIMED/); + t.regex(errorString4, /ERR_BOUNTY_ALL_CLAIMED/); }); -workspace.test( +test( 'Bounty done with NEAR token', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; const proposalId = await proposeBountyWithNear(alice, dao); await voteOnBounty(root, dao, proposalId); await claimBounty(alice, dao, proposalId); - const bob = await root.createAccount('bob'); + const bob = await root.createSubAccount('bob'); //Should panic if the caller is not in the list of claimers let errorString1 = await captureError( async () => await doneBounty(alice, bob, dao, proposalId), ); - test.regex(errorString1, /ERR_NO_BOUNTY_CLAIMS/); + t.regex(errorString1, /ERR_NO_BOUNTY_CLAIMS/); await claimBounty(bob, dao, proposalId); @@ -184,85 +189,87 @@ workspace.test( let errorString2 = await captureError( async () => await doneBounty(alice, alice, dao, proposalId + 10), ); - test.regex(errorString2, /ERR_NO_BOUNTY_CLAIM/); + t.regex(errorString2, /ERR_NO_BOUNTY_CLAIM/); //`bounty_done` can only be called by the claimer let errorString3 = await captureError( async () => await doneBounty(alice, bob, dao, proposalId), ); - test.regex(errorString3, /ERR_BOUNTY_DONE_MUST_BE_SELF/); + t.regex(errorString3, /ERR_BOUNTY_DONE_MUST_BE_SELF/); let bounty: any = await dao.view('get_bounty_claims', { account_id: alice, }); - test.is(bounty[0].completed, false); + t.is(bounty[0].completed, false); await doneBounty(alice, alice, dao, proposalId); //claim is marked as completed bounty = await dao.view('get_bounty_claims', { account_id: alice }); - test.is(bounty[0].completed, true); + t.is(bounty[0].completed, true); let proposal: any = await dao.view('get_proposal', { id: proposalId + 1, }); - test.is(proposal.status, 'InProgress'); + t.is(proposal.status, 'InProgress'); await voteOnBounty(root, dao, proposalId + 1); //proposal is approved proposal = await dao.view('get_proposal', { id: proposalId + 1 }); - test.is(proposal.status, 'Approved'); + t.is(proposal.status, 'Approved'); //Should panic if the bounty claim is completed let errorString4 = await captureError( async () => await doneBounty(alice, alice, dao, proposalId), ); - test.regex(errorString4, /ERR_NO_BOUNTY_CLAIMS/); + t.regex(errorString4, /ERR_NO_BOUNTY_CLAIMS/); }, ); -workspace.test('Bounty giveup', async (test, { alice, root, dao }) => { +test('Bounty giveup', async (t) => { + const { alice, root, dao } = t.context.accounts; const testToken = await initTestToken(root); const proposalId = await proposeBounty(alice, dao, testToken); await voteOnBounty(root, dao, proposalId); await claimBounty(alice, dao, proposalId); //Should panic if the caller is not in the list of claimers - const bob = await root.createAccount('bob'); + const bob = await root.createSubAccount('bob'); let errorString = await captureError( async () => await giveupBounty(bob, dao, proposalId), ); - test.regex(errorString, /ERR_NO_BOUNTY_CLAIMS/); + t.regex(errorString, /ERR_NO_BOUNTY_CLAIMS/); //Should panic if the list of claims for the caller of the method //doesn't contain the claim with given ID errorString = await captureError( async () => await giveupBounty(alice, dao, proposalId + 10), ); - test.regex(errorString, /ERR_NO_BOUNTY_CLAIM/); + t.regex(errorString, /ERR_NO_BOUNTY_CLAIM/); //If within forgiveness period, `bounty_bond` should be returned ??? const balance1: NEAR = (await alice.balance()).total; const result = await giveupBountyRaw(alice, dao, proposalId); const balance2: NEAR = (await alice.balance()).total; - test.is( + t.is( Number(balance2.add(result.gas_burnt).toHuman().slice(0, -1)).toFixed( 1, ), Number(balance1.add(ONE_NEAR).toHuman().slice(0, -1)).toFixed(1), ); - test.not(balance2, balance1); + t.not(balance2, balance1); //If within forgiveness period, //claim should be removed from the list of claims, done by this account - test.deepEqual( + t.deepEqual( await dao.view('get_bounty_claims', { account_id: alice }), [], ); }); -workspace.test('Bounty ft done', async (test, { alice, root, dao }) => { +test('Bounty ft done', async (t) => { + const { alice, root, dao } = t.context.accounts; const testToken = await initTestToken(root); await dao.call( testToken, @@ -311,8 +318,8 @@ workspace.test('Bounty ft done', async (test, { alice, root, dao }) => { }, ); await voteApprove(root, dao, proposalId); - let { status } = await dao.view('get_proposal', { id: proposalId }); - test.is(status, 'Approved'); + let { status }: Proposal = await dao.view('get_proposal', { id: proposalId }); + t.is(status, 'Approved'); const bountyId = 0; // first bounty await claimBounty(alice, dao, bountyId); await alice.call( @@ -329,32 +336,34 @@ workspace.test('Bounty ft done', async (test, { alice, root, dao }) => { ); await voteApprove(root, dao, proposalId + 1); - ({ status } = await dao.view('get_proposal', { id: proposalId })); - test.is(status, 'Approved'); + ({ status } = await dao.view('get_proposal', { id: proposalId }) as Proposal ); + t.is(status, 'Approved'); }); -workspace.test( +test( 'Callback for BountyDone with NEAR token', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; //During the callback the number bounty_claims_count should decrease const proposalId = await proposeBountyWithNear(alice, dao); await voteOnBounty(root, dao, proposalId); await claimBounty(alice, dao, proposalId); await doneBounty(alice, alice, dao, proposalId); //Before the bounty is done there is 1 claim - test.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); + t.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); const balanceBefore: NEAR = (await alice.balance()).total; //During the callback this number is decreased await voteOnBounty(root, dao, proposalId + 1); const balanceAfter: NEAR = (await alice.balance()).total; - test.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 0); - test.assert(balanceBefore.lt(balanceAfter)); + t.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 0); + t.assert(balanceBefore.lt(balanceAfter)); }, ); -workspace.test( +test( 'Callback for BountyDone ft token fail', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; //Test the callback with Failed proposal status const testTokenFail = await initTestToken(root); const proposalIdFail = await proposeBounty(alice, dao, testTokenFail); @@ -385,16 +394,17 @@ workspace.test( await doneBounty(alice, alice, dao, proposalIdFail); await voteOnBounty(root, dao, proposalIdFail + 1); //Proposal should be Failed - let { status } = await dao.view('get_proposal', { + let { status }: Proposal = await dao.view('get_proposal', { id: proposalIdFail + 1, }); - test.is(status, 'Failed'); + t.is(status, 'Failed'); }, ); -workspace.test( +test( 'Callback for BountyDone ft token', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; //Test correct callback const testToken = await initTestToken(root); await dao.call( @@ -447,18 +457,18 @@ workspace.test( await claimBounty(alice, dao, proposalId); await doneBounty(alice, alice, dao, proposalId); //Before the bounty is done there is 1 claim - test.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); + t.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); const balanceBefore: NEAR = (await alice.balance()).total; //During the callback this number is decreased await voteOnBounty(root, dao, proposalId + 1); //Proposal should be approved - let { status } = await dao.view('get_proposal', { id: proposalId + 1 }); - test.is(status, 'Approved'); + let { status }: Proposal = await dao.view('get_proposal', { id: proposalId + 1 }); + t.is(status, 'Approved'); //During the callback the number bounty_claims_count should decrease const balanceAfter: NEAR = (await alice.balance()).total; - test.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 0); - test.assert(balanceBefore.lt(balanceAfter)); + t.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 0); + t.assert(balanceBefore.lt(balanceAfter)); }, ); diff --git a/sputnikdao2/tests-ava/__tests__/delegation.ava.ts b/sputnikdao2/tests-ava/__tests__/delegation.ava.ts index 28f2c6b6a..b247285f9 100644 --- a/sputnikdao2/tests-ava/__tests__/delegation.ava.ts +++ b/sputnikdao2/tests-ava/__tests__/delegation.ava.ts @@ -4,18 +4,21 @@ import { captureError, toYocto, tGas, -} from 'near-workspaces-ava'; +} from 'near-workspaces'; import { - workspace, initStaking, initTestToken, STORAGE_PER_BYTE, setStakingId, registerAndDelegate, regCost, + initWorkspace, } from './utils'; -workspace.test('Register delegation', async (test, { root, dao, alice }) => { +const test = initWorkspace(); + +test('Register delegation', async (t) => { + const { root, dao, alice } = t.context.accounts; const testToken = await initTestToken(root); const staking = await initStaking(root, dao, testToken); @@ -28,14 +31,15 @@ workspace.test('Register delegation', async (test, { root, dao, alice }) => { let bal: BN = new BN( await dao.view('delegation_balance_of', { account_id: alice }), ); - test.deepEqual(bal, new BN(1)); + t.deepEqual(bal, new BN(1)); const total: BN = new BN(await dao.view('delegation_total_supply')); - test.deepEqual(total, new BN(1)); + t.deepEqual(total, new BN(1)); }); -workspace.test( +test( 'Register delegation fail', - async (test, { root, dao, alice }) => { + async (t) => { + const { root, dao, alice } = t.context.accounts; const testToken = await initTestToken(root); const staking = await initStaking(root, dao, testToken); @@ -48,7 +52,7 @@ workspace.test( { attachedDeposit: regCost }, ), ); - test.regex(errorString, /ERR_NO_STAKING/); + t.regex(errorString, /ERR_NO_STAKING/); await setStakingId(root, dao, staking); // Can only be called by the `staking_id` @@ -60,7 +64,7 @@ workspace.test( { attachedDeposit: regCost }, ), ); - test.regex(errorString, /ERR_INVALID_CALLER/); + t.regex(errorString, /ERR_INVALID_CALLER/); // Attached deposit is handled correctly await captureError(async () => @@ -82,40 +86,42 @@ workspace.test( }, ); -workspace.test('Delegation', async (test, { root, dao, alice }) => { +test('Delegation', async (t) => { + const { root, dao, alice } = t.context.accounts; const testToken = await initTestToken(root); const staking = await initStaking(root, dao, testToken); const randomAmount = new BN('10087687667869'); - const bob = await root.createAccount('bob'); + const bob = await root.createSubAccount('bob'); // set staking await setStakingId(root, dao, staking); let result = await registerAndDelegate(dao, staking, alice, randomAmount); - test.deepEqual( + t.deepEqual( [new BN(result[0]), new BN(result[1]), new BN(result[2])], [new BN('0'), randomAmount, randomAmount], ); result = await registerAndDelegate(dao, staking, bob, randomAmount.muln(2)); - test.deepEqual( + t.deepEqual( [new BN(result[0]), new BN(result[1]), new BN(result[2])], [new BN('0'), randomAmount.muln(2), randomAmount.muln(3)], ); - test.deepEqual( + t.deepEqual( new BN(await dao.view('delegation_balance_of', { account_id: alice })), randomAmount, ); - test.deepEqual( + t.deepEqual( new BN(await dao.view('delegation_balance_of', { account_id: bob })), randomAmount.muln(2), ); - test.deepEqual( + t.deepEqual( new BN(await dao.view('delegation_total_supply')), randomAmount.muln(3), ); }); -workspace.test('Delegation fail', async (test, { root, dao, alice }) => { +test('Delegation fail', async (t) => { + const { root, dao, alice } = t.context.accounts; const testToken = await initTestToken(root); const staking = await initStaking(root, dao, testToken); const randomAmount = new BN('10087687667869'); @@ -127,7 +133,7 @@ workspace.test('Delegation fail', async (test, { root, dao, alice }) => { amount: randomAmount, }), ); - test.regex(errorString, /ERR_NO_STAKING/); + t.regex(errorString, /ERR_NO_STAKING/); // set staking await setStakingId(root, dao, staking); @@ -139,7 +145,7 @@ workspace.test('Delegation fail', async (test, { root, dao, alice }) => { amount: randomAmount, }), ); - test.regex(errorString, /ERR_INVALID_CALLER/); + t.regex(errorString, /ERR_INVALID_CALLER/); // Can't be called without previos registration errorString = await captureError(async () => @@ -148,10 +154,11 @@ workspace.test('Delegation fail', async (test, { root, dao, alice }) => { amount: randomAmount, }), ); - test.regex(errorString, /ERR_NOT_REGISTERED/); + t.regex(errorString, /ERR_NOT_REGISTERED/); }); -workspace.test('Undelegate', async (test, { root, dao, alice }) => { +test('Undelegate', async (t) => { + const { root, dao, alice } = t.context.accounts; const testToken = await initTestToken(root); const staking = await initStaking(root, dao, testToken); const randomAmount = new BN('44887687667868'); @@ -166,13 +173,14 @@ workspace.test('Undelegate', async (test, { root, dao, alice }) => { account_id: alice, amount: randomAmount.divn(2).toString(), }); - test.deepEqual( + t.deepEqual( [new BN(result[0]), new BN(result[1]), new BN(result[2])], [randomAmount, randomAmount.divn(2), randomAmount.divn(2)], ); }); -workspace.test('Undelegate fail', async (test, { root, dao, alice }) => { +test('Undelegate fail', async (t) => { + const { root, dao, alice } = t.context.accounts; const testToken = await initTestToken(root); const staking = await initStaking(root, dao, testToken); const randomAmount = new BN('44887687667868'); @@ -184,7 +192,7 @@ workspace.test('Undelegate fail', async (test, { root, dao, alice }) => { amount: randomAmount, }), ); - test.regex(errorString, /ERR_NO_STAKING/); + t.regex(errorString, /ERR_NO_STAKING/); // Set staking await setStakingId(root, dao, staking); @@ -196,7 +204,7 @@ workspace.test('Undelegate fail', async (test, { root, dao, alice }) => { amount: randomAmount, }), ); - test.regex(errorString, /ERR_INVALID_CALLER/); + t.regex(errorString, /ERR_INVALID_CALLER/); await registerAndDelegate(dao, staking, alice, randomAmount); // Check that a user can't remove more than it delegated @@ -206,5 +214,5 @@ workspace.test('Undelegate fail', async (test, { root, dao, alice }) => { amount: randomAmount.addn(1).toString(), }), ); - test.regex(errorString, /ERR_INVALID_STAKING_CONTRACT/); + t.regex(errorString, /ERR_INVALID_STAKING_CONTRACT/); }); diff --git a/sputnikdao2/tests-ava/__tests__/lib.ava.ts b/sputnikdao2/tests-ava/__tests__/lib.ava.ts index 7bb7cc9dd..f108dea65 100644 --- a/sputnikdao2/tests-ava/__tests__/lib.ava.ts +++ b/sputnikdao2/tests-ava/__tests__/lib.ava.ts @@ -7,14 +7,14 @@ import { DEFAULT_FUNCTION_CALL_GAS, Gas, NEAR, -} from 'near-workspaces-ava'; +} from 'near-workspaces'; import { - workspace, initStaking, initTestToken, STORAGE_PER_BYTE, - workspaceWithoutInit, - workspaceWithFactory, + initWorkspace, + Proposal, + DAO_WASM_BYTES, } from './utils'; import { voteApprove } from './utils'; import { @@ -26,118 +26,12 @@ import { claimBounty, doneBounty, } from './utils'; -import * as fs from 'fs'; -const DAO_WASM_BYTES: Uint8Array = fs.readFileSync('../res/sputnikdao2.wasm'); -workspaceWithFactory.test( - 'Upgrade self using factory', - async (test, { root, factory }) => { - const config = { - name: 'testdao', - purpose: 'to test', - metadata: '', - }; - const policy = [root.accountId]; - const params = { - config, - policy, - }; - - await root.call( - factory, - 'create', - { - name: 'testdao', - args: Buffer.from(JSON.stringify(params)).toString('base64'), - }, - { - attachedDeposit: toYocto('10'), - gas: tGas(300), - }, - ); - - test.deepEqual(await factory.view('get_dao_list', {}), [ - 'testdao.factory.test.near', - ]); - const hash = await factory.view('get_default_code_hash', {}); - - const proposalId: number = await root.call( - 'testdao.factory.test.near', - 'add_proposal', - { - proposal: { - description: 'proposal to test', - kind: { - UpgradeSelf: { - hash: hash, - }, - }, - }, - }, - { - attachedDeposit: toYocto('1'), - }, - ); - test.is(proposalId, 0); - - await root.call( - 'testdao.factory.test.near', - 'act_proposal', - { - id: proposalId, - action: 'VoteApprove', - }, - { - gas: tGas(300), - }, - ); - }, -); - -workspaceWithoutInit.test( - 'Upgrade self negative', - async (test, { root, dao }) => { - const config = { name: 'sputnik', purpose: 'testing', metadata: '' }; - - // NOT INITIALIZED - let err = await captureError(async () => - root.call(dao, 'store_blob', DAO_WASM_BYTES, { - attachedDeposit: toYocto('200'), - gas: tGas(300), - }), - ); - test.regex(err, /ERR_CONTRACT_IS_NOT_INITIALIZED/); - - // Initializing contract - await root.call(dao, 'new', { config, policy: [root.accountId] }); - - // not enough deposit - err = await captureError(async () => - root.call(dao, 'store_blob', DAO_WASM_BYTES, { - attachedDeposit: toYocto('1'), - gas: tGas(300), - }), - ); - test.regex(err, /ERR_NOT_ENOUGH_DEPOSIT/); - - await root.call(dao, 'store_blob', DAO_WASM_BYTES, { - attachedDeposit: toYocto('200'), - gas: tGas(300), - }); - - // Already exists - err = await captureError(async () => - root.call(dao, 'store_blob', DAO_WASM_BYTES, { - attachedDeposit: toYocto('200'), - gas: tGas(300), - }), - ); - test.regex(err, /ERR_ALREADY_EXISTS/); - }, -); +const test = initWorkspace(); -workspace.test('Remove blob', async (test, { root, dao, alice }) => { +test('Remove blob', async (t) => { + const { root, dao, alice } = t.context.accounts; const hash: String = await root.call(dao, 'store_blob', DAO_WASM_BYTES, { attachedDeposit: toYocto('200'), gas: tGas(300), @@ -149,7 +43,7 @@ workspace.test('Remove blob', async (test, { root, dao, alice }) => { hash: 'HLBiX51txizmQzZJMrHMCq4u7iEEqNbaJppZ84yW7628', // some_random hash }), ); - test.regex(err, /ERR_NO_BLOB/); + t.regex(err, /ERR_NO_BLOB/); // Can only be called by the original storer err = await captureError(async () => @@ -157,7 +51,7 @@ workspace.test('Remove blob', async (test, { root, dao, alice }) => { hash: hash, }), ); - test.regex(err, /ERR_INVALID_CALLER/); + t.regex(err, /ERR_INVALID_CALLER/); // blob is removed with payback const rootAmountBeforeRemove = (await root.balance()).total; @@ -165,32 +59,34 @@ workspace.test('Remove blob', async (test, { root, dao, alice }) => { hash: hash, }); const rootAmountAfterRemove = (await root.balance()).total; - test.false(await dao.view('has_blob', { hash: hash })); - test.assert(rootAmountAfterRemove.gt(rootAmountBeforeRemove)); + t.false(await dao.view('has_blob', { hash: hash })); + t.assert(rootAmountAfterRemove.gt(rootAmountBeforeRemove)); }); -workspace.test( +test( 'Callback for BountyDone with NEAR token', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; //During the callback the number bounty_claims_count should decrease const proposalId = await proposeBountyWithNear(alice, dao); await voteOnBounty(root, dao, proposalId); await claimBounty(alice, dao, proposalId); await doneBounty(alice, alice, dao, proposalId); //Before the bounty is done there is 1 claim - test.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); + t.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); const balanceBefore: NEAR = (await alice.balance()).total; //During the callback this number is decreased await voteOnBounty(root, dao, proposalId + 1); const balanceAfter: NEAR = (await alice.balance()).total; - test.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 0); - test.assert(balanceBefore.lt(balanceAfter)); + t.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 0); + t.assert(balanceBefore.lt(balanceAfter)); }, ); -workspace.test( +test( 'Callback for BountyDone ft token fail', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; //Test the callback with Failed proposal status const testTokenFail = await initTestToken(root); const proposalIdFail = await proposeBounty(alice, dao, testTokenFail); @@ -221,16 +117,17 @@ workspace.test( await doneBounty(alice, alice, dao, proposalIdFail); await voteOnBounty(root, dao, proposalIdFail + 1); //Proposal should be Failed - let { status } = await dao.view('get_proposal', { + let { status }: Proposal = await dao.view('get_proposal', { id: proposalIdFail + 1, }); - test.is(status, 'Failed'); + t.is(status, 'Failed'); }, ); -workspace.test( +test( 'Callback for BountyDone ft token', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; //Test correct callback const testToken = await initTestToken(root); await dao.call( @@ -283,24 +180,25 @@ workspace.test( await claimBounty(alice, dao, proposalId); await doneBounty(alice, alice, dao, proposalId); //Before the bounty is done there is 1 claim - test.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); + t.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); const balanceBefore: NEAR = (await alice.balance()).total; //During the callback this number is decreased await voteOnBounty(root, dao, proposalId + 1); //Proposal should be approved - let { status } = await dao.view('get_proposal', { id: proposalId + 1 }); - test.is(status, 'Approved'); + let { status } : Proposal = await dao.view('get_proposal', { id: proposalId + 1 }); + t.is(status, 'Approved'); //During the callback the number bounty_claims_count should decrease const balanceAfter: NEAR = (await alice.balance()).total; - test.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 0); - test.assert(balanceBefore.lt(balanceAfter)); + t.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 0); + t.assert(balanceBefore.lt(balanceAfter)); }, ); -workspace.test('Callback transfer', async (test, { alice, root, dao }) => { - const user1 = await root.createAccount('user1'); +test('Callback transfer', async (t) => { + const { alice, root, dao } = t.context.accounts; + const user1 = await root.createSubAccount('user1'); // Fail transfer by transfering to non-existent accountId let transferId: number = await user1.call( dao, @@ -321,9 +219,9 @@ workspace.test('Callback transfer', async (test, { alice, root, dao }) => { ); let user1Balance = (await user1.balance()).total; await voteApprove(root, dao, transferId); - let { status } = await dao.view('get_proposal', { id: transferId }); - test.is(status, 'Failed'); - test.assert((await user1.balance()).total.eq(user1Balance)); // no bond returns on fail + let { status } : Proposal = await dao.view('get_proposal', { id: transferId }); + t.is(status, 'Failed'); + t.assert((await user1.balance()).total.eq(user1Balance)); // no bond returns on fail // now we transfer to real accountId transferId = await user1.call( @@ -345,12 +243,13 @@ workspace.test('Callback transfer', async (test, { alice, root, dao }) => { ); user1Balance = (await user1.balance()).total; await voteApprove(root, dao, transferId); - ({ status } = await dao.view('get_proposal', { id: transferId })); - test.is(status, 'Approved'); - test.assert((await user1.balance()).total.gt(user1Balance)); // returns bond + ({ status } = await dao.view('get_proposal', { id: transferId }) as Proposal ); + t.is(status, 'Approved'); + t.assert((await user1.balance()).total.gt(user1Balance)); // returns bond }); -workspace.test('Callback function call', async (test, { alice, root, dao }) => { +test('Callback function call', async (t) => { + const { alice, root, dao } = t.context.accounts; const testToken = await initTestToken(root); let transferId: number = await root.call( dao, @@ -388,8 +287,8 @@ workspace.test('Callback function call', async (test, { alice, root, dao }) => { gas: tGas(200), }, ); - let { status } = await dao.view('get_proposal', { id: transferId }); - test.is(status, 'Failed'); + let { status } : Proposal = await dao.view('get_proposal', { id: transferId }); + t.is(status, 'Failed'); transferId = await root.call( dao, @@ -439,6 +338,6 @@ workspace.test('Callback function call', async (test, { alice, root, dao }) => { gas: tGas(200), }, ); - ({ status } = await dao.view('get_proposal', { id: transferId })); - test.is(status, 'Approved'); + ({ status } = await dao.view('get_proposal', { id: transferId }) as Proposal); + t.is(status, 'Approved'); }); diff --git a/sputnikdao2/tests-ava/__tests__/policy.ava.ts b/sputnikdao2/tests-ava/__tests__/policy.ava.ts deleted file mode 100644 index dc65c4e16..000000000 --- a/sputnikdao2/tests-ava/__tests__/policy.ava.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { - Workspace, - BN, - NearAccount, - captureError, - toYocto, - tGas, - DEFAULT_FUNCTION_CALL_GAS, -} from 'near-workspaces-ava'; -import { - initStaking, - initTestToken, - STORAGE_PER_BYTE, - registerAndDelegate, - setStakingId, - workspaceWithoutInit as workspace, -} from './utils'; - -workspace.test( - 'Testing policy TokenWeight', - async (test, { alice, root, dao }) => { - const config = { name: 'sputnik', purpose: 'testing', metadata: '' }; - const bob = await root.createAccount('bob'); - const period = new BN('1000000000') - .muln(60) - .muln(60) - .muln(24) - .muln(7) - .toString(); - const testToken = await initTestToken(root); - const staking = await initStaking(root, dao, testToken); - await root.call(dao, 'new', { config, policy: [root.accountId] }); - await setStakingId(root, dao, staking); - - const policy = { - roles: [ - { - name: 'all', - kind: { Group: [alice.accountId, bob.accountId] }, // fails with kind: "Everyone" need to investigate - permissions: ['*:AddProposal', '*:VoteApprove'], - vote_policy: {}, - }, - ], - default_vote_policy: { - weight_kind: 'TokenWeight', - quorum: new BN('1').toString(), - threshold: '5', - }, - proposal_bond: toYocto('1'), - proposal_period: period, - bounty_bond: toYocto('1'), - bounty_forgiveness_period: period, - }; - - let proposalId: number = await alice.call( - dao, - 'add_proposal', - { - proposal: { - description: 'test', - kind: { ChangePolicy: { policy } }, - }, - }, - { - attachedDeposit: toYocto('1'), - }, - ); - await root.call(dao, 'act_proposal', { - id: proposalId, - action: 'VoteApprove', - }); - - // Setting up a new config - const new_config = { - name: 'new dao wohoo', - purpose: 'testing', - metadata: '', - }; - await registerAndDelegate(dao, staking, alice, new BN('1')); - await registerAndDelegate(dao, staking, bob, new BN('4')); - proposalId = await alice.call( - dao, - 'add_proposal', - { - proposal: { - description: 'test', - kind: { - ChangeConfig: { - config: new_config, - }, - }, - }, - }, - { - attachedDeposit: toYocto('1'), - }, - ); - await alice.call(dao, 'act_proposal', { - id: proposalId, - action: 'VoteApprove', - }); - await bob.call(dao, 'act_proposal', { - id: proposalId, - action: 'VoteApprove', - }); - test.deepEqual(await dao.view('get_config'), new_config); - }, -); - -workspace.test('Policy self-lock', async (test, { alice, root, dao }) => { - const config = { name: 'sputnik', purpose: 'testing', metadata: '' }; - const period = new BN('1000000000') - .muln(60) - .muln(60) - .muln(24) - .muln(7) - .toString(); - const policy = { - roles: [ - { - name: 'all', - kind: { Group: [alice.accountId] }, - permissions: ['*:AddProposal', '*:VoteApprove'], - vote_policy: {}, - }, - ], - default_vote_policy: { - weight_kind: 'TokenWeight', - quorum: new BN('1').toString(), - threshold: '5', - }, - proposal_bond: toYocto('1'), - proposal_period: period, - bounty_bond: toYocto('1'), - bounty_forgiveness_period: period, - }; - // 'staking_id' is not set, we can't delegate, so this contract got locked - await root.call(dao, 'new', { config, policy }); - const proposalId = await alice.call( - dao, - 'add_proposal', - { - proposal: { - description: 'test', - kind: { - ChangePolicy: { - policy, - }, - }, - }, - }, - { - attachedDeposit: toYocto('1'), - }, - ); - await alice.call(dao, 'act_proposal', { - id: proposalId, - action: 'VoteApprove', - }); - let { status } = await dao.view('get_proposal', { id: proposalId }); - test.is(status, 'InProgress'); -}); diff --git a/sputnikdao2/tests-ava/__tests__/proposals.ava.ts b/sputnikdao2/tests-ava/__tests__/proposals.ava.ts index 3166b2b3e..54cb13035 100644 --- a/sputnikdao2/tests-ava/__tests__/proposals.ava.ts +++ b/sputnikdao2/tests-ava/__tests__/proposals.ava.ts @@ -6,28 +6,32 @@ import { NEAR, ONE_NEAR, tGas, -} from 'near-workspaces-ava'; +} from 'near-workspaces'; import { - workspace, initTestToken, initStaking, setStakingId, - workspaceWithoutInit, voteApprove, + Proposal, + initWorkspace, } from './utils'; -workspace.test('basic', async (test, { alice, root, dao }) => { - test.true(await alice.exists()); - test.true(await root.exists()); - test.true(await dao.exists()); - test.log(await dao.view('get_config')); +const test = initWorkspace(); + +test('basic', async (t) => { + const { alice, root, dao } = t.context.accounts; + t.true(await alice.exists()); + t.true(await root.exists()); + t.true(await dao.exists()); + t.log(await dao.view('get_config')); }); -workspace.test( +test( 'add_proposal fails in case of insufficient deposit', - async (test, { alice, root, dao }) => { - test.is(await dao.view('get_last_proposal_id'), 0); + async (t) => { + const { alice, root, dao } = t.context.accounts; + t.is(await dao.view('get_last_proposal_id'), 0); const config = { name: 'sputnikdao', purpose: 'testing', @@ -53,10 +57,10 @@ workspace.test( ), ); - test.log(err.toString()); - test.true(err.includes('ERR_MIN_BOND')); + t.log(err.toString()); + t.true(err.includes('ERR_MIN_BOND')); //the proposal did not count - test.is(await dao.view('get_last_proposal_id'), 0); + t.is(await dao.view('get_last_proposal_id'), 0); //Checks that the same proposal doesn't fail //if the deposit is at least 1 near @@ -75,19 +79,19 @@ workspace.test( }, { attachedDeposit: toYocto('1') }, ); - test.is(await dao.view('get_last_proposal_id'), 1); + t.is(await dao.view('get_last_proposal_id'), 1); let new_proposal: any = await dao.view('get_proposal', { id: 0 }); - test.log(new_proposal); - test.is(new_proposal.description, 'rename the dao'); - test.is(new_proposal.proposer, 'alice.test.near'); - test.is(new_proposal.status, 'InProgress'); + t.log(new_proposal); + t.is(new_proposal.description, 'rename the dao'); + t.is(new_proposal.proposer, 'alice.test.near'); + t.is(new_proposal.status, 'InProgress'); - test.truthy(new_proposal.kind.ChangeConfig); - test.is(new_proposal.kind.ChangeConfig.config.name, 'sputnikdao'); + t.truthy(new_proposal.kind.ChangeConfig); + t.is(new_proposal.kind.ChangeConfig.config.name, 'sputnikdao'); //same config as we did not execute that proposal - test.deepEqual(await dao.view('get_config'), { + t.deepEqual(await dao.view('get_config'), { name: 'sputnik', purpose: 'testing', metadata: '', @@ -95,10 +99,11 @@ workspace.test( }, ); -workspace.test( +test( 'Bob can not add proposals', - async (test, { alice, root, dao }) => { - const bob = await root.createAccount('bob'); + async (t) => { + const { alice, root, dao } = t.context.accounts; + const bob = await root.createSubAccount('bob'); //First we change a policy so that Bob can't add proposals const period = new BN('1000000000') @@ -165,12 +170,13 @@ workspace.test( { attachedDeposit: toYocto('1') }, ), ); - test.regex(errorString, /ERR_PERMISSION_DENIED/); + t.regex(errorString, /ERR_PERMISSION_DENIED/); }, ); -workspace.test('Proposal ChangePolicy', async (test, { alice, root, dao }) => { - test.deepEqual( +test('Proposal ChangePolicy', async (t) => { + const { alice, root, dao } = t.context.accounts; + t.deepEqual( await dao.view('get_proposals', { from_index: 0, limit: 10 }), [], ); @@ -195,7 +201,7 @@ workspace.test('Proposal ChangePolicy', async (test, { alice, root, dao }) => { { attachedDeposit: toYocto('1') }, ), ); - test.regex(errorString, /ERR_INVALID_POLICY/); + t.regex(errorString, /ERR_INVALID_POLICY/); //Check that we can change to a correct policy const period = new BN('1000000000') @@ -242,7 +248,7 @@ workspace.test('Proposal ChangePolicy', async (test, { alice, root, dao }) => { ); //Number of proposals = 1 - test.is(await dao.view('get_last_proposal_id'), 1); + t.is(await dao.view('get_last_proposal_id'), 1); //Check that the proposal is added to the list of proposals let proposals = await dao.view('get_proposals', { from_index: 0, @@ -257,33 +263,34 @@ workspace.test('Proposal ChangePolicy', async (test, { alice, root, dao }) => { vote_counts: {}, votes: {}, }; - test.is(proposals[0].id, realProposal.id); - test.is(proposals[0].proposer, realProposal.proposer); - test.is(proposals[0].description, realProposal.description); - test.is(proposals[0].status, realProposal.status); - test.deepEqual(proposals[0].vote_counts, realProposal.vote_counts); - test.deepEqual(proposals[0].votes, realProposal.votes); - test.deepEqual(proposals[0].kind, realProposal.kind); + t.is(proposals[0].id, realProposal.id); + t.is(proposals[0].proposer, realProposal.proposer); + t.is(proposals[0].description, realProposal.description); + t.is(proposals[0].status, realProposal.status); + t.deepEqual(proposals[0].vote_counts, realProposal.vote_counts); + t.deepEqual(proposals[0].votes, realProposal.votes); + t.deepEqual(proposals[0].kind, realProposal.kind); //After voting on the proposal it is Approved await voteApprove(root, dao, id); - test.deepEqual( + t.deepEqual( (await dao.view('get_proposals', { from_index: 0, limit: 10 }))[0] .vote_counts, { council: [1, 0, 0] }, ); - test.is( + t.is( (await dao.view('get_proposals', { from_index: 0, limit: 10 }))[0] .status, 'Approved', ); //Check that the policy is changed - test.deepEqual(await dao.view('get_policy'), correctPolicy); + t.deepEqual(await dao.view('get_policy'), correctPolicy); }); -workspace.test('Proposal Transfer', async (test, { alice, root, dao }) => { +test('Proposal Transfer', async (t) => { + const { alice, root, dao } = t.context.accounts; let errorString = await captureError( async () => await root.call( @@ -308,7 +315,7 @@ workspace.test('Proposal Transfer', async (test, { alice, root, dao }) => { }, ), ); - test.regex(errorString, /ERR_BASE_TOKEN_NO_MSG/); + t.regex(errorString, /ERR_BASE_TOKEN_NO_MSG/); const transferId: number = await root.call( dao, @@ -330,28 +337,30 @@ workspace.test('Proposal Transfer', async (test, { alice, root, dao }) => { const initBalance: NEAR = (await alice.balance()).total; await voteApprove(root, dao, transferId); const balance: NEAR = (await alice.balance()).total; - test.deepEqual(balance, initBalance.add(ONE_NEAR)); + t.deepEqual(balance, initBalance.add(ONE_NEAR)); }); -workspace.test( +test( 'Proposal SetStakingContract', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; const testToken = await initTestToken(root); const staking = await initStaking(root, dao, testToken); await setStakingId(root, dao, staking); - test.is(await dao.view('get_staking_contract'), staking.accountId); + t.is(await dao.view('get_staking_contract'), staking.accountId); let errorString = await captureError( async () => await setStakingId(root, dao, staking), ); - test.regex(errorString, /ERR_STAKING_CONTRACT_CANT_CHANGE/); + t.regex(errorString, /ERR_STAKING_CONTRACT_CANT_CHANGE/); }, ); -workspace.test( +test( 'Voting is only allowed for councils', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; const config = { name: 'sputnikdao', purpose: 'testing', @@ -379,32 +388,33 @@ workspace.test( const err = await captureError( async () => await voteApprove(alice, dao, id), ); - test.log(err); - test.true(err.includes('ERR_PERMISSION_DENIED')); + t.log(err); + t.true(err.includes('ERR_PERMISSION_DENIED')); let proposal: any = await dao.view('get_proposal', { id }); - test.log(proposal); - test.is(proposal.status, 'InProgress'); + t.log(proposal); + t.is(proposal.status, 'InProgress'); //Check that voting is allowed for councils //council (root) votes on alice's promise const res = await voteApprove(root, dao, id); proposal = await dao.view('get_proposal', { id }); - test.log(res); - test.log(proposal); - test.is(proposal.status, 'Approved'); + t.log(res); + t.log(proposal); + t.is(proposal.status, 'Approved'); // proposal approved so now the config is equal to what alice did propose - test.deepEqual(await dao.view('get_config'), config); + t.deepEqual(await dao.view('get_config'), config); }, ); // If the number of votes in the group has changed (new members has been added) // the proposal can lose it's approved state. // In this case new proposal needs to be made, this one should expire -workspace.test( +test( 'Proposal group changed during voting', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; const transferId: number = await root.call( dao, 'add_proposal', @@ -441,149 +451,13 @@ workspace.test( ); await voteApprove(root, dao, addMemberToRoleId); await voteApprove(root, dao, transferId); - const { status } = await dao.view('get_proposal', { id: transferId }); - test.is(status, 'InProgress'); - }, -); - -workspaceWithoutInit.test( - 'Proposal action types', - async (test, { alice, root, dao }) => { - const user1 = await root.createAccount('user1'); - const user2 = await root.createAccount('user2'); - const user3 = await root.createAccount('user3'); - const period = new BN('1000000000') - .muln(60) - .muln(60) - .muln(24) - .muln(7) - .toString(); - const policy = { - roles: [ - { - name: 'council', - kind: { - Group: [ - alice.accountId, - user1.accountId, - user2.accountId, - user3.accountId, - ], - }, - permissions: ['*:*'], - vote_policy: {}, - }, - ], - default_vote_policy: { - weight_kind: 'RoleWeight', - quorum: new BN('0').toString(), - threshold: [1, 2], - }, - proposal_bond: toYocto('1'), - proposal_period: period, - bounty_bond: toYocto('1'), - bounty_forgiveness_period: period, - }; - - let config = { name: 'sputnik', purpose: 'testing', metadata: '' }; - - await root.call(dao, 'new', { config, policy }); - - let proposalId = await alice.call( - dao, - 'add_proposal', - { - proposal: { - description: 'rename the dao', - kind: { - ChangeConfig: { - config, - }, - }, - }, - }, - { attachedDeposit: toYocto('1') }, - ); - - // Remove proposal works - await alice.call(dao, 'act_proposal', { - id: proposalId, - action: 'RemoveProposal', - }); - let err = await captureError(async () => - dao.view('get_proposal', { id: proposalId }), - ); - test.regex(err, /ERR_NO_PROPOSAL/); - - err = await captureError(async () => - alice.call(dao, 'act_proposal', { - id: proposalId, - action: 'VoteApprove', - }), - ); - test.regex(err, /ERR_NO_PROPOSAL/); - - proposalId = await alice.call( - dao, - 'add_proposal', - { - proposal: { - description: 'rename the dao', - kind: { - ChangeConfig: { - config, - }, - }, - }, - }, - { attachedDeposit: toYocto('1') }, - ); - - err = await captureError(async () => - alice.call(dao, 'act_proposal', { - id: proposalId, - action: 'AddProposal', - }), - ); - test.regex(err, /ERR_WRONG_ACTION/); - - // Check if every vote counts - await user1.call(dao, 'act_proposal', { - id: proposalId, - action: 'VoteApprove', - }); - await user2.call(dao, 'act_proposal', { - id: proposalId, - action: 'VoteReject', - }); - await alice.call(dao, 'act_proposal', { - id: proposalId, - action: 'VoteRemove', - }); - { - const { vote_counts, votes } = await dao.view('get_proposal', { - id: proposalId, - }); - test.deepEqual(vote_counts.council, [1, 1, 1]); - test.deepEqual(votes, { - [alice.accountId]: 'Remove', - [user1.accountId]: 'Approve', - [user2.accountId]: 'Reject', - }); - } - - // Finalize proposal will panic if not exired or failed - err = await captureError(async () => - alice.call(dao, 'act_proposal', { - id: proposalId, - action: 'Finalize', - }), - ); - test.regex(err, /ERR_PROPOSAL_NOT_EXPIRED_OR_FAILED/); + const { status } : Proposal = await dao.view('get_proposal', { id: transferId }); + t.is(status, 'InProgress'); }, ); -workspace.test('Proposal transfer ft', async (test, { alice, root, dao }) => { +test('Proposal transfer ft', async (t) => { + const { alice, root, dao } = t.context.accounts; const testToken = await initTestToken(root); await dao.call( testToken, @@ -627,12 +501,13 @@ workspace.test('Proposal transfer ft', async (test, { alice, root, dao }) => { }, ); await voteApprove(root, dao, transferId); - const { status } = await dao.view('get_proposal', { id: transferId }); - test.is(status, 'Approved'); + const { status } : Proposal = await dao.view('get_proposal', { id: transferId }); + t.is(status, 'Approved'); }); -workspace.test('Callback transfer', async (test, { alice, root, dao }) => { - const user1 = await root.createAccount('user1'); +test('Callback transfer', async (t) => { + const { alice, root, dao } = t.context.accounts; + const user1 = await root.createSubAccount('user1'); // Fail transfer by transfering to non-existent accountId let transferId: number = await user1.call( dao, @@ -653,9 +528,9 @@ workspace.test('Callback transfer', async (test, { alice, root, dao }) => { ); let user1Balance = (await user1.balance()).total; await voteApprove(root, dao, transferId); - let { status } = await dao.view('get_proposal', { id: transferId }); - test.is(status, 'Failed'); - test.assert((await user1.balance()).total.eq(user1Balance)); // no bond returns on fail + let { status } : Proposal = await dao.view('get_proposal', { id: transferId }); + t.is(status, 'Failed'); + t.assert((await user1.balance()).total.eq(user1Balance)); // no bond returns on fail // now we transfer to real accountId transferId = await user1.call( @@ -677,12 +552,13 @@ workspace.test('Callback transfer', async (test, { alice, root, dao }) => { ); user1Balance = (await user1.balance()).total; await voteApprove(root, dao, transferId); - ({ status } = await dao.view('get_proposal', { id: transferId })); - test.is(status, 'Approved'); - test.assert((await user1.balance()).total.gt(user1Balance)); // returns bond + ({ status } = await dao.view('get_proposal', { id: transferId }) as Proposal); + t.is(status, 'Approved'); + t.assert((await user1.balance()).total.gt(user1Balance)); // returns bond }); -workspace.test('Callback function call', async (test, { alice, root, dao }) => { +test('Callback function call', async (t) => { + const { alice, root, dao } = t.context.accounts; const testToken = await initTestToken(root); let transferId: number = await root.call( dao, @@ -720,8 +596,8 @@ workspace.test('Callback function call', async (test, { alice, root, dao }) => { gas: tGas(200), }, ); - let { status } = await dao.view('get_proposal', { id: transferId }); - test.is(status, 'Failed'); + let { status } : Proposal = await dao.view('get_proposal', { id: transferId }); + t.is(status, 'Failed'); transferId = await root.call( dao, @@ -771,6 +647,6 @@ workspace.test('Callback function call', async (test, { alice, root, dao }) => { gas: tGas(200), }, ); - ({ status } = await dao.view('get_proposal', { id: transferId })); - test.is(status, 'Approved'); + ({ status } = await dao.view('get_proposal', { id: transferId }) as Proposal); + t.is(status, 'Approved'); }); diff --git a/sputnikdao2/tests-ava/__tests__/skip-init.ava.ts b/sputnikdao2/tests-ava/__tests__/skip-init.ava.ts new file mode 100644 index 000000000..2afe2705a --- /dev/null +++ b/sputnikdao2/tests-ava/__tests__/skip-init.ava.ts @@ -0,0 +1,352 @@ +import { + BN, + NearAccount, + captureError, + toYocto, + tGas, + DEFAULT_FUNCTION_CALL_GAS, +} from 'near-workspaces'; +import { + initStaking, + initTestToken, + STORAGE_PER_BYTE, + registerAndDelegate, + setStakingId, + initWorkspace, + Proposal, + DAO_WASM_BYTES, +} from './utils'; + +// Set up workspace without initializing DAO contract +const test = initWorkspace({ skipInit: true }); + +// -- Upgrade -- +test( + 'Upgrade self negative', + async (t) => { + const { root, dao } = t.context.accounts; + const config = { name: 'sputnik', purpose: 'testing', metadata: '' }; + + // NOT INITIALIZED + let err = await captureError(async () => + root.call(dao, 'store_blob', DAO_WASM_BYTES, { + attachedDeposit: toYocto('200'), + gas: tGas(300), + }), + ); + t.regex(err, /ERR_CONTRACT_IS_NOT_INITIALIZED/); + + // Initializing contract + await root.call(dao, 'new', { config, policy: [root.accountId] }); + + // not enough deposit + err = await captureError(async () => + root.call(dao, 'store_blob', DAO_WASM_BYTES, { + attachedDeposit: toYocto('1'), + gas: tGas(300), + }), + ); + t.regex(err, /ERR_NOT_ENOUGH_DEPOSIT/); + + await root.call(dao, 'store_blob', DAO_WASM_BYTES, { + attachedDeposit: toYocto('200'), + gas: tGas(300), + }); + + // Already exists + err = await captureError(async () => + root.call(dao, 'store_blob', DAO_WASM_BYTES, { + attachedDeposit: toYocto('200'), + gas: tGas(300), + }), + ); + t.regex(err, /ERR_ALREADY_EXISTS/); + }, +); + +// -- Proposal -- +test( + 'Proposal action types', + async (t) => { + const { alice, root, dao } = t.context.accounts; + const user1 = await root.createSubAccount('user1'); + const user2 = await root.createSubAccount('user2'); + const user3 = await root.createSubAccount('user3'); + const period = new BN('1000000000') + .muln(60) + .muln(60) + .muln(24) + .muln(7) + .toString(); + const policy = { + roles: [ + { + name: 'council', + kind: { + Group: [ + alice.accountId, + user1.accountId, + user2.accountId, + user3.accountId, + ], + }, + permissions: ['*:*'], + vote_policy: {}, + }, + ], + default_vote_policy: { + weight_kind: 'RoleWeight', + quorum: new BN('0').toString(), + threshold: [1, 2], + }, + proposal_bond: toYocto('1'), + proposal_period: period, + bounty_bond: toYocto('1'), + bounty_forgiveness_period: period, + }; + + let config = { name: 'sputnik', purpose: 'testing', metadata: '' }; + + await root.call(dao, 'new', { config, policy }); + + let proposalId = await alice.call( + dao, + 'add_proposal', + { + proposal: { + description: 'rename the dao', + kind: { + ChangeConfig: { + config, + }, + }, + }, + }, + { attachedDeposit: toYocto('1') }, + ); + + // Remove proposal works + await alice.call(dao, 'act_proposal', { + id: proposalId, + action: 'RemoveProposal', + }); + let err = await captureError(async () => + dao.view('get_proposal', { id: proposalId }), + ); + t.regex(err, /ERR_NO_PROPOSAL/); + + err = await captureError(async () => + alice.call(dao, 'act_proposal', { + id: proposalId, + action: 'VoteApprove', + }), + ); + t.regex(err, /ERR_NO_PROPOSAL/); + + proposalId = await alice.call( + dao, + 'add_proposal', + { + proposal: { + description: 'rename the dao', + kind: { + ChangeConfig: { + config, + }, + }, + }, + }, + { attachedDeposit: toYocto('1') }, + ); + + err = await captureError(async () => + alice.call(dao, 'act_proposal', { + id: proposalId, + action: 'AddProposal', + }), + ); + t.regex(err, /ERR_WRONG_ACTION/); + + // Check if every vote counts + await user1.call(dao, 'act_proposal', { + id: proposalId, + action: 'VoteApprove', + }); + await user2.call(dao, 'act_proposal', { + id: proposalId, + action: 'VoteReject', + }); + await alice.call(dao, 'act_proposal', { + id: proposalId, + action: 'VoteRemove', + }); + { + const { vote_counts, votes } = await dao.view('get_proposal', { + id: proposalId, + }) as any; + t.deepEqual(vote_counts.council, [1, 1, 1]); + t.deepEqual(votes, { + [alice.accountId]: 'Remove', + [user1.accountId]: 'Approve', + [user2.accountId]: 'Reject', + }); + } + + // Finalize proposal will panic if not exired or failed + err = await captureError(async () => + alice.call(dao, 'act_proposal', { + id: proposalId, + action: 'Finalize', + }), + ); + t.regex(err, /ERR_PROPOSAL_NOT_EXPIRED_OR_FAILED/); + }, +); + +// -- Policy -- +test( + 'Testing policy TokenWeight', + async (t) => { + const { alice, root, dao } = t.context.accounts; + const config = { name: 'sputnik', purpose: 'testing', metadata: '' }; + const bob = await root.createSubAccount('bob'); + const period = new BN('1000000000') + .muln(60) + .muln(60) + .muln(24) + .muln(7) + .toString(); + const testToken = await initTestToken(root); + const staking = await initStaking(root, dao, testToken); + await root.call(dao, 'new', { config, policy: [root.accountId] }); + await setStakingId(root, dao, staking); + + const policy = { + roles: [ + { + name: 'all', + kind: { Group: [alice.accountId, bob.accountId] }, // fails with kind: "Everyone" need to investigate + permissions: ['*:AddProposal', '*:VoteApprove'], + vote_policy: {}, + }, + ], + default_vote_policy: { + weight_kind: 'TokenWeight', + quorum: new BN('1').toString(), + threshold: '5', + }, + proposal_bond: toYocto('1'), + proposal_period: period, + bounty_bond: toYocto('1'), + bounty_forgiveness_period: period, + }; + + let proposalId: number = await alice.call( + dao, + 'add_proposal', + { + proposal: { + description: 'test', + kind: { ChangePolicy: { policy } }, + }, + }, + { + attachedDeposit: toYocto('1'), + }, + ); + await root.call(dao, 'act_proposal', { + id: proposalId, + action: 'VoteApprove', + }); + + // Setting up a new config + const new_config = { + name: 'new dao wohoo', + purpose: 'testing', + metadata: '', + }; + await registerAndDelegate(dao, staking, alice, new BN('1')); + await registerAndDelegate(dao, staking, bob, new BN('4')); + proposalId = await alice.call( + dao, + 'add_proposal', + { + proposal: { + description: 'test', + kind: { + ChangeConfig: { + config: new_config, + }, + }, + }, + }, + { + attachedDeposit: toYocto('1'), + }, + ); + await alice.call(dao, 'act_proposal', { + id: proposalId, + action: 'VoteApprove', + }); + await bob.call(dao, 'act_proposal', { + id: proposalId, + action: 'VoteApprove', + }); + t.deepEqual(await dao.view('get_config'), new_config); + }, +); + +test('Policy self-lock', async (t) => { + const { alice, root, dao } = t.context.accounts; + const config = { name: 'sputnik', purpose: 'testing', metadata: '' }; + const period = new BN('1000000000') + .muln(60) + .muln(60) + .muln(24) + .muln(7) + .toString(); + const policy = { + roles: [ + { + name: 'all', + kind: { Group: [alice.accountId] }, + permissions: ['*:AddProposal', '*:VoteApprove'], + vote_policy: {}, + }, + ], + default_vote_policy: { + weight_kind: 'TokenWeight', + quorum: new BN('1').toString(), + threshold: '5', + }, + proposal_bond: toYocto('1'), + proposal_period: period, + bounty_bond: toYocto('1'), + bounty_forgiveness_period: period, + }; + // 'staking_id' is not set, we can't delegate, so this contract got locked + await root.call(dao, 'new', { config, policy }); + const proposalId = await alice.call( + dao, + 'add_proposal', + { + proposal: { + description: 'test', + kind: { + ChangePolicy: { + policy, + }, + }, + }, + }, + { + attachedDeposit: toYocto('1'), + }, + ); + await alice.call(dao, 'act_proposal', { + id: proposalId, + action: 'VoteApprove', + }); + let { status } : Proposal = await dao.view('get_proposal', { id: proposalId }); + t.is(status, 'InProgress'); +}); diff --git a/sputnikdao2/tests-ava/__tests__/upgrade.ava.ts b/sputnikdao2/tests-ava/__tests__/upgrade.ava.ts index bd039613a..9ccaf28c6 100644 --- a/sputnikdao2/tests-ava/__tests__/upgrade.ava.ts +++ b/sputnikdao2/tests-ava/__tests__/upgrade.ava.ts @@ -1,6 +1,6 @@ -import { toYocto, tGas } from 'near-workspaces-ava'; +import { toYocto, tGas } from 'near-workspaces'; -import { workspaceWithFactory } from './utils'; +import { initWorkspace } from './utils'; // DAO v2 Upgrade flow: // 1. add proposal for store_contract_self(get it approved) @@ -8,14 +8,19 @@ import { workspaceWithFactory } from './utils'; // 3. add proposal for remove_contract_self(get it approved) // 4. Confirm DAO contract code_hash and returned balance -workspaceWithFactory.test('basic', async (test, { root, factory }) => { - test.true(await root.exists()); - test.true(await factory.exists()); +// Set up workspace with DAO factory contract +const test = initWorkspace({ factory: true }); + +test('basic', async (t) => { + const { root, factory } = t.context.accounts; + t.true(await root.exists()); + t.true(await factory.exists()); }); -workspaceWithFactory.test( +test( 'Store DAO upgrade code in DAO via factory', - async (test, { root, factory }) => { + async (t) => { + const { root, factory } = t.context.accounts; const config = { name: 'upgradedao', purpose: 'to test', @@ -40,7 +45,7 @@ workspaceWithFactory.test( }, ); - test.deepEqual(await factory.view('get_dao_list', {}), [ + t.deepEqual(await factory.view('get_dao_list', {}), [ 'upgradedao.factory.test.near', ]); @@ -55,7 +60,7 @@ workspaceWithFactory.test( {}, { gas: tGas(300) }, ); - test.is(proposalId, 0); + t.is(proposalId, 0); const args = Buffer.from( `{ "code_hash": "${default_code_hash}" }`, @@ -97,7 +102,7 @@ workspaceWithFactory.test( {}, { gas: tGas(300) }, ); - test.is(proposalId, 1); + t.is(proposalId, 1); let new_proposal: any = await root.call( 'upgradedao.factory.test.near', @@ -106,15 +111,15 @@ workspaceWithFactory.test( { gas: tGas(300) }, ); - test.log(new_proposal); - test.is( + t.log(new_proposal); + t.is( new_proposal.description, 'Store DAO upgrade contract code blob', ); - test.is(new_proposal.proposer, 'test.near'); - test.is(new_proposal.status, 'InProgress'); - test.truthy(new_proposal.kind.FunctionCall); - test.is( + t.is(new_proposal.proposer, 'test.near'); + t.is(new_proposal.status, 'InProgress'); + t.truthy(new_proposal.kind.FunctionCall); + t.is( new_proposal.kind.FunctionCall.receiver_id, `${factory.accountId}`, ); @@ -132,8 +137,8 @@ workspaceWithFactory.test( { id: 0 }, { gas: tGas(300) }, ); - test.log(passed_proposal_0); - test.is(passed_proposal_0.status, 'Approved'); + t.log(passed_proposal_0); + t.is(passed_proposal_0.status, 'Approved'); // 2. add proposal for UpgradeSelf with hash of blob from #1(get it approved) // -------------------------------------------------------------------- @@ -164,14 +169,14 @@ workspaceWithFactory.test( { gas: tGas(300) }, ); - test.log(new_proposal_1); - test.is( + t.log(new_proposal_1); + t.is( new_proposal_1.description, 'Upgrade DAO contract using local code blob', ); - test.is(new_proposal_1.proposer, 'test.near'); - test.is(new_proposal_1.status, 'InProgress'); - test.truthy(new_proposal_1.kind.UpgradeSelf); + t.is(new_proposal_1.proposer, 'test.near'); + t.is(new_proposal_1.status, 'InProgress'); + t.truthy(new_proposal_1.kind.UpgradeSelf); await root.call( 'upgradedao.factory.test.near', @@ -187,8 +192,8 @@ workspaceWithFactory.test( { gas: tGas(300) }, ); - test.log(passed_proposal_1); - test.is(passed_proposal_1.status, 'Approved'); + t.log(passed_proposal_1); + t.is(passed_proposal_1.status, 'Approved'); // 3. add proposal for remove_contract_self(get it approved) // -------------------------------------------------------------------- @@ -236,15 +241,15 @@ workspaceWithFactory.test( { gas: tGas(300) }, ); - test.log(new_proposal_2); - test.is( + t.log(new_proposal_2); + t.is( new_proposal_2.description, 'Remove DAO upgrade contract local code blob via factory', ); - test.is(new_proposal_2.proposer, 'test.near'); - test.is(new_proposal_2.status, 'InProgress'); - test.truthy(new_proposal_2.kind.FunctionCall); - test.is( + t.is(new_proposal_2.proposer, 'test.near'); + t.is(new_proposal_2.status, 'InProgress'); + t.truthy(new_proposal_2.kind.FunctionCall); + t.is( new_proposal_2.kind.FunctionCall.receiver_id, `${factory.accountId}`, ); @@ -263,11 +268,77 @@ workspaceWithFactory.test( { gas: tGas(300) }, ); - test.log(passed_proposal_2); - test.is(passed_proposal_2.status, 'Approved'); + t.log(passed_proposal_2); + t.is(passed_proposal_2.status, 'Approved'); // 4. Confirm DAO contract code_hash and returned balance // -------------------------------------------------------------------- // TODO: Check if balance increased by 6 NEAR for refund }, ); + +test( + 'Upgrade self using factory', + async (t) => { + const { root, factory } = t.context.accounts; + const config = { + name: 'testdao', + purpose: 'to test', + metadata: '', + }; + const policy = [root.accountId]; + const params = { + config, + policy, + }; + + await root.call( + factory, + 'create', + { + name: 'testdao', + args: Buffer.from(JSON.stringify(params)).toString('base64'), + }, + { + attachedDeposit: toYocto('10'), + gas: tGas(300), + }, + ); + + t.deepEqual(await factory.view('get_dao_list', {}), [ + 'testdao.factory.test.near', + ]); + const hash = await factory.view('get_default_code_hash', {}); + + const proposalId: number = await root.call( + 'testdao.factory.test.near', + 'add_proposal', + { + proposal: { + description: 'proposal to test', + kind: { + UpgradeSelf: { + hash: hash, + }, + }, + }, + }, + { + attachedDeposit: toYocto('1'), + }, + ); + t.is(proposalId, 0); + + await root.call( + 'testdao.factory.test.near', + 'act_proposal', + { + id: proposalId, + action: 'VoteApprove', + }, + { + gas: tGas(300), + }, + ); + }, +); diff --git a/sputnikdao2/tests-ava/__tests__/utils.ts b/sputnikdao2/tests-ava/__tests__/utils.ts index 9b94da5d7..e8ca61566 100644 --- a/sputnikdao2/tests-ava/__tests__/utils.ts +++ b/sputnikdao2/tests-ava/__tests__/utils.ts @@ -1,61 +1,117 @@ -import { Workspace, NearAccount, BN, toYocto, tGas } from 'near-workspaces-ava'; +import { Worker, NearAccount, BN, toYocto, tGas, KeyPair } from 'near-workspaces'; +import anyTest, { TestFn } from 'ava'; +import * as fs from 'fs'; -async function initWorkspace(root: NearAccount) { - const alice = await root.createAccount('alice'); - // console.log('alice\'s balance is: ' + (await alice.balance()).total) //100N +export async function deployAndInit({ + root, + subContractId, + code, + init, + initialBalance, +}: { + root: NearAccount; + subContractId: string; + code: Uint8Array | string; + init?: { + methodName: string; + args?: Record; + options?: { + gas?: string | BN; + attachedDeposit?: string | BN; + signWithKey?: KeyPair; + } + }; + initialBalance?: string; +}): Promise { + const contract = await root.createSubAccount(subContractId, { + initialBalance, + }); + const result = await contract.deploy(code); + if (result.failed) { + throw result.Failure; + } + if (init) { + await contract.call(contract, init.methodName, init.args ?? {}, init.options); + } + return contract; +} - const config = { name: 'sputnik', purpose: 'testing', metadata: '' }; - const policy = [root.accountId]; +export function initWorkspace(options?: { skipInit?: boolean, factory?: boolean}) { + const test = anyTest as TestFn<{ + worker: Worker; + accounts: Record; + }>; - //for short let's call it just dao - const dao = await root.createAndDeploy('dao', '../res/sputnikdao2.wasm', { - method: 'new', - args: { config, policy }, - initialBalance: toYocto('200'), - }); + test.beforeEach(async (t) => { + // Init the worker and start a Sandbox server + const worker = await Worker.init(); - // console.log('dao\'s balance is: ' + (await dao.balance()).total) //~200N + // Create accounts + const root = worker.rootAccount; + const alice = await root.createSubAccount('alice'); + // console.log('alice\'s balance is: ' + (await alice.balance()).total) //100N - return { alice, dao }; -} + const config = { name: 'sputnik', purpose: 'testing', metadata: '' }; + const policy = [root.accountId]; -export const STORAGE_PER_BYTE = new BN('10000000000000000000'); + //for short let's call it just dao + const dao = await deployAndInit({ + root, + subContractId: 'dao', + code: '../res/sputnikdao2.wasm', + init: options?.skipInit ? undefined : { + methodName: 'new', + args: { config, policy }, + }, + initialBalance: toYocto('200'), + }); -export const workspace = Workspace.init(async ({ root }) => { - return initWorkspace(root); -}); + const factory = options?.factory ? await deployAndInit({ + root, + subContractId: 'factory', + code: '../../sputnikdao-factory2/res/sputnikdao_factory2.wasm', + init: { + methodName: 'new', + args: {}, + options: { + gas: tGas(300), + }, + }, // 300 Tags + initialBalance: toYocto('500'), + }) : undefined; -export const workspaceWithoutInit = Workspace.init(async ({ root }) => { - const alice = await root.createAccount('alice'); + // Save state for test runs, it is unique for each test + t.context.worker = worker; + t.context.accounts = { + root, + alice, + dao, + factory, + }; + }); - //for short let's call it just dao - const dao = await root.createAndDeploy('dao', '../res/sputnikdao2.wasm', { - initialBalance: toYocto('200'), + test.afterEach.always(async (t) => { + // Stop Sandbox server + await t.context.worker.tearDown().catch((error) => { + console.log('Failed to stop the Sandbox:', error); + }); }); - return { alice, dao }; -}); -export const workspaceWithFactory = Workspace.init(async ({ root }) => { - const factory = await root.createAndDeploy( - 'factory', - '../../sputnikdao-factory2/res/sputnikdao_factory2.wasm', - { - initialBalance: toYocto('500'), - }, - ); - await factory.call(factory.accountId, 'new', {}, { gas: tGas(300) }); - return { factory }; -}); + return test; +} + +export const STORAGE_PER_BYTE = new BN('10000000000000000000'); export async function initTestToken(root: NearAccount) { - const testToken = await root.createAndDeploy( - 'test-token', - '../../test-token/res/test_token.wasm', - { - method: 'new', - initialBalance: toYocto('200'), + const testToken = await deployAndInit({ + root, + subContractId: 'test-token', + code: '../../test-token/res/test_token.wasm', + init: { + methodName: 'new', }, - ); + initialBalance: toYocto('200'), + }); return testToken; } @@ -64,19 +120,20 @@ export async function initStaking( dao: NearAccount, testToken: NearAccount, ) { - const staking = await root.createAndDeploy( - 'staking', - '../../sputnik-staking/res/sputnik_staking.wasm', - { - method: 'new', + const staking = await deployAndInit({ + root, + subContractId: 'staking', + code: '../../sputnik-staking/res/sputnik_staking.wasm', + init: { + methodName: 'new', args: { owner_id: dao, token_id: testToken, unstake_period: '100000000000', }, - initialBalance: toYocto('100'), }, - ); + initialBalance: toYocto('100'), + }); return staking; } @@ -106,6 +163,7 @@ export async function setStakingId( } export const regCost = STORAGE_PER_BYTE.mul(new BN(16)); +export const DAO_WASM_BYTES: Uint8Array = fs.readFileSync('../res/sputnikdao2.wasm'); export async function registerAndDelegate( dao: NearAccount, @@ -261,7 +319,7 @@ export async function giveupBountyRaw( dao: NearAccount, proposalId: number, ) { - return await alice.call_raw(dao, 'bounty_giveup', { id: proposalId }); + return await alice.callRaw(dao, 'bounty_giveup', { id: proposalId }); } export async function voteApprove( @@ -281,3 +339,9 @@ export async function voteApprove( }, ); } + +export type ProposalStatus = 'InProgress' | 'Approved' | 'Rejected' | 'Removed' | 'Expired' | 'Moved' | 'Failed'; + +export interface Proposal { + status: ProposalStatus +}; diff --git a/sputnikdao2/tests-ava/__tests__/views.ava.ts b/sputnikdao2/tests-ava/__tests__/views.ava.ts index 5ba2ea957..63d4b64ad 100644 --- a/sputnikdao2/tests-ava/__tests__/views.ava.ts +++ b/sputnikdao2/tests-ava/__tests__/views.ava.ts @@ -1,19 +1,19 @@ import { - Workspace, BN, NearAccount, captureError, toYocto, tGas, ONE_NEAR, -} from 'near-workspaces-ava'; +} from 'near-workspaces'; import { - workspace, + initWorkspace, initStaking, initTestToken, setStakingId, registerAndDelegate, STORAGE_PER_BYTE, + deployAndInit, } from './utils'; import { DEADLINE, @@ -24,13 +24,17 @@ import { } from './utils'; import * as fs from 'fs'; -workspace.test('View method version', async (test, { alice, root, dao }) => { - test.log('Version:'); - test.log(await dao.view('version')); - test.is(await dao.view('version'), '2.0.0'); +const test = initWorkspace(); + +test('View method version', async (t) => { + const { alice, root, dao } = t.context.accounts; + t.log('Version:'); + t.log(await dao.view('version')); + t.is(await dao.view('version'), '2.3.1'); }); -workspace.test('View method get_config', async (test, { root }) => { +test('View method get_config', async (t) => { + const { root } = t.context.accounts; const config = { name: 'sputnikda2', purpose: 'testing get_config', @@ -38,15 +42,21 @@ workspace.test('View method get_config', async (test, { root }) => { }; const policy = [root.accountId]; - const bob = await root.createAndDeploy('bob', '../res/sputnikdao2.wasm', { - method: 'new', - args: { config, policy }, + const bob = await deployAndInit({ + root, + subContractId: 'bob', + code: '../res/sputnikdao2.wasm', + init: { + methodName: 'new', + args: { config, policy } + }, initialBalance: toYocto('200'), }); - test.deepEqual(await bob.view('get_config'), config); + t.deepEqual(await bob.view('get_config'), config); }); -workspace.test('View method get_policy', async (test, { root }) => { +test('View method get_policy', async (t) => { + const { root } = t.context.accounts; const config = { name: 'sputnikda2', purpose: 'testing get_policy', @@ -54,9 +64,14 @@ workspace.test('View method get_policy', async (test, { root }) => { }; const versionedPolicy = [root.accountId]; - const bob = await root.createAndDeploy('bob', '../res/sputnikdao2.wasm', { - method: 'new', - args: { config, policy: versionedPolicy }, + const bob = await deployAndInit({ + root, + subContractId: 'bob', + code: '../res/sputnikdao2.wasm', + init: { + methodName: 'new', + args: { config, policy: versionedPolicy }, + }, initialBalance: toYocto('200'), }); const policy = { @@ -92,24 +107,26 @@ workspace.test('View method get_policy', async (test, { root }) => { bounty_bond: '1000000000000000000000000', bounty_forgiveness_period: '86400000000000', }; - test.deepEqual(await bob.view('get_policy'), policy); + t.deepEqual(await bob.view('get_policy'), policy); }); -workspace.test( +test( 'View method get_staking_contract', - async (test, { alice, root, dao }) => { - test.is(await dao.view('get_staking_contract'), ''); + async (t) => { + const { alice, root, dao } = t.context.accounts; + t.is(await dao.view('get_staking_contract'), ''); //To set the staking_id const testToken = await initTestToken(root); const staking = await initStaking(root, dao, testToken); await setStakingId(root, dao, staking); - test.is(await dao.view('get_staking_contract'), staking.accountId); + t.is(await dao.view('get_staking_contract'), staking.accountId); }, ); -workspace.test('View has_blob', async (test, { alice, root, dao }) => { +test('View has_blob', async (t) => { + const { alice, root, dao } = t.context.accounts; const DAO_WASM_BYTES: Uint8Array = fs.readFileSync( '../res/sputnikdao2.wasm', ); @@ -118,20 +135,21 @@ workspace.test('View has_blob', async (test, { alice, root, dao }) => { gas: tGas(300), }); - test.true(await dao.view('has_blob', { hash: hash })); + t.true(await dao.view('has_blob', { hash: hash })); await root.call(dao, 'remove_blob', { hash: hash, }); - test.false(await dao.view('has_blob', { hash: hash })); + t.false(await dao.view('has_blob', { hash: hash })); }); -workspace.test( +test( 'View get_locked_storage_amount', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; const beforeProposal = new BN( await dao.view('get_locked_storage_amount'), ); - test.log('Locked amount: ' + beforeProposal); + t.log('Locked amount: ' + beforeProposal); await root.call( dao, 'add_proposal', @@ -148,15 +166,16 @@ workspace.test( const afterProposal = new BN( await dao.view('get_locked_storage_amount'), ); - test.assert(beforeProposal.lt(afterProposal)); + t.assert(beforeProposal.lt(afterProposal)); }, ); -workspace.test( +test( 'View get_available_amount', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; const beforeProposal = new BN(await dao.view('get_available_amount')); - test.log('Available amount: ' + beforeProposal); + t.log('Available amount: ' + beforeProposal); await root.call( dao, 'add_proposal', @@ -171,17 +190,18 @@ workspace.test( }, ); const afterProposal = new BN(await dao.view('get_available_amount')); - test.assert(beforeProposal.gt(afterProposal)); + t.assert(beforeProposal.gt(afterProposal)); }, ); -workspace.test( +test( 'View methods for delegation', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; const testToken = await initTestToken(root); const staking = await initStaking(root, dao, testToken); const randomAmount = new BN('10087687667869'); - const bob = await root.createAccount('bob'); + const bob = await root.createSubAccount('bob'); await setStakingId(root, dao, staking); @@ -199,13 +219,13 @@ workspace.test( ); //Test delegation_balance_of - test.deepEqual( + t.deepEqual( new BN( await dao.view('delegation_balance_of', { account_id: alice }), ), randomAmount, ); - test.deepEqual( + t.deepEqual( new BN( await dao.view('delegation_balance_of', { account_id: bob }), ), @@ -213,13 +233,13 @@ workspace.test( ); //Test delegation_total_supply - test.deepEqual( + t.deepEqual( new BN(await dao.view('delegation_total_supply')), randomAmount.muln(3), ); //Test delegation_balance_ratio - test.deepEqual( + t.deepEqual( await dao.view('delegation_balance_ratio', { account_id: alice }), [ await dao.view('delegation_balance_of', { account_id: alice }), @@ -229,14 +249,15 @@ workspace.test( }, ); -workspace.test( +test( 'View methods for proposals', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; //Test get_last_proposal_id - test.is(await dao.view('get_last_proposal_id'), 0); + t.is(await dao.view('get_last_proposal_id'), 0); //Test get_proposals - test.deepEqual( + t.deepEqual( await dao.view('get_proposals', { from_index: 0, limit: 100 }), [], ); @@ -275,46 +296,47 @@ workspace.test( const proposalAlice: any = await dao.view('get_proposal', { id: 0 }); //Test get_proposal - test.is(proposalAlice.proposer, realProposalAlice.proposer); - test.is(proposalAlice.description, realProposalAlice.description); - test.is(proposalAlice.status, realProposalAlice.status); - test.deepEqual( + t.is(proposalAlice.proposer, realProposalAlice.proposer); + t.is(proposalAlice.description, realProposalAlice.description); + t.is(proposalAlice.status, realProposalAlice.status); + t.deepEqual( proposalAlice.vote_counts, realProposalAlice.vote_counts, ); - test.deepEqual(proposalAlice.votes, realProposalAlice.votes); - test.deepEqual(proposalAlice.kind, realProposalAlice.kind); + t.deepEqual(proposalAlice.votes, realProposalAlice.votes); + t.deepEqual(proposalAlice.kind, realProposalAlice.kind); //Test get_last_proposal_id - test.deepEqual(await dao.view('get_last_proposal_id'), 1); + t.deepEqual(await dao.view('get_last_proposal_id'), 1); //Test get_proposals const proposals: any = await dao.view('get_proposals', { from_index: 0, limit: 100, }); - test.is(proposals[0].proposer, realProposalAlice.proposer); - test.is(proposals[0].description, realProposalAlice.description); - test.is(proposals[0].status, realProposalAlice.status); - test.deepEqual(proposals[0].vote_counts, realProposalAlice.vote_counts); - test.deepEqual(proposals[0].votes, realProposalAlice.votes); - test.deepEqual(proposals[0].kind, realProposalAlice.kind); + t.is(proposals[0].proposer, realProposalAlice.proposer); + t.is(proposals[0].description, realProposalAlice.description); + t.is(proposals[0].status, realProposalAlice.status); + t.deepEqual(proposals[0].vote_counts, realProposalAlice.vote_counts); + t.deepEqual(proposals[0].votes, realProposalAlice.votes); + t.deepEqual(proposals[0].kind, realProposalAlice.kind); //Should panic if the proposal with the given id doesn't exist const errorString = await captureError( async () => await dao.view('get_proposal', { id: 10 }), ); - test.regex(errorString, /ERR_NO_PROPOSAL/); + t.regex(errorString, /ERR_NO_PROPOSAL/); }, ); -workspace.test( +test( 'View methods for bounties', - async (test, { alice, root, dao }) => { + async (t) => { + const { alice, root, dao } = t.context.accounts; //Test get_last_bounty_id - test.is(await dao.view('get_last_bounty_id'), 0); + t.is(await dao.view('get_last_bounty_id'), 0); //Test get_bounties - test.deepEqual( + t.deepEqual( await dao.view('get_bounties', { from_index: 0, limit: 100 }), [], ); @@ -332,19 +354,19 @@ workspace.test( await voteOnBounty(root, dao, proposalId); //Test get_last_bounty_id - test.is(await dao.view('get_last_bounty_id'), 1); + t.is(await dao.view('get_last_bounty_id'), 1); //Test get_bounties - test.deepEqual( + t.deepEqual( await dao.view('get_bounties', { from_index: 0, limit: 100 }), [bounty], ); //Test get_bounty - test.deepEqual(await dao.view('get_bounty', { id: 0 }), bounty); + t.deepEqual(await dao.view('get_bounty', { id: 0 }), bounty); await claimBounty(alice, dao, proposalId); //Test get_bounty_number_of_claims - test.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); + t.is(await dao.view('get_bounty_number_of_claims', { id: 0 }), 1); //Test get_bounty_claims const realClaim = { bounty_id: 0, @@ -354,14 +376,14 @@ workspace.test( const claims: any = await dao.view('get_bounty_claims', { account_id: alice.accountId, }); - test.is(claims[0].bounty_id, realClaim.bounty_id); - test.is(claims[0].deadline, realClaim.deadline); - test.is(claims[0].completed, realClaim.completed); + t.is(claims[0].bounty_id, realClaim.bounty_id); + t.is(claims[0].deadline, realClaim.deadline); + t.is(claims[0].completed, realClaim.completed); //Should panic if the bounty with the given id doesn't exist const errorString = await captureError( async () => await dao.view('get_bounty', { id: 10 }), ); - test.regex(errorString, /ERR_NO_BOUNTY/); + t.regex(errorString, /ERR_NO_BOUNTY/); }, ); diff --git a/sputnikdao2/tests-ava/ava.config.cjs b/sputnikdao2/tests-ava/ava.config.cjs index e8085ce14..ea3fdf5ab 100644 --- a/sputnikdao2/tests-ava/ava.config.cjs +++ b/sputnikdao2/tests-ava/ava.config.cjs @@ -1,4 +1,14 @@ +require('util').inspect.defaultOptions.depth = 5; // Increase AVA's printing depth + module.exports = { - ...require('near-workspaces-ava/ava.config.cjs'), - concurrency: 1, + timeout: '300000', + files: ['**/*.ava.ts', '**/*.ava.js', '!examples/**/*.ava.js'], + failWithoutAssertions: false, + extensions: [ + 'ts', + 'js', + ], + require: [ + 'ts-node/register', + ], }; diff --git a/sputnikdao2/tests-ava/ava.testnet.config.cjs b/sputnikdao2/tests-ava/ava.testnet.config.cjs index f1d9a8f1d..032980e93 100644 --- a/sputnikdao2/tests-ava/ava.testnet.config.cjs +++ b/sputnikdao2/tests-ava/ava.testnet.config.cjs @@ -1,10 +1,12 @@ module.exports = { - ...require('near-workspaces-ava/ava.testnet.config.cjs'), ...require('./ava.config.cjs'), }; -// Add files you only want to run in Sandbox mode here +module.exports.environmentVariables = { + NEAR_WORKSPACES_NETWORK: 'testnet', +}; + module.exports.files.push( - // '!__tests__/example-file-name*', - // '!__tests__/another-example-file-name*', + '!__tests__/02*', + '!__tests__/05*', ); diff --git a/sputnikdao2/tests-ava/package.json b/sputnikdao2/tests-ava/package.json index 9ea2629b8..c3fbb8af8 100644 --- a/sputnikdao2/tests-ava/package.json +++ b/sputnikdao2/tests-ava/package.json @@ -2,11 +2,14 @@ "private": true, "scripts": { "format": "prettier --write \"__tests__/*.{js,jsx,ts,tsx}\" --tab-width=4 --single-quote --trailing-comma all", - "test": "near-workspaces-ava", - "test:testnet": "near-workspaces-ava --config ./ava.testnet.config.cjs" + "test": "ava", + "test:testnet": "ava --config ./ava.testnet.config.cjs" }, "devDependencies": { - "near-workspaces-ava": "^1.1.0", - "prettier": "^2.5.1" + "@types/bn.js": "^5.1.6", + "ava": "^6.1.3", + "near-workspaces": "^3.5.0", + "prettier": "^2.5.1", + "ts-node": "^10.9.2" } } diff --git a/sputnikdao2/tests-ava/tsconfig.json b/sputnikdao2/tests-ava/tsconfig.json index c4e598d35..3056100e0 100644 --- a/sputnikdao2/tests-ava/tsconfig.json +++ b/sputnikdao2/tests-ava/tsconfig.json @@ -1,3 +1,13 @@ { - "extends": "near-workspaces-ava/tsconfig.ava.json" -} \ No newline at end of file + "compilerOptions": { + "target": "ES2019", + "module": "commonjs", + "lib": [ + "es2020", + "ESNext" + ], + "allowJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/sputnikdao2_original.wasm b/sputnikdao2_original.wasm new file mode 100644 index 000000000..c89f4a571 Binary files /dev/null and b/sputnikdao2_original.wasm differ diff --git a/test-token/res/test_token.wasm b/test-token/res/test_token.wasm index cd0a80d5e..760f72444 100755 Binary files a/test-token/res/test_token.wasm and b/test-token/res/test_token.wasm differ