diff --git a/docs/forcestop_contract/forcestop_contract.md b/docs/forcestop_contract/forcestop_contract.md new file mode 100644 index 000000000..7fab0cfcb --- /dev/null +++ b/docs/forcestop_contract/forcestop_contract.md @@ -0,0 +1,33 @@ +# Force Stop Contract + + +## Keys +| key | type | value | description | +| :-------------------------- | --------: | :----------------------- | :------------------------------------------------ | +| `%s%s__disabled__{address}` | `boolean` | `true` | Contract status | +| `%s__forceStopPermission` | `String` | `{address1}__{address2}` | List of Addresses allowed to force stop Contracts | + + +## Functions +Enable/Disable contracts: +``` +@Callable(i) +func forceStopContract(address: String, disable: Boolean) +``` + +Add address to `%s__forceStopPermission` list: +``` +@Callable(i) +func addPermission(address: String) +``` + +Remove address from `%s__forceStopPermission` list: +``` +@Callable(i) +func removePermission(address: String) +``` + +# TESTNET +| Name | Address | Public key | +| --------- | ------------------------------------- | ---------------------------------------------- | +| forcestop | `3MqsDXvFU9WM8hEzCcrw2aVGtVimCgbNeXD` | `4dNR2Up6mpwUEcy9WQuE2wAzR7HRURa6Pk9jrFKgcHqB` | \ No newline at end of file diff --git a/migrations/2024_04_08_force_stop_contract/01_forcestop_data_tx.json b/migrations/2024_04_08_force_stop_contract/01_forcestop_data_tx.json new file mode 100644 index 000000000..e6e8587a3 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/01_forcestop_data_tx.json @@ -0,0 +1,19 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "3hP4hEyMLcQrPrEhRkFjYvqKWMFTEsoT7ws1sN94KgwW", + "data": [ + { + "key": "%s__managerVaultAddress", + "type": "string", + "value": "3P4QKaU3NfbfxwF9aB5Kz4rvxFnLc4mBqYN" + }, + { + "key": "%s__forceStopPermission", + "type": "string", + "value": "" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/02_algo_cons_data_tx.json b/migrations/2024_04_08_force_stop_contract/02_algo_cons_data_tx.json new file mode 100644 index 000000000..ffc9e664a --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/02_algo_cons_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "3g4LaykUG5wGWdSeML6WkuKVnzkREN7uAWEK1UR1uUGB", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/03_algo_aggr.data_tx.json b/migrations/2024_04_08_force_stop_contract/03_algo_aggr.data_tx.json new file mode 100644 index 000000000..3acd44e2d --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/03_algo_aggr.data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "BwKfcbhBESamhFtQJPk6cWHZwmdSGfmvQo2e3cgJXNF2", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/04_algo_moder_data_tx.json b/migrations/2024_04_08_force_stop_contract/04_algo_moder_data_tx.json new file mode 100644 index 000000000..e8f0df713 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/04_algo_moder_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "7AkYRHYaiaxocnoA7YJv29kujpmkVBJxFppUkasDe4Ej", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/05_asset_store_data_tx.json b/migrations/2024_04_08_force_stop_contract/05_asset_store_data_tx.json new file mode 100644 index 000000000..bb078c3fd --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/05_asset_store_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "Ajaez2Qx6UNQu7rT1BceE2hNDrQd52fLHsoq7Js5q3by", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/06_boosting_data_tx.json b/migrations/2024_04_08_force_stop_contract/06_boosting_data_tx.json new file mode 100644 index 000000000..46c8191d9 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/06_boosting_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "6PdA345SW1zresPEKuWiLPsh9Ku14mFDW6cZtpCQrp3E", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/07_emission_data_tx.json b/migrations/2024_04_08_force_stop_contract/07_emission_data_tx.json new file mode 100644 index 000000000..7a4b9ec6d --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/07_emission_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "FoGdD9qz4AFW3pdmp1QCA5vvo9wn3fTbv4JnYrWZe3BB", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/08_factory_v2_data_tx.json b/migrations/2024_04_08_force_stop_contract/08_factory_v2_data_tx.json new file mode 100644 index 000000000..320c31b9f --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/08_factory_v2_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "HBWgh7DKPyzCnEXKJAJ5dKQ3jmPtMhGD78tt6jRdkV61", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/09_l2mp_staking_data_tx.json b/migrations/2024_04_08_force_stop_contract/09_l2mp_staking_data_tx.json new file mode 100644 index 000000000..f379e31b6 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/09_l2mp_staking_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "5erVrXGwDf4uTNPpGnGNKjHcwFUTztTpFTvj8ZBrLq57", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/10_l2mp_swap_data_tx.json b/migrations/2024_04_08_force_stop_contract/10_l2mp_swap_data_tx.json new file mode 100644 index 000000000..0e637c669 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/10_l2mp_swap_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "EfRKQWb1FWaWmo9fkorRwMA4BrSzUWNAVhXqB9vLmv7g", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/11_lp_staking_pools_data_tx.json b/migrations/2024_04_08_force_stop_contract/11_lp_staking_pools_data_tx.json new file mode 100644 index 000000000..3609d7538 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/11_lp_staking_pools_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "8f1rQCPEcUfjitkvwMuxK9bwf2jupzdaHjtKTW5J45Rf", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/12_lp_staking_v2_data_tx.json b/migrations/2024_04_08_force_stop_contract/12_lp_staking_v2_data_tx.json new file mode 100644 index 000000000..929d2ef5d --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/12_lp_staking_v2_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "6ggGUvisvwXkZVUaHNi5huvqSYFSVk3T1RiD5VnRq4DY", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/13_otc_multiasset_data_tx.json b/migrations/2024_04_08_force_stop_contract/13_otc_multiasset_data_tx.json new file mode 100644 index 000000000..c9a5e4e5b --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/13_otc_multiasset_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "DziAWik7v792pPHDeEqmkowKnGawQAGHvQVBRQx2s9gB", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/14_proposal_data_tx.json b/migrations/2024_04_08_force_stop_contract/14_proposal_data_tx.json new file mode 100644 index 000000000..3aa3a44de --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/14_proposal_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "2VkbtS8CodmbZqAounEpDSf4dAfLoCWfEEGHZK7qtbeD", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/15_referral_data_tx.json b/migrations/2024_04_08_force_stop_contract/15_referral_data_tx.json new file mode 100644 index 000000000..b80955043 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/15_referral_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "AEV7LzxNAcdrjC2NC4182iTxuyTyqfCLzqJ14TXnVSCm", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/16_slippage_data_tx.json b/migrations/2024_04_08_force_stop_contract/16_slippage_data_tx.json new file mode 100644 index 000000000..664e8e9f2 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/16_slippage_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "9Ps647vMbFkxBfwxidninuYHYNazJGxBssESpf6Pya34", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/17_staking_data_tx.json b/migrations/2024_04_08_force_stop_contract/17_staking_data_tx.json new file mode 100644 index 000000000..91e3b0309 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/17_staking_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "EAtbDa63mS5omrvW7Pfr7DKWEVLuReJtu72vfiBLRXsx", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/18_swap_data_tx.json b/migrations/2024_04_08_force_stop_contract/18_swap_data_tx.json new file mode 100644 index 000000000..c26a3bc4e --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/18_swap_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "GERKRN49XMxhcF1QrLqUAazuipfaJc7KQGq2jChkA4pb", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/19_user_pools_data_tx.json b/migrations/2024_04_08_force_stop_contract/19_user_pools_data_tx.json new file mode 100644 index 000000000..742dbb739 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/19_user_pools_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "52HVeUFDYgRVXrPNfdX4WinZBFdizmuf1vqmbd8ExAZS", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/20_vesting_data_tx.json b/migrations/2024_04_08_force_stop_contract/20_vesting_data_tx.json new file mode 100644 index 000000000..9f9d2709a --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/20_vesting_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "5k2UWS7wygmRcxBwEgn852bk5KPTt5435nWprBqxXooc", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/21_vesting_multiasset_data_tx.json b/migrations/2024_04_08_force_stop_contract/21_vesting_multiasset_data_tx.json new file mode 100644 index 000000000..39a54064e --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/21_vesting_multiasset_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "CCXSSdRtixuMuZvCAKzJ5Kr1YR74z994WNqq2ooMWBB", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/migrations/2024_04_08_force_stop_contract/TEMPLATE_contract_data_tx.json b/migrations/2024_04_08_force_stop_contract/TEMPLATE_contract_data_tx.json new file mode 100644 index 000000000..865f58fa0 --- /dev/null +++ b/migrations/2024_04_08_force_stop_contract/TEMPLATE_contract_data_tx.json @@ -0,0 +1,14 @@ +{ + "type": 12, + "fee": 500000, + "feeAssetId": null, + "version": 2, + "senderPublicKey": "", + "data": [ + { + "key": "%s__forceStopContract", + "type": "string", + "value": "3PPxptrgcPnwgtRBcryN7qyTxSRKZdPtRU3" + } + ] +} diff --git a/ride/algo.ride b/ride/algo.ride index e8d088e9d..715c522d6 100644 --- a/ride/algo.ride +++ b/ride/algo.ride @@ -442,9 +442,25 @@ func commonExecute(operationType: String, baseAssetStr: String, userAddressStr: :+ TotalLockedStringEntry("DECREMENT", keyTotalLockedByUser(internalBaseAssetStr, userAddressStr), diffTuple._2) } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + # read only to provide data #@Callable - workaround to disable minification func privateCurrentSysParamsREST(baseAssetStr: String) = { + strict checkForceStop = isForceStopped() let baseAssetId = baseAssetStr.fromBase58String() let cfgArray = readAssetCfgOrFail(baseAssetStr) @@ -490,6 +506,7 @@ func adminRegisterAsset(baseAssetStr: String, shareAssetName: St topupIntervalInBlocks: Int, topupMaxNegativePart: Int, topupManagerAddress: String, submitLimitsBaseMax: Int, submitLimitsBaseReset: Int, submitLimitsShareMax: Int, submitLimitsShareReset: Int, adminAddress: String) = { + strict checkForceStop = isForceStopped() # TODO submitLimitsBaseMax and submitLimitsShareMax can be < 0 let baseAssetId = baseAssetStr.fromBase58String() let bothAssetsDecimals = assetInfo(baseAssetId).value().decimals @@ -574,6 +591,7 @@ func adminRegisterAsset(baseAssetStr: String, shareAssetName: St @Callable(i) func shutdownSubmits(internalBaseAssetId: Int) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() let internalBaseAssetIdStr = internalBaseAssetId.toString() let baseAssetIdStr = getStringOrFail(keyMappingsInternal2baseAssetId(internalBaseAssetId)) @@ -585,6 +603,7 @@ func shutdownSubmits(internalBaseAssetId: Int) = { @Callable(i) func submitPut() = { + strict checkForceStop = isForceStopped() let pmt = i.payments[0].value() let inAmount = pmt.amount let inAssetId = pmt.assetId.value() @@ -594,6 +613,7 @@ func submitPut() = { @Callable(i) func submitGet() = { + strict checkForceStop = isForceStopped() let pmt = i.payments[0].value() let inAmount = pmt.amount let inAssetId = pmt.assetId.value() @@ -604,16 +624,19 @@ func submitGet() = { @Callable(i) func executePut(baseAssetStr: String, userAddressStr: String, submitTxIdStr: String) = { + strict checkForceStop = isForceStopped() commonExecute("P", baseAssetStr, userAddressStr, submitTxIdStr) } @Callable(i) func executeGet(baseAssetStr: String, userAddressStr: String, submitTxIdStr: String) = { + strict checkForceStop = isForceStopped() commonExecute("G", baseAssetStr, userAddressStr, submitTxIdStr) } @Callable(i) func operationsMutex(baseAssetStr: String) = { + strict checkForceStop = isForceStopped() let assetCfgArray = readAssetCfgOrFail(baseAssetStr) let topUpManagerAddressStr = assetCfgArray[IdxCfgTopupManagerAddress] let internalBaseAssetStr = assetCfgArray[IdxCfgInternalBaseAsset] @@ -624,6 +647,7 @@ func operationsMutex(baseAssetStr: String) = { @Callable(i) func topUpBalance(baseAssetStr: String, income: Int) = { + strict checkForceStop = isForceStopped() let baseAssetId = baseAssetStr.fromBase58String() let cfg = readAssetCfgOrFail(baseAssetStr) @@ -691,6 +715,7 @@ func topUpBalance(baseAssetStr: String, income: Int) = { @Callable(i) func currentSysParamsREST(baseAssetStr: String)= { + strict checkForceStop = isForceStopped() let sysStateTuple = privateCurrentSysParamsREST(baseAssetStr) # (price, decimalsMultPrice, baseAssetBalance, -1, baseAssetBalanceWCO, shareEmission, currIterTotalInBaseAmount, currIterTotalInShareAmount, totalLockedOutBaseAmount, totalLockedOutShareAmount, decimalsMultBothAssets, priceATH) let price = sysStateTuple._1.value diff --git a/ride/assets_store.ride b/ride/assets_store.ride index 38e67ab41..c1f345a61 100644 --- a/ride/assets_store.ride +++ b/ride/assets_store.ride @@ -142,8 +142,24 @@ func setVerifiedActions(assetId: String, verified: Boolean) = { [IntegerEntry(assetId.keyStatus(), verified.verifiedToStatus())] ++ labelCommunityVerifiedActions } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func setAssetName(assetID: String, assetName: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() [ StringEntry(keyAssetName(assetID), assetName) @@ -152,6 +168,7 @@ func setAssetName(assetID: String, assetName: String) = { @Callable(i) func setAssetDescription(assetID: String, assetDescription: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() [ StringEntry(keyAssetDescription(assetID), assetDescription) @@ -160,6 +177,7 @@ func setAssetDescription(assetID: String, assetDescription: String) = { @Callable(i) func constructor(userPoolsContract: String, labels: List[String]) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() strict checksAddress = userPoolsContract.addressFromString().valueOrErrorMessage("Invalid address") @@ -171,6 +189,7 @@ func constructor(userPoolsContract: String, labels: List[String]) = { @Callable(i) func constructorV2(factoryContract: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() strict checkAddress = factoryContract.addressFromString().valueOrErrorMessage("invalid factory contract address") @@ -182,6 +201,7 @@ func constructorV2(factoryContract: String) = { # add amount, price and lp assets link @Callable(i) func addAssetsLink(amountAsset: String, priceAsset: String, lpAsset: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() # TODO: amountAsset должен быть создан? [ @@ -192,6 +212,7 @@ func addAssetsLink(amountAsset: String, priceAsset: String, lpAsset: String) = { @Callable(i) func increaseAssetPoolsNumber(assetId: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() let assetPoolsNumberKey = assetId.keyAssetPoolsNumber() let assetPoolsNumber = assetPoolsNumberKey.getInteger().valueOrElse(0) @@ -201,6 +222,7 @@ func increaseAssetPoolsNumber(assetId: String) = { @Callable(i) func createLabel(label: String) = { + strict checkForceStop = isForceStopped() let labels = getLabels() strict checkCaller = i.mustAdmin() strict checkLabel = [ @@ -213,6 +235,7 @@ func createLabel(label: String) = { @Callable(i) func dropLabel(label: String) = { + strict checkForceStop = isForceStopped() let labels = getLabels() strict checkCaller = i.mustManager() strict checkLabel = labels.containsElement(label) || "Label doesn't exist".throw() @@ -222,6 +245,7 @@ func dropLabel(label: String) = { @Callable(i) func addLabel(assetId: String, label: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() assetId.addLabelActions(label, true) @@ -229,6 +253,7 @@ func addLabel(assetId: String, label: String) = { @Callable(i) func deleteLabel(assetId: String, label: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() assetId.deleteLabelActions(label, true) @@ -236,6 +261,7 @@ func deleteLabel(assetId: String, label: String) = { @Callable(i) func updateTicker(assetId: String, ticker: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() strict checkAsset = assetId.isCreated() || throwNoAsset() strict checkTicker = ticker != "" || throwInvalidTicker() @@ -270,6 +296,7 @@ func updateTicker(assetId: String, ticker: String) = { @Callable(i) func deleteTicker(assetId: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() let ticker = assetId.keyAssetIdToTicker().getString().valueOrErrorMessage("Invalid asset") @@ -281,6 +308,7 @@ func deleteTicker(assetId: String) = { @Callable(i) func createOrUpdate(assetId: String, logo: String, verified: Boolean) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() # causes an error in factory_v2.activateNewPool @@ -299,6 +327,7 @@ func createOrUpdate(assetId: String, logo: String, verified: Boolean) = { @Callable(i) func setLogo(assetId: String, logo: String) = { + strict checkForceStop = isForceStopped() strict checks = [ i.mustAdmin(), assetId.isCreated() || throwNoAsset() @@ -311,6 +340,7 @@ func setLogo(assetId: String, logo: String) = { @Callable(i) func getAssetLogoREADONLY(assetId: String) = { + strict checkForceStop = isForceStopped() let logo = keyLogo(assetId).getString().valueOrElse("") (nil, logo) @@ -318,6 +348,7 @@ func getAssetLogoREADONLY(assetId: String) = { @Callable(i) func setVerified(assetId: String, verified: Boolean) = { + strict checkForceStop = isForceStopped() strict checks = [ i.mustAdmin(), assetId.isCreated() || throwNoAsset() @@ -332,6 +363,7 @@ func setVerified(assetId: String, verified: Boolean) = { @Callable(i) func onEliminate(assetId: String) = { + strict checkForceStop = isForceStopped() let poolsNumber = assetId.keyAssetPoolsNumber().getInteger().valueOrElse(0) let actions = if (poolsNumber > 0) then [] else [DeleteEntry(assetId.keyLogo())] @@ -340,17 +372,20 @@ func onEliminate(assetId: String) = { @Callable(i) func setAdmins(adminPubKeys: List[String]) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [StringEntry(keyAdminPubKeys(), adminPubKeys.makeString(SEP))] } @Callable(i) func isVerifiedREADONLY(assetId: String) = { + strict checkForceStop = isForceStopped() (nil, assetId.isVerified()) } @Callable(i) func deletePool(amountAssetId: String, priceAssetId: String) = { + strict checkForceStop = isForceStopped() let factoryContract = keyFactoryContract().getStringValue().addressFromStringValue() strict checkCaller = i.caller == factoryContract || i.mustManager() diff --git a/ride/boosting.ride b/ride/boosting.ride index 3ed56919f..ad029790f 100644 --- a/ride/boosting.ride +++ b/ride/boosting.ride @@ -681,8 +681,24 @@ func lockActions(i: Invocation, duration: Int) = { ], gWxAmountStart) } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func constructor(factoryAddressStr: String, lockAssetIdStr: String, minLockAmount: Int, minDuration: Int, maxDuration: Int, mathContract: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [IntegerEntry(keyNextUserNum(), 0), @@ -696,6 +712,7 @@ func constructor(factoryAddressStr: String, lockAssetIdStr: String, minLockAmoun @Callable(i) func lockRef(duration: Int, referrerAddress: String, signature: ByteVector) = { + strict checkForceStop = isForceStopped() let (lockActionsResult, gWxAmountStart) = i.lockActions(duration) let referralAddress = i.caller.toString() strict refInv = if (referrerAddress == "" || signature == base58'') then unit else { @@ -708,6 +725,7 @@ func lockRef(duration: Int, referrerAddress: String, signature: ByteVector) = { @Callable(i) func lock(duration: Int) = { + strict checkForceStop = isForceStopped() let (lockActionsResult, gWxAmountStart) = i.lockActions(duration) strict updateRefActivity = mathContract.invoke("updateReferralActivity", [i.caller.toString(), gWxAmountStart], []) @@ -716,6 +734,7 @@ func lock(duration: Int) = { @Callable(i) func increaseLock(deltaDuration: Int) = { + strict checkForceStop = isForceStopped() let cfgArray = readConfigArrayOrFail() let assetIdStr = cfgArray[IdxCfgAssetId] let assetId = assetIdStr.fromBase58String() @@ -770,6 +789,7 @@ func increaseLock(deltaDuration: Int) = { @Callable(i) func claimWxBoost(lpAssetIdStr: String, userAddressStr: String) = { + strict checkForceStop = isForceStopped() if (stakingContract != i.caller) then throwErr("permissions denied") else let (userBoostAvailable, dataState, debug) = internalClaimWxBoost(lpAssetIdStr, userAddressStr, false) @@ -778,12 +798,14 @@ func claimWxBoost(lpAssetIdStr: String, userAddressStr: String) = { @Callable(i) func claimWxBoostREADONLY(lpAssetIdStr: String, userAddressStr: String) = { + strict checkForceStop = isForceStopped() let (userBoostAvailable, dataState, debug) = internalClaimWxBoost(lpAssetIdStr, userAddressStr, true) ([], [userBoostAvailable, debug]) } @Callable(i) func unlock(userAddress: String) = { + strict checkForceStop = isForceStopped() let userRecordArray = readLockParamsRecordOrFail(userAddress) let userNumStr = userRecordArray[IdxLockUserNum] @@ -808,6 +830,7 @@ func unlock(userAddress: String) = { @Callable(i) func gwxUserInfoREADONLY(userAddress: String) = { + strict checkForceStop = isForceStopped() let gwxAmount = calcCurrentGwxAmount(userAddress) ([], [gwxAmount]) @@ -816,6 +839,7 @@ func gwxUserInfoREADONLY(userAddress: String) = { # for lp_staking_pools @Callable(i) func userMaxDurationREADONLY(userAddressStr: String) = { + strict checkForceStop = isForceStopped() let cfgArray = readConfigArrayOrFail() let maxLockDuration = cfgArray[IdxCfgMaxLockDuration].parseIntValue() @@ -835,6 +859,7 @@ func userMaxDurationREADONLY(userAddressStr: String) = { @Callable(i) func getUserGwxAmountAtHeightREADONLY(userAddress: String, targetHeight: Int) = { + strict checkForceStop = isForceStopped() let gwxAmount = userAddress.calcUserGwxAmountAtHeight(targetHeight) ([], gwxAmount) @@ -842,12 +867,14 @@ func getUserGwxAmountAtHeightREADONLY(userAddress: String, targetHeight: Int) = @Callable(i) func getTotalCachedGwxREADONLY() = { + strict checkForceStop = isForceStopped() ([], getTotalCachedGwx(true)) } # call this function when wxEmissionRate or boostCoeff changes @Callable(i) func onBoostEmissionUpdate() = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller == emissionContract || i.mustManager() refreshBoostEmissionIntegral() } @@ -864,6 +891,7 @@ func onBoostEmissionUpdate() = { # ╰ edge = false (falling) @Callable(i) func onStakedVoteUpdate(lpAssetIdStr: String, userAddressStr: String, edge: Boolean) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller == stakingContract || i.mustManager() let actions = lpAssetIdStr.refreshVoteStakedIntegral(userAddressStr, edge) @@ -872,21 +900,25 @@ func onStakedVoteUpdate(lpAssetIdStr: String, userAddressStr: String, edge: Bool @Callable(i) func getVotingResultStakedREADONLY(lpAssetIdStr: String) = { + strict checkForceStop = isForceStopped() ([], lpAssetIdStr.getVotingResultStaked()) } @Callable(i) func getVotingResultStakedIntegralREADONLY(lpAssetIdStr: String) = { + strict checkForceStop = isForceStopped() ([], lpAssetIdStr.getVotingResultStakedIntegral()) } @Callable(i) func getUserVoteFinalizedREADONLY(lpAssetIdStr: String, userAddressStr: String) = { + strict checkForceStop = isForceStopped() ([], lpAssetIdStr.getUserVoteFinalized(userAddressStr)) } @Callable(i) func getUserVoteStakedIntegralREADONLY(lpAssetIdStr: String, userAddressStr: String) = { + strict checkForceStop = isForceStopped() ([], lpAssetIdStr.getUserVoteStakedIntegral(userAddressStr)) } diff --git a/ride/emission.ride b/ride/emission.ride index fd3294ba1..6ed70f4d6 100644 --- a/ride/emission.ride +++ b/ride/emission.ride @@ -98,8 +98,24 @@ func mustManager(i: Invocation) = { } } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func constructor(factoryAddress: String, ratePerBlockMax: Int, ratePerBlock: Int, emissionStartBlock: Int, emissionDuration: Int, emissionStartTimestamp: Int, wxAssetIdStr: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [IntegerEntry(keyRatePerBlockMaxStartFrom(emissionStartTimestamp, emissionStartBlock), ratePerBlockMax), @@ -117,6 +133,7 @@ func constructor(factoryAddress: String, ratePerBlockMax: Int, ratePerBlock: Int @Callable(i) func constructorV2(votingVerifiedContractPrm: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() ( @@ -129,6 +146,7 @@ func constructorV2(votingVerifiedContractPrm: String) = { @Callable(i) func emit(amount: Int) = { + strict checkForceStop = isForceStopped() if (amount <= 0) then ([], []) else let factoryContract = readFactoryAddressOrFail() let factoryCfg = factoryContract.readFactoryCfgOrFail() @@ -145,6 +163,7 @@ func emit(amount: Int) = { @Callable(i) func burn() = { + strict checkForceStop = isForceStopped() let factoryContract = readFactoryAddressOrFail() let factoryCfg = factoryContract.readFactoryCfgOrFail() let boostingContract = factoryCfg.getBoostingAddressOrFail() @@ -168,6 +187,7 @@ func burn() = { @Callable(i) func gwxHoldersRewardUpdate() = { + strict checkForceStop = isForceStopped() let factoryContract = readFactoryAddressOrFail() let factoryCfg = factoryContract.readFactoryCfgOrFail() let gwxRewardsContract = factoryCfg.getGwxRewardAddressOrFail() @@ -187,11 +207,13 @@ func gwxHoldersRewardUpdate() = { @Callable(i) func getBoostCoeffREADONLY() = { + strict checkForceStop = isForceStopped() ([], boostCoeff) } @Callable(i) func changeRatePerBlock(newRatePerBlock: Int) = { + strict checkForceStop = isForceStopped() strict check = i.caller == votingEmissionRateContract || i.mustManager() || throwErr("should be invoked by votingEmissionRateContract") let factoryContract = readFactoryAddressOrFail() diff --git a/ride/factory_v2.ride b/ride/factory_v2.ride index c02b130fc..ab805f738 100644 --- a/ride/factory_v2.ride +++ b/ride/factory_v2.ride @@ -430,8 +430,25 @@ func isPoolEmpty(poolContractAddress: String) = { getAddressBalance(poolContractAddress, amountAssetId) == 0 && getAddressBalance(poolContractAddress, priceAssetId) == 0 } + +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throwErr() + else false +} +### End Force Stop function + @Callable(i) func getSwapContractREADONLY() = { + strict checkForceStop = isForceStopped() ([], getStringOrFail(swapContract()) ) @@ -439,6 +456,7 @@ func getSwapContractREADONLY() = { @Callable(i) func getPoolLeaseConfigREADONLY(poolAddress: String, assetId: String) = { + strict checkForceStop = isForceStopped() let (isLeasable, leasedRatio) = match this.getInteger(keyLeasedRatio(poolAddress, assetId)) { case r:Int => (true, min([100, max([0, r])])) case _ => match this.getInteger(keyLeasedRatioDefault(assetId)) { @@ -468,6 +486,7 @@ func getPoolLeaseConfigREADONLY(poolAddress: String, assetId: String) = { @Callable(i) func getLpAssetFromPoolAssetsREADONLY(asset1: String, asset2: String) = { + strict checkForceStop = isForceStopped() # we have 2 pools: WAVES/BTC (status 1) and BTC/WAVES (status 4). This hack to resolve swap path unless we make Pool Deletion if (asset1 == "8LQW8f7P5d5PZM7GtZEBgaqRPGSzS3DfPuiXrURJ4AJS" && asset2 == "WAVES") then { ([], @@ -491,6 +510,7 @@ func getLpAssetFromPoolAssetsREADONLY(asset1: String, asset2: String) = { @Callable(i) func getPoolAddressFromLpAssetREADONLY(lp: String) = { + strict checkForceStop = isForceStopped() ([], this.getString(keyMappingPoolLPAssetToPoolContractAddress(lp)).valueOrElse("") ) @@ -507,6 +527,7 @@ func constructor(stakingContract: String, boostingContract: String, idoContract: @Callable(i) func constructorV2(mathcherPub58Str: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() strict mathcerPub = mathcherPub58Str.fromBase58String().addressFromPublicKey() @@ -515,6 +536,7 @@ func constructorV2(mathcherPub58Str: String) = { @Callable(i) func constructorV3(daoContract: String, marketingContract: String, gwxRewardsContract: String, birdsContract: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() let factoryCfg = getStringOrFail(keyFactoryConfig()).split(SEP) @@ -538,6 +560,7 @@ func constructorV3(daoContract: String, marketingContract: String, gwxRewardsCon @Callable(i) func constructorV4(legacyFactoryContract: String, legacyPools: List[String]) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [ @@ -548,6 +571,7 @@ func constructorV4(legacyFactoryContract: String, legacyPools: List[String]) = { @Callable(i) func constructorV5(assetsStoreContract: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [ @@ -557,6 +581,7 @@ func constructorV5(assetsStoreContract: String) = { @Callable(i) func constructorV6(votingEmissionContract: String, priceAssets: List[String]) = { + strict checkForceStop = isForceStopped() strict cheks = [ i.mustManager(), votingEmissionContract.addressFromString() != unit || "invalid voting emission contract address" @@ -570,6 +595,7 @@ func constructorV6(votingEmissionContract: String, priceAssets: List[String]) = @Callable(i) func setAdmins(adminPubKeys: List[String]) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [StringEntry(keyAdminPubKeys(), adminPubKeys.makeString(SEP))] } @@ -591,6 +617,7 @@ func setAdmins(adminPubKeys: List[String]) = { # return: @Callable(i) func activateNewPool(poolAddress: String, amountAssetStr: String, priceAssetStr: String, lpAssetName: String, lpAssetDescr: String, poolWeight: Int, poolType: String, logo: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() if(getString(keyMappingPoolContractAddressToPoolAssets(poolAddress)).isDefined()) then throwErr("Pool address " + poolAddress + " already registered.") else let internalAmountAssetData = getInternalAssetIdOrCreate(amountAssetStr, 1) @@ -684,6 +711,7 @@ func activateNewPool(poolAddress: String, amountAssetStr: String, priceAssetStr: # return: @Callable(i) func managePool(poolAddress: String, newStatus: Int) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() let poolConfig = getPoolConfig(poolAddress) @@ -716,6 +744,7 @@ func managePool(poolAddress: String, newStatus: Int) = { @Callable(i) func emit(amountToEmit: Int) = { + strict checkForceStop = isForceStopped() let caller = i.caller let originCaller = i.originCaller @@ -742,6 +771,7 @@ func emit(amountToEmit: Int) = { @Callable(i) func burn(amountToBurn: Int) = { + strict checkForceStop = isForceStopped() let caller = i.caller let originCaller = i.originCaller let payment = i.payments[0].value() @@ -758,6 +788,7 @@ func burn(amountToBurn: Int) = { @Callable(i) func modifyWeight(lpAssetId: String, share: Int) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller.bytes.toBase58String() == this.getString(keyVotingEmissionContract()).valueOrElse("") || i.mustManager() let poolContractAddress = keyMappingPoolLPAssetToPoolContractAddress(lpAssetId).getStringOrFail() let poolWeightKey = poolContractAddress.keyPoolToWeight() @@ -783,6 +814,7 @@ func modifyWeight(lpAssetId: String, share: Int) = { @Callable(i) func modifyWeights(btcPoolAddress: String, ethPoolAddress: String, ethBtcPoolAddress: String, usdcUsdtPoolAddress: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() let ethBtcWeightNew = 5 * POOLWEIGHTMULT / 100 # 0.05 @@ -830,6 +862,7 @@ func modifyWeights(btcPoolAddress: String, ethPoolAddress: String, ethBtcPoolAdd @Callable(i) func checkWxEmissionPoolLabel(amountAssetId: String, priceAssetId: String) = { + strict checkForceStop = isForceStopped() let haveLabel = match keyWxEmission(amountAssetId, priceAssetId).getBoolean() { case b: Boolean => b case _ => false @@ -840,6 +873,7 @@ func checkWxEmissionPoolLabel(amountAssetId: String, priceAssetId: String) = { @Callable(i) func setWxEmissionPoolLabel(amountAssetId: String, priceAssetId: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustAdmin() ([BooleanEntry(keyWxEmission(amountAssetId, priceAssetId), true)], unit) @@ -848,6 +882,7 @@ func setWxEmissionPoolLabel(amountAssetId: String, priceAssetId: String) = { @Callable(i) func deleteWxEmissionPoolLabel(amountAssetId: String, priceAssetId: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller == this || i.mustAdmin() ([DeleteEntry(keyWxEmission(amountAssetId, priceAssetId))], unit) @@ -855,6 +890,7 @@ func deleteWxEmissionPoolLabel(amountAssetId: String, priceAssetId: String) = { @Callable(i) func onVerificationLoss(assetId: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller.bytes.toBase58String() == this.getString(keyAssetsStoreContract()).valueOrElse("") || i.mustManager() let priceAssets = getPriceAssets() strict checkPriceAssetsSize = priceAssets.size() > 0 || "invalid price assets".throwErr() @@ -871,6 +907,7 @@ func onVerificationLoss(assetId: String) = { @Callable(i) func isPoolOneTokenOperationsDisabledREADONLY(poolAddress: String) = { + strict checkForceStop = isForceStopped() let poolConfig = poolAddress.getPoolConfig() let assetsInternal = (poolConfig[idxIAmtAssetId].parseIntValue(), poolConfig[idxIPriceAssetId].parseIntValue()) let poolOneTokenOperationsDisabled = assetsInternal.keyPoolOneTokenOperationsDisabled().getBoolean().valueOrElse(false) @@ -881,6 +918,7 @@ func isPoolOneTokenOperationsDisabledREADONLY(poolAddress: String) = { @Callable(i) func isPoolSwapDisabledREADONLY(poolAddress: String) = { + strict checkForceStop = isForceStopped() let poolConfig = poolAddress.getPoolConfig() let assetsInternal = (poolConfig[idxIAmtAssetId].parseIntValue(), poolConfig[idxIPriceAssetId].parseIntValue()) let poolSwapDisabled = assetsInternal.keyPoolSwapDisabled().getBoolean().valueOrElse(false) @@ -891,12 +929,14 @@ func isPoolSwapDisabledREADONLY(poolAddress: String) = { @Callable(i) func getInFeeREADONLY(poolAddress: String) = { + strict checkForceStop = isForceStopped() let fee = intOrDefaultOrFallback(keyInFee(poolAddress), keyInFeeDefault(), 0) ([], fee) } @Callable(i) func getOutFeeREADONLY(poolAddress: String) = { + strict checkForceStop = isForceStopped() let fee = intOrDefaultOrFallback(keyOutFee(poolAddress), keyOutFeeDefault(), fraction(10, MULT8, 10_000)) ([], fee) } @@ -904,6 +944,7 @@ func getOutFeeREADONLY(poolAddress: String) = { # %d%d__poolFee__protocolFee @Callable(i) func getSwapFeeREADONLY(poolAddressStr: String) = { + strict checkForceStop = isForceStopped() let feeOption = match this.getString(keySwapFee(poolAddressStr)) { case s: String => { let parts = s.split(SEP) @@ -919,6 +960,7 @@ func getSwapFeeREADONLY(poolAddressStr: String) = { @Callable(i) func poolInfoREADONLY(amountAssetIdStr: String, priceAssetIdStr: String) = { + strict checkForceStop = isForceStopped() let amountAssetInternalIdOption = this.getInteger(amountAssetIdStr.keyMappingsBaseAsset2internalId()) let priceAssetInternalIdOption = this.getInteger(priceAssetIdStr.keyMappingsBaseAsset2internalId()) let poolContractAddressOption = this.getString(keyMappingPoolAssetsToPoolContractAddress(amountAssetInternalIdOption.value(), priceAssetInternalIdOption.value())) @@ -937,6 +979,7 @@ func poolInfoREADONLY(amountAssetIdStr: String, priceAssetIdStr: String) = { @Callable(i) func getPoolStatusREADONLY(poolAddress: String) = { + strict checkForceStop = isForceStopped() let poolAssets = keyMappingPoolContractAddressToPoolAssets(poolAddress).getStringOrFail().split(SEP) let amountAssetInternal = poolAssets[1] let priceAssetInternal = poolAssets[2] @@ -946,6 +989,7 @@ func getPoolStatusREADONLY(poolAddress: String) = { @Callable(i) func getPoolConfigREADONLY(poolAddress: String) = { + strict checkForceStop = isForceStopped() let poolAssets = keyMappingPoolContractAddressToPoolAssets(poolAddress).getStringOrFail().split(SEP) let amountAssetInternal = poolAssets[1] let priceAssetInternal = poolAssets[2] @@ -955,6 +999,7 @@ func getPoolConfigREADONLY(poolAddress: String) = { @Callable(i) func getPoolConfigByLpAssetIdREADONLY(lpAssetIdStr: String) = { + strict checkForceStop = isForceStopped() let poolAssets = keyMappingLpAssetToPoolAssets(lpAssetIdStr).getStringOrFail().split(SEP) let amountAssetInternal = poolAssets[1] let priceAssetInternal = poolAssets[2] @@ -964,16 +1009,19 @@ func getPoolConfigByLpAssetIdREADONLY(lpAssetIdStr: String) = { @Callable(i) func poolExistsREADONLY(amountAssetId: String, priceAssetId: String) = { + strict checkForceStop = isForceStopped() ([], poolExistsInternal(amountAssetId, priceAssetId)) } @Callable(i) func skipOrderValidationREADONLY(poolAddress: String) = { + strict checkForceStop = isForceStopped() ([], keySkipOrderValidation(poolAddress).getBoolean().valueOrElse(false)) } @Callable(i) func getChangeAmpConfigREADONLY(poolAddress: String) = { + strict checkForceStop = isForceStopped() let delay = keyChangeAmpDelay(poolAddress).getIntegerValue() let delta = keyChangeAmpDelta(poolAddress).getIntegerValue() let target = keyChangeAmpTarget(poolAddress).getIntegerValue() @@ -982,6 +1030,7 @@ func getChangeAmpConfigREADONLY(poolAddress: String) = { @Callable(i) func getPoolWeightREADONLY(lpAssetId: String) = { + strict checkForceStop = isForceStopped() let poolAddress = keyMappingPoolLPAssetToPoolContractAddress(lpAssetId).getStringOrFail() let weight = keyPoolToWeight(poolAddress).getInteger().valueOrElse(0) @@ -990,6 +1039,7 @@ func getPoolWeightREADONLY(lpAssetId: String) = { @Callable(i) func deletePool(poolContractAddress: String) = { + strict checkForceStop = isForceStopped() let poolConfig = getPoolConfig(poolContractAddress) strict checkCaller = if (i.caller.toString() == getPoolCreator(poolContractAddress)) then { @@ -1039,6 +1089,7 @@ func deletePool(poolContractAddress: String) = { @Callable(i) func getPrice(poolAddressStr: String) = { + strict checkForceStop = isForceStopped() let poolAddress = poolAddressStr.addressFromStringValue() let cfg = getPoolConfig(poolAddressStr) let lpAssetId = cfg[idxPoolLPAssetId].fromBase58String() @@ -1068,6 +1119,7 @@ func getPrice(poolAddressStr: String) = { # for voting_emission.ride @Callable(i) func checkBalance(lpAssetIdStr: String) = { + strict checkForceStop = isForceStopped() let poolAddressStr = keyMappingPoolLPAssetToPoolContractAddress(lpAssetIdStr).getStringOrFail() let poolAddress = addressFromStringValue(poolAddressStr) let cfg = getPoolConfig(poolAddressStr) diff --git a/ride/forcestop.ride b/ride/forcestop.ride new file mode 100644 index 000000000..2601b3e04 --- /dev/null +++ b/ride/forcestop.ride @@ -0,0 +1,111 @@ +{-# STDLIB_VERSION 6 #-} +{-# CONTENT_TYPE DAPP #-} +{-# SCRIPT_TYPE ACCOUNT #-} + +let contractFilename = "forcestop.ride" +let SEP = "__" + +func wrapErr(msg: String) = [contractFilename, ": ", msg].makeString("") +func throwErr(msg: String) = msg.wrapErr().throw() + +func keyManagerVaultAddress() = ["%s", "managerVaultAddress"].makeString(SEP) +func keyManagerPublicKey() = ["%s", "managerPublicKey"].makeString(SEP) + +func keyPermissionList() = ["%s", "forceStopPermission"].makeString(SEP) +func keyForceStop(address: String) = ["%s%s", "disabled", address].makeString(SEP) + +# Format: +# "{addr1}_{addr2}_{addr3}" -> ["addr1", "addr2", "addr3"] +let permissionList = this.getString(keyPermissionList()).valueOrElse("").split(SEP) + +let permissionDeniedError = "Permission denied".throwErr() + + +func getManagerVaultAddressOrThis() = { + match keyManagerVaultAddress().getString() { + case s:String => s.addressFromStringValue() + case _=> this + } +} + +func managerPublicKeyOrUnit() = { + let managerVaultAddress = getManagerVaultAddressOrThis() + match managerVaultAddress.getString(keyManagerPublicKey()) { + case s: String => s.fromBase58String() + case _: Unit => unit + } +} + +func mustThis(i: Invocation) = { + i.caller == this || permissionDeniedError +} + +func mustManager(i: Invocation) = { + match managerPublicKeyOrUnit() { + case pk: ByteVector => i.callerPublicKey == pk || permissionDeniedError + case _: Unit => i.mustThis() + } +} + +func isValidAddress(address: String) = { + match (address.addressFromString()) { + case a:Address => true + case _ => false + } +} + +@Callable(i) +func forceStopContract(address: String, stop: Boolean) = { + let callerAddressString = i.caller.toString() + + strict check = [ + permissionList.containsElement(callerAddressString) || i.mustManager(), + address.isValidAddress() || ["[", address, "] not an address"].makeString("").throwErr() + ] + + [ + BooleanEntry(keyForceStop(address), stop) + ] +} + +@Callable(i) +func addPermission(address: String) = { + strict check = [ + i.mustManager(), + address.isValidAddress() || ["[", address, "] not an address"].makeString("").throwErr(), + !permissionList.containsElement(address) || "already in permission list".throwErr() + ] + + let newList = permissionList :+ address + let newListString = newList.makeString(SEP) + + [ + StringEntry(keyPermissionList(), newListString) + ] +} + +@Callable(i) +func removePermission(address: String) = { + strict check = [ + i.mustManager(), + address.isValidAddress() || ["[", address, "] not an address"].makeString("").throwErr(), + permissionList.containsElement(address) || "not in permission list".throwErr() + ] + + let r_index = permissionList.indexOf(address).valueOrErrorMessage(address + "not in list".wrapErr()) + let newList = permissionList.removeByIndex(r_index) + let newListString = newList.makeString(SEP) + + [ + StringEntry(keyPermissionList(), newListString) + ] +} + +@Verifier(tx) +func verify() = { + let targetPublicKey = match managerPublicKeyOrUnit() { + case pk: ByteVector => pk + case _: Unit => tx.senderPublicKey + } + sigVerify(tx.bodyBytes, tx.proofs[0], targetPublicKey) +} diff --git a/ride/gwx_reward.ride b/ride/gwx_reward.ride index 179dd9da3..7d0de43e8 100644 --- a/ride/gwx_reward.ride +++ b/ride/gwx_reward.ride @@ -316,6 +316,21 @@ func getTradingReward(userAddress: String) = { this.getInteger(userAddress.keyTradingReward()).valueOrElse(0) } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func tradeRewardInternal( paymentAmountLeftOver: Int, @@ -323,6 +338,7 @@ func tradeRewardInternal( rewards: List[Int], currentIter: Int ) = { + strict checkForceStop = isForceStopped() if (currentIter == userAddresses.size()) then ([]) else strict checks = [ @@ -354,8 +370,10 @@ func tradeRewardInternal( ], tradeRewardInternal) } + @Callable(i) func updateReferralActivity(userAddress: String, gWxAmountStart: Int) = { + strict checkForceStop = isForceStopped() let referrer = referralsContractAddressOrFail.getString(userAddress.keyReferrer()) strict activeReferralInv = if (referrer == unit) then unit else { referralsContractAddressOrFail.invoke("updateReferralActivity", [referralProgramName, userAddress, gWxAmountStart >= referralMinGWxAmount], []) @@ -366,6 +384,7 @@ func updateReferralActivity(userAddress: String, gWxAmountStart: Int) = { @Callable(i) func finalizeHelper() = { + strict checkForceStop = isForceStopped() let processingStage = keyProcessingStage().getInteger().valueOrElse(processingStageTotal) let currentPeriod = keyNextProcessedPeriod().getNumberByKey() # period with reward being currently distributed let currentUser = keyNextProcessedUser().getNumberByKey() # user to start with @@ -433,6 +452,7 @@ func finalizeHelper() = { @Callable(i) func finalizeWrapper(counter: Int) = { + strict checkForceStop = isForceStopped() strict result = this.invoke("finalizeHelper", [], []).exactAs[Boolean] if (!result) then { if (counter == maxDepth) then "Nothing to process".throw() else ([], unit) @@ -447,6 +467,7 @@ func finalizeWrapper(counter: Int) = { @Callable(i) func processPendingPeriodsAndUsers() = { + strict checkForceStop = isForceStopped() ([], this.invoke("finalizeWrapper", [maxDepth], [])) } @@ -456,6 +477,7 @@ func processPendingPeriodsAndUsers() = { # Called by matcher pacemaker @Callable(i) func deposit() = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller == votingEmissionContract || i.mustManager() let period = nextPeriod() let deltaH = height - keyGwxRewardEmissionStartHeight().getNumberOrFail() @@ -490,6 +512,7 @@ func deposit() = { # called by user @Callable(i) func claimReward() = { + strict checkForceStop = isForceStopped() let cfgArray = readConfigArrayOrFail() let address = i.caller.toString() let (amount, actions) = commonClaimReward(address) @@ -507,6 +530,7 @@ func claimReward() = { # returns total claimable reward by user address @Callable(i) func claimRewardREADONLY(address: String) = { + strict checkForceStop = isForceStopped() let (amount, actions) = commonClaimReward(address) let referralUnclaimed = referralsContractAddressOrFail.getInteger(keyUnclaimedReferral(referralProgramName, address)).valueOrElse(0) let totalAmount = amount + referralUnclaimed @@ -517,12 +541,14 @@ func claimRewardREADONLY(address: String) = { # returns -1 if there were no payments via deposit() call @Callable(i) func latestFinalizedPeriodREADONLY(address: String) = { + strict checkForceStop = isForceStopped() ([], getInteger(this, keyLatestPeriod()).valueOrElse(-1)) } # returns %d%d%d__${latestFinalizedPeriod}__${matcherPart}__${emissionPart} @Callable(i) func latestFinalizedPeriodInfoREADONLY(address: String) = { + strict checkForceStop = isForceStopped() ([], getStringByKey(keyLastPayoutInfo())) } @@ -531,6 +557,7 @@ func latestFinalizedPeriodInfoREADONLY(address: String) = { # *********************** @Callable(i) func calcGwxParamsREADONLY(gwxAmountStart: Int, lockStartHeight: Int, lockDurationBlocks: Int) = { + strict checkForceStop = isForceStopped() let lockEndHeight = lockStartHeight + lockDurationBlocks let scale8ParamK = -fraction(gwxAmountStart, SCALE, lockDurationBlocks) let scale8ParamB = fraction(gwxAmountStart, SCALE, lockDurationBlocks) * lockEndHeight @@ -539,6 +566,7 @@ func calcGwxParamsREADONLY(gwxAmountStart: Int, lockStartHeight: Int, lockDurati @Callable(i) func calcGwxAmountStartREADONLY(wxLockAmount: Int, lockDuration: Int, maxLockDuration: Int) = { + strict checkForceStop = isForceStopped() let coeffX8 = fraction(lockDuration, MULT8, maxLockDuration) let gWxAmountStart = fraction(wxLockAmount, coeffX8, MULT8) ([], [gWxAmountStart]) @@ -547,12 +575,14 @@ func calcGwxAmountStartREADONLY(wxLockAmount: Int, lockDuration: Int, maxLockDur # save starting height of reward from emission 5% @Callable(i) func onEmissionForGwxStart() = { + strict checkForceStop = isForceStopped() if (i.caller != factoryContract) then throw("permissions denied") else [IntegerEntry(keyGwxRewardEmissionStartHeight(), height)] } @Callable(i) func latestPeriodEmissionRewardsREADONLY(address: String) = { + strict checkForceStop = isForceStopped() let period = nextPeriod() ([], [getNumberByKey(keyAuxEmissionRewardForPeriod(period))]) } @@ -613,6 +643,7 @@ func calcD( @Callable(i) func tradeReward(userAddresses: List[String], rewards: List[Int]) = { + strict checkForceStop = isForceStopped() let argsComparison = userAddresses.size() == rewards.size() let maxRecipients = keyMaxRecipients().getInteger().valueOrElse(0) let payment = i.payments[0] @@ -641,6 +672,7 @@ func tradeReward(userAddresses: List[String], rewards: List[Int]) = { @Callable(i) func claimTradingReward() = { + strict checkForceStop = isForceStopped() let userAddress = i.caller let userAddressString = userAddress.toString() let reward = userAddressString.getTradingReward() @@ -654,6 +686,7 @@ func claimTradingReward() = { @Callable(i) func claimTradingRewardREADONLY(userAddress: String) = { + strict checkForceStop = isForceStopped() (nil, userAddress.getTradingReward()) } diff --git a/ride/l2mp_leasing.ride b/ride/l2mp_leasing.ride index 6f2aabc8b..7ce9c6218 100644 --- a/ride/l2mp_leasing.ride +++ b/ride/l2mp_leasing.ride @@ -394,8 +394,25 @@ func getSetNewPeriodLengthActions(newPeriodLength: Int) = { } } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + + @Callable(i) func setNewPeriodLength(newPeriodLength: Int) = { + strict checkForceStop = isForceStopped() strict checks = [ isInAdminList(i.caller.toString()) || "caller is not in adminList".throwErr() ] @@ -405,6 +422,7 @@ func setNewPeriodLength(newPeriodLength: Int) = { @Callable(i) func setForceStopFlag(stop: Boolean) = { + strict checkForceStop = isForceStopped() strict check = [ isInAdminList(i.caller.toString()) || "caller is not in adminList".throwErr() ] @@ -422,6 +440,7 @@ func setForceStopFlag(stop: Boolean) = { # _5 = current height @Callable(i) func getNodeDataREADONLY(nodeAddress: String) = { + strict checkForceStop = isForceStopped() let (currentLease, nextLeased) = getLeasingNodeData(nodeAddress) ([], (currentPeriodHeight, currentLease, nextPeriodHeight, nextLeased, height)) @@ -435,6 +454,7 @@ func getNodeDataREADONLY(nodeAddress: String) = { # _5 = current height @Callable(i) func getUserLeasingDataREADONLY(nodeAddress: String, userAddress: String) = { + strict checkForceStop = isForceStopped() let (currentLease, nextLeased) = getUserLeasingData(nodeAddress, userAddress) ([], (currentPeriodHeight, currentLease, nextPeriodHeight, nextLeased, height)) @@ -449,6 +469,7 @@ func getUserLeasingDataREADONLY(nodeAddress: String, userAddress: String) = { # _6 = current height @Callable(i) func getUserDataREADONLY(userAddress: String) = { + strict checkForceStop = isForceStopped() let (toClaim, toUnlock) = getUserToClaimBalance(userAddress) let userTotalLocked = getUserTotalLocked(userAddress) @@ -457,11 +478,13 @@ func getUserDataREADONLY(userAddress: String) = { @Callable(i) func leaseByAddress(nodeAddress: String, userAddress: String) = { + strict checkForceStop = isForceStopped() getStakeFromPaymentActions(nodeAddress, userAddress, i) } @Callable(i) func lease(nodeAddress: String) = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() getStakeFromPaymentActions(nodeAddress, userAddress, i) @@ -469,6 +492,7 @@ func lease(nodeAddress: String) = { @Callable(i) func leaseFromLocked(nodeAddress: String, amount: Int) = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() getStakeActions(nodeAddress, userAddress, amount, i) @@ -476,6 +500,7 @@ func leaseFromLocked(nodeAddress: String, amount: Int) = { @Callable(i) func cancelLease(nodeAddress: String, amount: Int) = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() getUnstakeActions(nodeAddress, userAddress, amount) @@ -483,6 +508,7 @@ func cancelLease(nodeAddress: String, amount: Int) = { @Callable(i) func claim(amount: Int) = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() getClaimUnlockedActions(userAddress, amount) @@ -490,6 +516,7 @@ func claim(amount: Int) = { @Callable(i) func claimAll() = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() let (toClaim, toUnlock) = getUserToClaimBalance(userAddress) @@ -506,6 +533,7 @@ func claimAll() = { # Vote for txId that is allowed in Verifier @Callable(i) func voteForTxId(txId: String) = { + strict checkForceStop = isForceStopped() let callerAddressString = toBase58String(i.caller.bytes) let keyPrefix = keyAllowedTxIdVotePrefix(txId) let result = [ StringEntry(keyAllowedTxId(), txId) ] diff --git a/ride/l2mp_staking.ride b/ride/l2mp_staking.ride index 795ef2648..89878fb08 100644 --- a/ride/l2mp_staking.ride +++ b/ride/l2mp_staking.ride @@ -315,8 +315,24 @@ func getSetStakingNodeActions(userAddress: String, nodeAddress: String, nodeShar ] } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func setEmissionPerBlock(emissionPerBlock: Int) = { + strict checkForceStop = isForceStopped() strict check = [ i.caller == this || "permission denied".throwErr() ] @@ -330,6 +346,7 @@ func setEmissionPerBlock(emissionPerBlock: Int) = { @Callable(i) func setEmissionPeriodInBlocks(p: Int) = { + strict checkForceStop = isForceStopped() strict check = [ p > 0 || "emission period should be greater than 0".throwErr(), i.caller == this || "permission denied".throwErr() @@ -344,6 +361,7 @@ func setEmissionPeriodInBlocks(p: Int) = { @Callable(i) func stake() = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() getStakeActions(i, userAddress) @@ -351,11 +369,13 @@ func stake() = { @Callable(i) func stakeFor(userAddress: String) = { + strict checkForceStop = isForceStopped() getStakeActions(i, userAddress) } @Callable(i) func withdraw(withdrawAssetAmount: Int) = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() let userLpAmount = getUserLpAmount(userAddress) let lpAmountToWithdraw = calcLpFromAsset(withdrawAssetAmount) @@ -373,6 +393,7 @@ func withdraw(withdrawAssetAmount: Int) = { @Callable(i) func setStakingNode(nodeAddress: String) = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() getSetStakingNodeActions(userAddress, nodeAddress, 100) @@ -380,6 +401,7 @@ func setStakingNode(nodeAddress: String) = { @Callable(i) func stakeAndSetStakingNode(nodeAddress: String) = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() getStakeActions(i, userAddress) ++ getSetStakingNodeActions(userAddress, nodeAddress, 100) @@ -388,6 +410,7 @@ func stakeAndSetStakingNode(nodeAddress: String) = { # Used in l2mp_swap.ride @Callable(i) func stakeForSwapHELPER(userAddress: String, nodeAddress: String) = { + strict checkForceStop = isForceStopped() strict check = [ i.originCaller.toString() == userAddress || "i.originCaller should be equal to userAddress".throwErr() ] @@ -399,6 +422,7 @@ func stakeForSwapHELPER(userAddress: String, nodeAddress: String) = { @Callable(i) func airdrop(addressList: List[String], amountList: List[Int]) = { + strict checkForceStop = isForceStopped() func sum(accum: Int, next: Int) = { if (next < 0) then "negative amount value in amountList".throwErr() else accum + next } @@ -460,6 +484,7 @@ func airdrop(addressList: List[String], amountList: List[Int]) = { # _10 = interest remaining blocks @Callable(i) func getUserAssetsREADONLY(userAddress: String) = { + strict checkForceStop = isForceStopped() let userLpAmount = getUserLpAmount(userAddress) let userLockedLpAmount = getUserLockedLpAmount(userAddress) let userLockedAssetAmount = calcAssetFromLp(userLockedLpAmount) @@ -494,6 +519,7 @@ func getUserAssetsREADONLY(userAddress: String) = { # _6 = interest remaining blocks @Callable(i) func getTotalAssetsREADONLY() = { + strict checkForceStop = isForceStopped() ( nil, ( @@ -513,6 +539,7 @@ func getTotalAssetsREADONLY() = { # Vote for txId that is allowed in Verifier @Callable(i) func voteForTxId(txId: String) = { + strict checkForceStop = isForceStopped() let callerAddressString = toBase58String(i.caller.bytes) let keyPrefix = keyAllowedTxIdVotePrefix(txId) let result = [ StringEntry(keyAllowedTxId(), txId) ] diff --git a/ride/l2mp_swap.ride b/ride/l2mp_swap.ride index 0e25957f6..7b9c5fbe0 100644 --- a/ride/l2mp_swap.ride +++ b/ride/l2mp_swap.ride @@ -207,13 +207,30 @@ func getSwapActions(i: Invocation, stakingNode: String) = { ) } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func swap() = { + strict checkForceStop = isForceStopped() getSwapActions(i, "NULL") } @Callable(i) func swapAndStake(stakingNode: String) = { + strict checkForceStop = isForceStopped() strict check = [ stakingNode.isValidAddress() || stakingNode == "" || "staking node address is no valid".throwErr() ] @@ -225,6 +242,7 @@ func swapAndStake(stakingNode: String) = { @Callable(i) func claim() = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller == allowedAddress || throwErr("permission denied") ([ @@ -239,6 +257,7 @@ func claim() = { # Vote for txId that is allowed in Verifier @Callable(i) func voteForTxId(txId: String) = { + strict checkForceStop = isForceStopped() let callerAddressString = toBase58String(i.caller.bytes) let keyPrefix = keyAllowedTxIdVotePrefix(txId) let result = [ StringEntry(keyAllowedTxId(), txId) ] diff --git a/ride/lp.ride b/ride/lp.ride index cbd5a8cc0..f464492b8 100644 --- a/ride/lp.ride +++ b/ride/lp.ride @@ -970,14 +970,36 @@ func mustManager(i: Invocation) = { } } +### Pool Force Stop check function +func isForceStopped() = { + func keyDisabledStatus(address: String) = ["%s%s", "disabled", address].makeString(SEP) + + let factoryAddressString = this.getString(fc()).valueOrElse("") + let factoryAddress = factoryAddressString.addressFromString().valueOrElse(this) + + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = factoryAddress.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let poolDisabledStatus = forceStopAddress.getBoolean(keyDisabledStatus(this.toString())).valueOrElse(false) + let factoryDisabledStatus = forceStopAddress.getBoolean(keyDisabledStatus(factoryAddress.toString())).valueOrElse(false) + + if (poolDisabledStatus || factoryDisabledStatus) + then "Force stopped".throw() + else false +} +### End Pool Force Stop function + # Rebalance AmountAsset and PriceAsset @Callable(i) func rebalance() = { + strict checkForceStop = isForceStopped() rebalanceAsset(getStringOrFail(this, aa())) ++ rebalanceAsset(getStringOrFail(this, pa())) } @Callable(i) func calculateAmountOutForSwapREADONLY(cleanAmountIn: Int, isReverse: Boolean, feePoolAmount: Int) = { + strict checkForceStop = isForceStopped() let (assetOut, assetIn) = if (isReverse == false) then { let assetOut = getStringOrFail(this, pa()) let assetIn = getStringOrFail(this, aa()) @@ -1000,6 +1022,7 @@ func calculateAmountOutForSwapREADONLY(cleanAmountIn: Int, isReverse: Boolean, f @Callable(i) func calculateAmountOutForSwapAndSendTokens(cleanAmountIn: Int, isReverse: Boolean, amountOutMin: Int, addressTo: String, feePoolAmount: Int) = { + strict checkForceStop = isForceStopped() let swapContact = invoke( factoryContract, "getSwapContractREADONLY", @@ -1079,6 +1102,7 @@ func calculateAmountOutForSwapAndSendTokens(cleanAmountIn: Int, isReverse: Boole # transfer LP tokens based on deposit share @Callable(i) func put(slippageTolerance: Int, shouldAutoStake: Boolean) = { + strict checkForceStop = isForceStopped() if(slippageTolerance < 0) then throw("Invalid slippageTolerance passed") else let estPut = commonPut(i, slippageTolerance, true) @@ -1140,6 +1164,7 @@ func put(slippageTolerance: Int, shouldAutoStake: Boolean) = { # Put without LP emission @Callable(i) func putForFree(maxSlippage: Int) = { + strict checkForceStop = isForceStopped() if(maxSlippage < 0) then throw("Invalid value passed") else let estPut = commonPut(i, maxSlippage, false) let state = estPut._9 @@ -1158,6 +1183,7 @@ func putForFree(maxSlippage: Int) = { @Callable(i) func putOneTkn(minOutAmount: Int, autoStake: Boolean) = { + strict checkForceStop = isForceStopped() let isPoolOneTokenOperationsDisabled = factoryContract.invoke( "isPoolOneTokenOperationsDisabledREADONLY", [this.toString()], @@ -1226,6 +1252,7 @@ func putOneTkn(minOutAmount: Int, autoStake: Boolean) = { @Callable(i) func putOneTknREADONLY(paymentAssetId: String, paymentAmountRaw: Int) = { + strict checkForceStop = isForceStopped() let (emitAmountEstimated, commonState, feeAmount, bonus, paymentInAmountAsset) = calcPutOneToken(paymentAmountRaw, paymentAssetId.parseAssetId(), unit, unit) ([], (emitAmountEstimated, feeAmount, bonus)) @@ -1233,6 +1260,7 @@ func putOneTknREADONLY(paymentAssetId: String, paymentAmountRaw: Int) = { @Callable(i) func getOneTkn(outAssetIdStr: String, minOutAmount: Int) = { + strict checkForceStop = isForceStopped() let isPoolOneTokenOperationsDisabled = factoryContract.invoke( "isPoolOneTokenOperationsDisabledREADONLY", [this.toString()], @@ -1290,6 +1318,7 @@ func getOneTkn(outAssetIdStr: String, minOutAmount: Int) = { @Callable(i) func getOneTknREADONLY(outAssetId: String, paymentAmount: Int) = { + strict checkForceStop = isForceStopped() let (amountEstimated, commonState, feeAmount, bonus, outInAmountAsset) = calcGetOneToken(outAssetId.parseAssetId(), paymentAmount, cfgLpAssetId, unit, unit) ([], (amountEstimated, feeAmount, bonus)) @@ -1297,6 +1326,7 @@ func getOneTknREADONLY(outAssetId: String, paymentAmount: Int) = { @Callable(i) func unstakeAndGetOneTkn(unstakeAmount: Int, outAssetIdStr: String, minOutAmount: Int) = { + strict checkForceStop = isForceStopped() let isPoolOneTokenOperationsDisabled = factoryContract.invoke( "isPoolOneTokenOperationsDisabledREADONLY", [this.toString()], @@ -1361,6 +1391,7 @@ func unstakeAndGetOneTkn(unstakeAmount: Int, outAssetIdStr: String, minOutAmount # transfer to user his share of pool tokens base on passed lp token amount @Callable(i) func get() = { + strict checkForceStop = isForceStopped() let res = commonGet(i) let outAmAmt = res._1 let outPrAmt = res._2 @@ -1387,6 +1418,7 @@ func get() = { @Callable(i) func getNoLess(noLessThenAmtAsset: Int, noLessThenPriceAsset: Int) = { + strict checkForceStop = isForceStopped() let res = commonGet(i) let outAmAmt = res._1 let outPrAmt = res._2 @@ -1416,6 +1448,7 @@ func getNoLess(noLessThenAmtAsset: Int, noLessThenPriceAsset: Int) = { # Unstake LP tokens and exit from pool @Callable(i) func unstakeAndGet(amount: Int) = { + strict checkForceStop = isForceStopped() strict checkPayments = if (i.payments.size() != 0) then throw("No payments are expected") else true let cfg = getPoolConfig() @@ -1452,6 +1485,7 @@ func unstakeAndGet(amount: Int) = { @Callable(i) func unstakeAndGetNoLess(unstakeAmount: Int, noLessThenAmountAsset: Int, noLessThenPriceAsset: Int) = { + strict checkForceStop = isForceStopped() let isGetDisabled = !isAddressWhitelisted(i.caller) && (isGlobalShutdown() || cfgPoolStatus == PoolShutdown) strict checks = [ @@ -1496,6 +1530,7 @@ func unstakeAndGetNoLess(unstakeAmount: Int, noLessThenAmountAsset: Int, noLessT # return: @Callable(i) func activate(amtAssetStr: String, priceAssetStr: String) = { + strict checkForceStop = isForceStopped() if (i.caller.toString() != factoryContract.toString()) then throw("permissions denied") else { ([ StringEntry(aa(),amtAssetStr), @@ -1507,6 +1542,7 @@ func activate(amtAssetStr: String, priceAssetStr: String) = { @Callable(i) func refreshKLp() = { + strict checkForceStop = isForceStopped() let lastRefreshedBlockHeight = keyKLpRefreshedHeight.getInteger().valueOrElse(0) strict checkLastRefreshedBlockHeight = if (height - lastRefreshedBlockHeight >= kLpRefreshDelay) then unit else { [ @@ -1529,6 +1565,7 @@ func refreshKLp() = { # API wrappers @Callable(i) func getPoolConfigWrapperREADONLY() = { + strict checkForceStop = isForceStopped() ( [], getPoolConfig() @@ -1537,6 +1574,7 @@ func getPoolConfigWrapperREADONLY() = { @Callable(i) func getAccBalanceWrapperREADONLY(assetId: String) = { + strict checkForceStop = isForceStopped() ( [], assetId.getAccBalance() @@ -1545,6 +1583,7 @@ func getAccBalanceWrapperREADONLY(assetId: String) = { @Callable(i) func calcPricesWrapperREADONLY(amAmt: Int, prAmt: Int, lpAmt: Int) = { + strict checkForceStop = isForceStopped() let prices = calcPrices(amAmt, prAmt, lpAmt) ( [], @@ -1559,6 +1598,7 @@ func calcPricesWrapperREADONLY(amAmt: Int, prAmt: Int, lpAmt: Int) = { @Callable(i) func toX18WrapperREADONLY(origVal: Int, origScaleMult: Int) = { + strict checkForceStop = isForceStopped() ( [], toX18(origVal, origScaleMult).toString() @@ -1567,6 +1607,7 @@ func toX18WrapperREADONLY(origVal: Int, origScaleMult: Int) = { @Callable(i) func fromX18WrapperREADONLY(val: String, resultScaleMult: Int) = { + strict checkForceStop = isForceStopped() ( [], fromX18(val.parseBigIntValue(), resultScaleMult) @@ -1575,6 +1616,7 @@ func fromX18WrapperREADONLY(val: String, resultScaleMult: Int) = { @Callable(i) func calcPriceBigIntWrapperREADONLY(prAmtX18: String, amAmtX18: String) = { + strict checkForceStop = isForceStopped() ( [], calcPriceBigInt(prAmtX18.parseBigIntValue(), amAmtX18.parseBigIntValue()).toString() @@ -1611,6 +1653,7 @@ func estimatePutOperationWrapperREADONLY( @Callable(i) func estimateGetOperationWrapperREADONLY(txId58: String, pmtAssetId: String, pmtLpAmt: Int, userAddress: String) = { + strict checkForceStop = isForceStopped() let res = estimateGetOperation( txId58, pmtAssetId, @@ -1626,6 +1669,7 @@ func estimateGetOperationWrapperREADONLY(txId58: String, pmtAssetId: String, pmt # API (moved to rest contract) @Callable(i) func statsREADONLY() = { + strict checkForceStop = isForceStopped() # data from pool config let cfg = getPoolConfig() let lpAssetId = cfg[idxPoolLPAssetId].fromBase58String() @@ -1662,6 +1706,7 @@ func statsREADONLY() = { @Callable(i) func evaluatePutByAmountAssetREADONLY(inAmAssetAmt: Int) = { + strict checkForceStop = isForceStopped() let cfg = getPoolConfig() let lpAssetId = cfg[idxPoolLPAssetId].fromBase58String() let amAssetIdStr = cfg[idxAmtAssetId] @@ -1710,6 +1755,7 @@ func evaluatePutByAmountAssetREADONLY(inAmAssetAmt: Int) = { @Callable(i) func evaluatePutByPriceAssetREADONLY(inPrAssetAmt: Int) = { + strict checkForceStop = isForceStopped() let cfg = getPoolConfig() let lpAssetId = cfg[idxPoolLPAssetId].fromBase58String() let amAssetIdStr = cfg[idxAmtAssetId] @@ -1757,6 +1803,7 @@ func evaluatePutByPriceAssetREADONLY(inPrAssetAmt: Int) = { @Callable(i) func evaluateGetREADONLY(paymentLpAssetId: String, paymentLpAmt: Int) = { + strict checkForceStop = isForceStopped() let res = estimateGetOperation("", paymentLpAssetId, paymentLpAmt, this) let outAmAmt = res._1 diff --git a/ride/lp_stable.ride b/ride/lp_stable.ride index 88b37557e..a41fea4e1 100644 --- a/ride/lp_stable.ride +++ b/ride/lp_stable.ride @@ -989,8 +989,29 @@ func skipOrderValidation() = { fca.getBoolean(keySkipOrderValidation(this.toString())).valueOrElse(false) } +### Pool Force Stop check function +func isForceStopped() = { + func keyDisabledStatus(address: String) = ["%s%s", "disabled", address].makeString(SEP) + + let factoryAddressString = this.getString(fc()).valueOrElse("") + let factoryAddress = factoryAddressString.addressFromString().valueOrElse(this) + + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = factoryAddress.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let poolDisabledStatus = forceStopAddress.getBoolean(keyDisabledStatus(this.toString())).valueOrElse(false) + let factoryDisabledStatus = forceStopAddress.getBoolean(keyDisabledStatus(factoryAddress.toString())).valueOrElse(false) + + if (poolDisabledStatus || factoryDisabledStatus) + then "Force stopped".throw() + else false +} +### End Pool Force Stop function + @Callable(i) func calculateAmountOutForSwapREADONLY(cleanAmountIn: Int, isReverse: Boolean, feePoolAmount: Int) = { + strict checkForceStop = isForceStopped() let (assetOut, poolAmountInBalance) = if (isReverse == false) then { let assetOut = this.strf(pa()) let poolAmountInBalance = getAccBalance(this.strf(aa())).toBigInt() + cleanAmountIn.toBigInt() @@ -1026,6 +1047,7 @@ func calculateAmountOutForSwapREADONLY(cleanAmountIn: Int, isReverse: Boolean, f @Callable(i) func calculateAmountOutForSwapAndSendTokens(cleanAmountIn: Int, isReverse: Boolean, amountOutMin: Int, addressTo: String, feePoolAmount: Int) = { + strict checkForceStop = isForceStopped() let swapContact = invoke( fca, "getSwapContractREADONLY", @@ -1129,6 +1151,7 @@ func constructor(fc: String) = { # transfer LP tokens based on deposit share @Callable(i) func put(slip: Int, autoStake: Boolean) = { + strict checkForceStop = isForceStopped() let factCfg = gfc() let stakingCntr = addressFromString(factCfg[idxFactStakCntr]).valueOrErrorMessage("Wr st addr") let slipCntr = addressFromString(factCfg[idxFactSlippCntr]).valueOrErrorMessage("Wr sl addr") @@ -1199,6 +1222,7 @@ func put(slip: Int, autoStake: Boolean) = { @Callable(i) func putOneTknV2(minOutAmount: Int, autoStake: Boolean) = { + strict checkForceStop = isForceStopped() let isPoolOneTokenOperationsDisabled = fca.invoke( "isPoolOneTokenOperationsDisabledREADONLY", [this.toString()], @@ -1285,6 +1309,7 @@ func putOneTknV2(minOutAmount: Int, autoStake: Boolean) = { # Put without LP emission @Callable(i) func putForFree(maxSlpg: Int) = { + strict checkForceStop = isForceStopped() if(maxSlpg < 0) then throw("Wrong slpg") else if (i.payments.size() != 2) then throw("2 pmnts expd") else let estPut = cp(i.caller.toString(), @@ -1325,6 +1350,7 @@ func putForFree(maxSlpg: Int) = { # transfer to user his share of pool tokens base on passed lp token amount @Callable(i) func get() = { + strict checkForceStop = isForceStopped() # amountAssetBalance # strict aab = cfgAmountAssetId.assetIdToString().getAccBalance().toBigInt() # # priceAssetBalance @@ -1355,6 +1381,7 @@ func get() = { @Callable(i) func getOneTknV2(outAssetId: String, minOutAmount: Int) = { + strict checkForceStop = isForceStopped() let isPoolOneTokenOperationsDisabled = fca.invoke( "isPoolOneTokenOperationsDisabledREADONLY", [this.toString()], @@ -1382,6 +1409,7 @@ func getOneTknV2(outAssetId: String, minOutAmount: Int) = { @Callable(i) func refreshDLp() = { + strict checkForceStop = isForceStopped() let lastRefreshedBlockHeight = keyDLpRefreshedHeight.getInteger().valueOrElse(0) strict checkLastRefreshedBlockHeight = if (height - lastRefreshedBlockHeight >= dLpRefreshDelay) then unit else { [ @@ -1465,6 +1493,7 @@ func getOneTknV2WithBonusREADONLY( @Callable(i) func getNoLess(noLessThenAmtAsset: Int, noLessThenPriceAsset: Int) = { + strict checkForceStop = isForceStopped() let r = cg(i) let outAmAmt = r._1 let outPrAmt = r._2 @@ -1491,6 +1520,7 @@ func getNoLess(noLessThenAmtAsset: Int, noLessThenPriceAsset: Int) = { # Unstake LP tokens and exit from pool @Callable(i) func unstakeAndGet(amount: Int) = { + strict checkForceStop = isForceStopped() strict checkPayments = if (i.payments.size() != 0) then throw("No pmnts expd") else true let factoryCfg = gfc() @@ -1523,6 +1553,7 @@ func unstakeAndGet(amount: Int) = { @Callable(i) func unstakeAndGetNoLess(unstakeAmount: Int, noLessThenAmountAsset: Int, noLessThenPriceAsset: Int) = { + strict checkForceStop = isForceStopped() let isGetDisabled = !isAddressWhitelisted(i.caller) && (igs() || cfgPoolStatus == PoolShutdown) strict checks = [ @@ -1555,6 +1586,7 @@ func unstakeAndGetNoLess(unstakeAmount: Int, noLessThenAmountAsset: Int, noLessT @Callable(i) func unstakeAndGetOneTknV2(unstakeAmount: Int, outAssetId: String, minOutAmount: Int) = { + strict checkForceStop = isForceStopped() let isPoolOneTokenOperationsDisabled = fca.invoke( "isPoolOneTokenOperationsDisabledREADONLY", [this.toString()], @@ -1594,6 +1626,7 @@ func unstakeAndGetOneTknV2(unstakeAmount: Int, outAssetId: String, minOutAmount: @Callable(i) func putOneTknV2WithBonusREADONLY(paymentAmountRaw: Int, paymentAssetId: String) = { + strict checkForceStop = isForceStopped() let (lpAmount, state, feeAmount, bonus) = calcPutOneTkn(paymentAmountRaw, paymentAssetId, "", "", true) (nil, (lpAmount, feeAmount, bonus)) @@ -1601,6 +1634,7 @@ func putOneTknV2WithBonusREADONLY(paymentAmountRaw: Int, paymentAssetId: String) @Callable(i) func putOneTknV2WithoutTakeFeeREADONLY(paymentAmountRaw: Int, paymentAssetId: String) = { + strict checkForceStop = isForceStopped() let (lpAmount, state, feeAmount, bonus) = calcPutOneTkn(paymentAmountRaw, paymentAssetId, "", "", false) (nil, (lpAmount, feeAmount, bonus)) @@ -1617,6 +1651,7 @@ func putOneTknV2WithoutTakeFeeREADONLY(paymentAmountRaw: Int, paymentAssetId: St # return: @Callable(i) func activate(amtAsStr: String, prAsStr: String) = { + strict checkForceStop = isForceStopped() if (i.caller.toString() != fca.toString()) then throw("denied") else { ([ StringEntry(aa(),amtAsStr), @@ -1631,6 +1666,7 @@ func activate(amtAsStr: String, prAsStr: String) = { # API wrappers @Callable(i) func getPoolConfigWrapperREADONLY() = { + strict checkForceStop = isForceStopped() ( [], gpc() @@ -1639,6 +1675,7 @@ func getPoolConfigWrapperREADONLY() = { @Callable(i) func getAccBalanceWrapperREADONLY(assetId: String) = { + strict checkForceStop = isForceStopped() ( [], assetId.getAccBalance() @@ -1647,6 +1684,7 @@ func getAccBalanceWrapperREADONLY(assetId: String) = { @Callable(i) func calcPricesWrapperREADONLY(amAmt: Int, prAmt: Int, lpAmt: Int) = { + strict checkForceStop = isForceStopped() let pr = calcPrices(amAmt, prAmt, lpAmt) ( [], @@ -1660,6 +1698,7 @@ func calcPricesWrapperREADONLY(amAmt: Int, prAmt: Int, lpAmt: Int) = { @Callable(i) func fromX18WrapperREADONLY(val: String, resScaleMult: Int) = { + strict checkForceStop = isForceStopped() ( [], f1(val.parseBigIntValue(), resScaleMult) @@ -1668,6 +1707,7 @@ func fromX18WrapperREADONLY(val: String, resScaleMult: Int) = { @Callable(i) func toX18WrapperREADONLY(origVal: Int, origScaleMult: Int) = { + strict checkForceStop = isForceStopped() ( [], t1(origVal, origScaleMult).toString() @@ -1676,6 +1716,7 @@ func toX18WrapperREADONLY(origVal: Int, origScaleMult: Int) = { @Callable(i) func calcPriceBigIntWrapperREADONLY(prAmtX18: String, amAmtX18: String) = { + strict checkForceStop = isForceStopped() ( [], cpbi(prAmtX18.parseBigIntValue(), amAmtX18.parseBigIntValue()).toString() @@ -1716,6 +1757,7 @@ func estimatePutOperationWrapperREADONLY( @Callable(i) func estimateGetOperationWrapperREADONLY(txId58: String, pmtAsId: String, pmtLpAmt: Int, usrAddr: String) = { + strict checkForceStop = isForceStopped() let r = ego( txId58, pmtAsId, @@ -1730,6 +1772,7 @@ func estimateGetOperationWrapperREADONLY(txId58: String, pmtAsId: String, pmtLpA @Callable(i) func changeAmp() = { + strict checkForceStop = isForceStopped() let cfg = fca.invoke("getChangeAmpConfigREADONLY", [this.toString()], []) let (delay, delta, target) = match cfg { case list: List[Any] => { diff --git a/ride/lp_staking.ride b/ride/lp_staking.ride index b5bb65b71..ca2057312 100644 --- a/ride/lp_staking.ride +++ b/ride/lp_staking.ride @@ -225,9 +225,25 @@ func privateCurrentSysParamsREST(baseAssetStr: String) = { ) } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func adminRegisterAsset(baseAssetStr: String, shareAssetName: String, shareAssetDescr: String, getDelayinBlocks: Int, shutdownManagerAddress: String) = { + strict checkForceStop = isForceStopped() let baseAssetId = baseAssetStr.fromBase58String() let decimals = assetInfo(baseAssetId).value().decimals # TODO validate that BaseToken has not been specified before @@ -267,6 +283,7 @@ func adminRegisterAsset(baseAssetStr: String, shareAssetName: String, shareAsset @Callable(i) func shutdownPut(internalBaseAssetId: Int) = { + strict checkForceStop = isForceStopped() let internalBaseAssetIdStr = internalBaseAssetId.toString() let baseAssetIdStr = getStringOrFail(keyMappingsInternal2baseAssetId(internalBaseAssetId)) let shutdownManagerAddress = getStringOrFail(keyShutdownManager(internalBaseAssetIdStr)) @@ -278,6 +295,7 @@ func shutdownPut(internalBaseAssetId: Int) = { @Callable(i) func put() = { + strict checkForceStop = isForceStopped() let pmt = i.payments[0].value() let baseAssetId = pmt.assetId.value() let baseAssetStr = baseAssetId.toBase58String() @@ -307,6 +325,7 @@ func put() = { @Callable(i) func submitGetRequest() = { + strict checkForceStop = isForceStopped() let pmt = i.payments[0].value() let shareAssetId = pmt.assetId.value() let shareAssetStr = shareAssetId.toBase58String() @@ -349,6 +368,7 @@ func submitGetRequest() = { @Callable(i) func executeGetRequest(baseAssetStr: String, userAddressStr: String, getTxIdStr: String) = { + strict checkForceStop = isForceStopped() let userAddress = addressFromStringValue(userAddressStr) let assetCfgArray = readAssetCfgOrFail(baseAssetStr) @@ -383,6 +403,7 @@ func executeGetRequest(baseAssetStr: String, userAddressStr: String, getTxIdStr: @Callable(i) func topUpBalance(baseAssetStr: String) = { + strict checkForceStop = isForceStopped() let assetCfgArray = readAssetCfgOrFail(baseAssetStr) let pmt = i.payments[0].value() let pmtAssetId = pmt.assetId.value() @@ -410,6 +431,7 @@ func topUpBalance(baseAssetStr: String) = { @Callable(i) func currentSysParamsREST(baseAssetStr: String)= { + strict checkForceStop = isForceStopped() let sysStateTuple = privateCurrentSysParamsREST(baseAssetStr) # (price, decimalsMultPrice, baseAssetBalance, totalLockedBaseAmount, baseAssetBalanceConsideringLock, shareEmission) let price = sysStateTuple._1.value diff --git a/ride/lp_staking_pools.ride b/ride/lp_staking_pools.ride index 40d66fdfb..b8ec9c9af 100644 --- a/ride/lp_staking_pools.ride +++ b/ride/lp_staking_pools.ride @@ -188,8 +188,24 @@ func calcPrice(lpAssetId: ByteVector, shareAssetId: ByteVector) = { price } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(separator) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(separator) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func emit(assetId: ByteVector, amount: Int) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustThis() let isReissuable = true @@ -201,6 +217,7 @@ func emit(assetId: ByteVector, amount: Int) = { @Callable(i) func burn(assetId: ByteVector, amount: Int) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustThis() ([ @@ -256,6 +273,7 @@ func create( @Callable(i) func put() = { + strict checkForceStop = isForceStopped() let pmt = if (i.payments.size() == 1) then i.payments[0] else "exactly 1 payment is expected".throwErr() let baseAssetId = pmt.assetId let userAddress = i.caller @@ -294,6 +312,7 @@ func put() = { # finish put @Callable(i) func claimShareAsset(baseAssetIdStr: String, userAddressStr: String) = { + strict checkForceStop = isForceStopped() # if userAddressStr != "" then i.mustThis() # if userAddressStr == "" then use i.caller strict checks = [ @@ -337,6 +356,7 @@ func claimShareAsset(baseAssetIdStr: String, userAddressStr: String) = { @Callable(i) func get() = { + strict checkForceStop = isForceStopped() strict checks = [ i.shutdownCheck() ] @@ -381,6 +401,7 @@ func get() = { # finish get @Callable(i) func claimBaseAsset(baseAssetIdStr: String, userAddressStr: String) = { + strict checkForceStop = isForceStopped() strict checks = [ i.shutdownCheck() ] @@ -425,6 +446,7 @@ func claimBaseAsset(baseAssetIdStr: String, userAddressStr: String) = { @Callable(i) func finalize(baseAssetIdStr: String) = { + strict checkForceStop = isForceStopped() strict checks = [ i.shutdownCheck() ] diff --git a/ride/lp_staking_v2.ride b/ride/lp_staking_v2.ride index 8ef7b27fa..63ad86740 100644 --- a/ride/lp_staking_v2.ride +++ b/ride/lp_staking_v2.ride @@ -248,8 +248,24 @@ func mustProxyAddress(i: Invocation, assetId: String) = { } } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func constructor(assetsStoreContract: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [StringEntry(keyAssetsStoreContract(), assetsStoreContract)] @@ -304,6 +320,7 @@ func adminRegisterAsset(baseAssetStr: String, shareAssetName: String, shareAsset @Callable(i) func shutdownPut(internalBaseAssetId: Int) = { + strict checkForceStop = isForceStopped() let internalBaseAssetIdStr = internalBaseAssetId.toString() let baseAssetIdStr = getStringOrFail(keyMappingsInternal2baseAssetId(internalBaseAssetId)) let shutdownManagerAddress = getStringOrFail(keyShutdownManager(internalBaseAssetIdStr)) @@ -315,6 +332,7 @@ func shutdownPut(internalBaseAssetId: Int) = { @Callable(i) func put() = { + strict checkForceStop = isForceStopped() let pmt = i.payments[0].value() let baseAssetId = pmt.assetId.value() let baseAssetStr = baseAssetId.toBase58String() @@ -347,6 +365,7 @@ func put() = { @Callable(i) func submitGetRequest() = { + strict checkForceStop = isForceStopped() let pmt = i.payments[0].value() let shareAssetId = pmt.assetId.value() let shareAssetStr = shareAssetId.toBase58String() @@ -387,6 +406,7 @@ func submitGetRequest() = { @Callable(i) func executeGetRequest(baseAssetStr: String, userAddressStr: String, getTxIdStr: String) = { + strict checkForceStop = isForceStopped() let userAddress = addressFromStringValue(userAddressStr) let assetCfgArray = readAssetCfgOrFail(baseAssetStr) @@ -418,6 +438,7 @@ func executeGetRequest(baseAssetStr: String, userAddressStr: String, getTxIdStr: @Callable(i) func topUpBalance(baseAssetStr: String, delta: Int) = { + strict checkForceStop = isForceStopped() let assetCfgArray = readAssetCfgOrFail(baseAssetStr) let pmt = i.payments[0].value() let pmtAssetId = pmt.assetId.value() @@ -449,6 +470,7 @@ func topUpBalance(baseAssetStr: String, delta: Int) = { @Callable(i) func currentSysParamsREST(baseAssetStr: String)= { + strict checkForceStop = isForceStopped() let sysStateTuple = privateCurrentSysParamsREST(baseAssetStr) # (price, decimalsMultPrice, baseAssetBalance, totalLockedBaseAmount, baseAssetBalanceConsideringLock, shareEmission) let price = sysStateTuple._1.value diff --git a/ride/otc_multiasset.ride b/ride/otc_multiasset.ride index 2273a6085..6763d46a9 100644 --- a/ride/otc_multiasset.ride +++ b/ride/otc_multiasset.ride @@ -79,9 +79,25 @@ func fmtErr(msg: String) = ["otc_multiasset.ride:", msg].makeString(" ") func throwErr(msg: String) = msg.fmtErr().throw() +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + #registering a new pair of assets @Callable(i) func registerAsset(assetA: String, assetB: String, withdrawDelay: Int, depositFee: Int, withdrawFee: Int, minAmountDeposit: Int, minAmountWithdraw: Int, pairStatus: Int) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() let withdrawDelayKey = keyWithdrawDelay(assetA, assetB) @@ -104,6 +120,7 @@ func registerAsset(assetA: String, assetB: String, withdrawDelay: Int, depositFe #exchange asset A for B @Callable(i) func swapAssetsAToB(assetB: String) = { + strict checkForceStop = isForceStopped() let payment = i.payments[0].value() let assetA = payment.assetId.value().toBase58String() let asset = assetB.value().fromBase58String() @@ -136,6 +153,7 @@ func swapAssetsAToB(assetB: String) = { #initialize the exchange of asset B for A @Callable(i) func initializationSwapAssetsBToA(assetA: String) = { + strict checkForceStop = isForceStopped() let payment = i.payments[0].value() let toWithdraw = payment.amount let assetB = payment.assetId.value().toBase58String() @@ -168,6 +186,7 @@ func initializationSwapAssetsBToA(assetA: String) = { #withdraw assets initialized at the specified height @Callable(i) func withdrawAsset(assetA: String, assetB: String, heightInKey: Int) = { + strict checkForceStop = isForceStopped() let userAddress = i.caller.toString() let toWithdraw = keyProcessInProgress(userAddress, assetA, assetB, heightInKey).getInteger().valueOrErrorMessage("At this height, withdraw was not initialized with this pair of assets.".fmtErr()) @@ -186,6 +205,7 @@ func withdrawAsset(assetA: String, assetB: String, heightInKey: Int) = { #withdraws the collected commission to the manager's address @Callable(i) func withdrawFee(assetA: String, assetB: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() let toWithdrawA = keyTotalFeeCollectedDeposit(assetA, assetB).getInteger().valueOrElse(0) diff --git a/ride/proposal.ride b/ride/proposal.ride index 7c5f0f1f6..954a052e6 100644 --- a/ride/proposal.ride +++ b/ride/proposal.ride @@ -82,8 +82,24 @@ func mustManager(i: Invocation) = { i.isManager() || "permission denied".throw() } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func startNewVote(name: String, description: String, expirationHeight: Int, quorumNumber: Int) = { + strict checkForceStop = isForceStopped() strict checks = [ i.mustManager() ] @@ -108,6 +124,7 @@ func startNewVote(name: String, description: String, expirationHeight: Int, quor @Callable(i) func voteFor(proposalIndex: Int, choice: Boolean) = { + strict checkForceStop = isForceStopped() let EMPTY = "EMPTY" let voteInfo = this.getString(keyProposalInfo(proposalIndex)).valueOrElse(EMPTY) let voteInfoArray = voteInfo.split(SEP) @@ -172,6 +189,7 @@ func voteFor(proposalIndex: Int, choice: Boolean) = { @Callable(i) func deleteVote(proposalIndex: Int) = { + strict checkForceStop = isForceStopped() let EMPTY = "EMPTY" let voteInfo = this.getString(keyProposalInfo(proposalIndex)).valueOrElse(EMPTY) let voteInfoArray = voteInfo.split(SEP) @@ -214,6 +232,7 @@ func deleteVote(proposalIndex: Int) = { @Callable(i) func changeVote(proposalIndex: Int, choice: Boolean) = { + strict checkForceStop = isForceStopped() let EMPTY = "EMPTY" let voteInfo = this.getString(keyProposalInfo(proposalIndex)).valueOrElse(EMPTY) let voteInfoArray = voteInfo.split(SEP) @@ -281,6 +300,7 @@ func changeVote(proposalIndex: Int, choice: Boolean) = { @Callable(i) func getResultREADONLY(proposalIndex: Int) = { + strict checkForceStop = isForceStopped() let EMPTY = "EMPTY" let voteInfo = this.getString(keyProposalInfo(proposalIndex)).valueOrElse(EMPTY) let voteInfoArray = voteInfo.split(SEP) diff --git a/ride/referral.ride b/ride/referral.ride index 99d0476fe..10b2f00a6 100644 --- a/ride/referral.ride +++ b/ride/referral.ride @@ -114,8 +114,24 @@ func updateAllReferralPrograms(address: String, programName: String) = { updatedAllReferralPrograms } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(separator) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(separator) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func claimInternal(programName: String, claimerAddress: String, isImplementationContract: Boolean) = { + strict checkForceStop = isForceStopped() strict checkProgramExists = programName.keyProgramName().getBoolean().valueOrElse( false ) || "program does not exist".throwErr() @@ -182,6 +198,7 @@ func claimInternal(programName: String, claimerAddress: String, isImplementation @Callable(i) func claimBulkInternal(claimer: String, programNames: List[String], currentIter: Int) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller == this || "permission denied".throwErr() if (currentIter == programNames.size()) then ([]) else @@ -266,6 +283,7 @@ func incUnclaimedWithPaymentInternal( @Callable(i) func createReferralProgram(programName: String, treasuryContract: String, implementationContract: String, rewardAssetId: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() strict checkProgramName = programName.keyProgramName().getBoolean() == unit || "program name already exists".throwErr() let programNameIsValid = !programName.contains(separator) && !programName.contains(" ") @@ -389,6 +407,7 @@ func incUnclaimed( @Callable(i) func incUnclaimedWithPayment(programName: String, referrerAddresses: List[String]) = { + strict checkForceStop = isForceStopped() strict checkProgram = programName == "wxSpotFee" || "invalid program".throwErr() func map(accum: List[Int], next: AttachedPayment) = { @@ -419,6 +438,7 @@ func incUnclaimedWithPayment(programName: String, referrerAddresses: List[String @Callable(i) func claim(programName: String) = { + strict checkForceStop = isForceStopped() strict checkProgramExists = programName.keyProgramName().getBoolean().valueOrElse( false ) || "program does not exist".throwErr() @@ -439,6 +459,7 @@ func claim(programName: String) = { @Callable(i) func claimBulk() = { + strict checkForceStop = isForceStopped() let callerAddress = i.caller.toString() let callerProgramsStr = i.caller.toString().keyAllReferralPrograms().getString() @@ -460,6 +481,7 @@ func claimBulk() = { @Callable(i) func claimREADONLY(programName: String, userAddress: String) = { + strict checkForceStop = isForceStopped() let claimerClaimed = programName.keyClaimedReferrer(userAddress).getInteger().valueOrElse(0) let claimerUnclaimed = programName.keyUnclaimedReferrer(userAddress).getInteger().valueOrElse(0) @@ -468,6 +490,7 @@ func claimREADONLY(programName: String, userAddress: String) = { @Callable(i) func claimBulkREADONLY(claimer: String) = { + strict checkForceStop = isForceStopped() let claimedTotalAddress = claimer.keyClaimedTotalAddress().getInteger().valueOrElse(0) let unclaimedTotalAddress = claimer.keyUnclaimedTotalAddress().getInteger().valueOrElse(0) @@ -476,6 +499,7 @@ func claimBulkREADONLY(claimer: String) = { @Callable(i) func setTotalKeys(programName: String, address: String) = { + strict checkForceStop = isForceStopped() let claimedReferrer = programName.keyClaimedReferrer(address).getInteger().valueOrElse(0) let claimedReferral = programName.keyClaimedReferral(address).getInteger().valueOrElse(0) let unclaimedReferrer = programName.keyUnclaimedReferrer(address).getInteger().valueOrElse(0) diff --git a/ride/slippage.ride b/ride/slippage.ride index ca02a82aa..e85ff217f 100644 --- a/ride/slippage.ride +++ b/ride/slippage.ride @@ -94,8 +94,24 @@ func mustManager(i: Invocation) = { } } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func constructor(factoryContract: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [StringEntry(keyFactoryContract(), factoryContract)] } @@ -127,6 +143,7 @@ func put() = { @Callable(i) func invest(poolAddressStr: String) = { + strict checkForceStop = isForceStopped() let poolAddress = poolAddressStr.addressFromString().valueOrErrorMessage("invalid pool address") let poolAssets = factoryContract.getString(keyMappingPoolContractAddressToPoolAssets(poolAddressStr)).valueOrErrorMessage("Invalid pool passed.").split(SEP) let amId = factoryContract.getString(keyMappingsInternal2baseAssetId(poolAssets[1].parseIntValue())).valueOrErrorMessage("No asset mapping found") diff --git a/ride/staking.ride b/ride/staking.ride index 398c1f89c..79fea96bb 100644 --- a/ride/staking.ride +++ b/ride/staking.ride @@ -469,8 +469,24 @@ func unstakeActions( ++ listActions } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func constructor(factoryAddressStr: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [StringEntry(keyFactoryAddress(), factoryAddressStr)] @@ -478,6 +494,7 @@ func constructor(factoryAddressStr: String) = { @Callable(i) func constructorV2(votingEmissionContract: String) = { + strict checkForceStop = isForceStopped() strict cheks = [ i.mustManager(), votingEmissionContract.addressFromString() != unit || "invalid voting emission contract address" @@ -490,6 +507,7 @@ func constructorV2(votingEmissionContract: String) = { @Callable(i) func stake() = { + strict checkForceStop = isForceStopped() if (i.payments.size() != 1) then throwErr("invalid payment - exact one payment must be attached") else let pmt = i.payments[0] let lpAssetId = pmt.assetId.value() @@ -510,6 +528,7 @@ func stake() = { @Callable(i) func stakeFor(userAddressStr: String) = { + strict checkForceStop = isForceStopped() if (i.payments.size() != 1) then throwErr("invalid payment - exact one payment must be attached") else let pmt = i.payments[0] let lpAssetId = pmt.assetId.value() @@ -532,6 +551,7 @@ func stakeFor(userAddressStr: String) = { @Callable(i) func unstake(lpAssetIdStr: String, amount: Int) = { + strict checkForceStop = isForceStopped() let lpAssetId = lpAssetIdStr.fromBase58String() let poolAddressStr = factoryContract.getString(keyFactoryLp2AssetsMapping(lpAssetIdStr)).valueOrErrorMessage(("unsupported lp asset " + lpAssetIdStr).wrapErr()) let poolAddress = poolAddressStr.addressFromStringValue() @@ -573,6 +593,7 @@ func unstakeINTERNAL( @Callable(i) func claimWx(lpAssetIdStr: String) = { + strict checkForceStop = isForceStopped() let userAddressStr = i.caller.toString() let throwIfNothingToClaim = true let result = this.invoke("claimWxINTERNAL", [lpAssetIdStr, userAddressStr, throwIfNothingToClaim], []) @@ -582,6 +603,7 @@ func claimWx(lpAssetIdStr: String) = { @Callable(i) func claimWxDoNotThrow(lpAssetIdStr: String) = { + strict checkForceStop = isForceStopped() let userAddressStr = i.caller.toString() let throwIfNothingToClaim = false let result = this.invoke("claimWxINTERNAL", [lpAssetIdStr, userAddressStr, throwIfNothingToClaim], []) @@ -591,6 +613,7 @@ func claimWxDoNotThrow(lpAssetIdStr: String) = { @Callable(i) func claimWxINTERNAL(lpAssetIdStr: String, userAddressStr: String, throwIfNothingToClaim: Boolean) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustThis() let userAddress = userAddressStr.addressFromString().valueOrErrorMessage(("claimWxINTERNAL: invalid user address").wrapErr()) @@ -640,6 +663,7 @@ func claimWxINTERNAL(lpAssetIdStr: String, userAddressStr: String, throwIfNothin @Callable(i) func claimWxBulkInternalREADONLY(currentIter: Int, lpAssetIds: List[String], userAddressStr: String, resAcc: List[String]) = { + strict checkForceStop = isForceStopped() if (currentIter == lpAssetIds.size()) then ([], resAcc) else let lpAssetId = lpAssetIds[currentIter] @@ -655,12 +679,14 @@ func claimWxBulkInternalREADONLY(currentIter: Int, lpAssetIds: List[String], use # Returns [%d%d%d______] @Callable(i) func claimWxBulkREADONLY(lpAssetIds: List[String], userAddressStr: String) = { + strict checkForceStop = isForceStopped() let res = this.invoke("claimWxBulkInternalREADONLY", [0, lpAssetIds, userAddressStr, []], []) ([], res) } @Callable(i) func claimWxREADONLY(lpAssetIdStr: String, userAddressStr: String) = { + strict checkForceStop = isForceStopped() let stakedByUserKEY = keyStakedByUser(userAddressStr, lpAssetIdStr) let stakedTotalKEY = keyStakedTotal(lpAssetIdStr) let claimedByUserKEY = keyClaimedByUser(lpAssetIdStr, userAddressStr) @@ -699,6 +725,7 @@ func claimWxREADONLY(lpAssetIdStr: String, userAddressStr: String) = { @Callable(i) func stakedByUserREADONLY(lpAssetIdStr: String, userAddressStr: String) = { + strict checkForceStop = isForceStopped() let stakedByUser = userAddressStr.keyStakedByUser(lpAssetIdStr).getInteger().valueOrElse(0) ([], stakedByUser) @@ -706,6 +733,7 @@ func stakedByUserREADONLY(lpAssetIdStr: String, userAddressStr: String) = { @Callable(i) func stakedTotalREADONLY(lpAssetIdStr: String) = { + strict checkForceStop = isForceStopped() let stakedTotal = lpAssetIdStr.keyStakedTotal().getInteger().valueOrElse(0) ([], stakedTotal) @@ -713,6 +741,7 @@ func stakedTotalREADONLY(lpAssetIdStr: String) = { @Callable(i) func usersListTraversal(lpAssetId: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller.bytes.toBase58String() == this.getString(keyVotingEmissionContract()).valueOrElse("") || i.mustManager() let listName = lpAssetId.getUsersListName() let userOrUnit = lpAssetId.keyNextUser().getString() @@ -748,6 +777,7 @@ func usersListTraversal(lpAssetId: String) = { @Callable(i) func onModifyWeight(lpAssetIdStr: String, poolAddressStr: String) = { + strict checkForceStop = isForceStopped() if (i.caller != factoryContract) then throwErr("permissions denied") else let (wxPerLpIntegralNew, poolIntegralSTATE, poolDEBUG) = refreshPoolINTEGRALS(lpAssetIdStr, poolAddressStr, 0) poolIntegralSTATE diff --git a/ride/swap.ride b/ride/swap.ride index 51872628f..9371bfd37 100644 --- a/ride/swap.ride +++ b/ride/swap.ride @@ -135,12 +135,28 @@ func getPoolAddressAndCheckPoolStatus( (poolAdr, isReverse) } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func swapCalculateREADONLY( amountIn: Int, assetIn: String, assetOut: String ) = { + strict checkForceStop = isForceStopped() let (poolAdr, isReverse) = getPoolAddressAndCheckPoolStatus(assetIn, assetOut) let (plFee, prFee) = getSwapFees(poolAdr.toString()) @@ -176,6 +192,7 @@ func swap( assetOutRaw: String, addressTo: String ) = { + strict checkForceStop = isForceStopped() let assetOut = if (assetOutRaw == "") then "WAVES" else assetOutRaw let pmt = i.payments[0].value() let assetIn = if(pmt.assetId == unit) then "WAVES" else pmt.assetId.value().toBase58String() diff --git a/ride/user_pools.ride b/ride/user_pools.ride index 2ac98c8c9..29b5cca6d 100644 --- a/ride/user_pools.ride +++ b/ride/user_pools.ride @@ -198,8 +198,24 @@ func _create(i: Invocation, poolType: String) = { ] } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func constructor(factoryV2Address: String, assetsStoreAddress: String, emissionAddress: String, feeAssetId: String, feeAmount: Int) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [ @@ -213,11 +229,13 @@ func constructor(factoryV2Address: String, assetsStoreAddress: String, emissionA @Callable(i) func create() = { + strict checkForceStop = isForceStopped() _create(i, VLTPOOL) } @Callable(i) func createStable() = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() _create(i, STBLPOOL) @@ -225,6 +243,7 @@ func createStable() = { @Callable(i) func activate(poolAddress: String, amountAssetId: String, amountAssetTicker: String, priceAssetId: String, priceAssetTicker: String, logo: String) = { + strict checkForceStop = isForceStopped() strict checks = [ i.mustAdmin(), isCreateCalled(amountAssetId, priceAssetId) || throwCreateNotCalled(), @@ -275,17 +294,20 @@ func activate(poolAddress: String, amountAssetId: String, amountAssetTicker: Str @Callable(i) func setAdmins(adminPubKeys: List[String]) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.mustManager() [StringEntry(keyAdminPubKeys(), adminPubKeys.makeString(SEP))] } @Callable(i) func priceAssetsREADONLY() = { + strict checkForceStop = isForceStopped() (nil, keyPriceAssets().getString().stringOptionToList()) } @Callable(i) func statusREADONLY(amountAssetId: String, priceAssetId: String) = { + strict checkForceStop = isForceStopped() let status = match keyStatus(amountAssetId, priceAssetId).getString() { case s: String => s case _ => unit @@ -296,12 +318,14 @@ func statusREADONLY(amountAssetId: String, priceAssetId: String) = { @Callable(i) func getPoolCreatorREADONLY(amountAssetId: String, priceAssetId: String) = { + strict checkForceStop = isForceStopped() let creator = keyCreateCaller(amountAssetId, priceAssetId).getString().valueOrElse("") (nil, creator) } @Callable(i) func deletePool(amountAssetId: String, priceAssetId: String) = { + strict checkForceStop = isForceStopped() strict checkCaller = i.caller == factoryContract || i.mustManager() || "Permission denied".throwErr() [ diff --git a/ride/vesting.ride b/ride/vesting.ride index 299e41c27..67e3c02e8 100644 --- a/ride/vesting.ride +++ b/ride/vesting.ride @@ -124,8 +124,24 @@ func claimInternal(address: String) = { } } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func constructor(wxAssetId: String) = { + strict checkForceStop = isForceStopped() strict check = i.mustManager() [StringEntry(keyWxAssetId(), wxAssetId)] @@ -133,6 +149,7 @@ func constructor(wxAssetId: String) = { @Callable(i) func withdrawRevoked() = { + strict checkForceStop = isForceStopped() let amount = keyRevokedTotal().getInteger().valueOrElse(0) let wx = keyWxAssetId().getString().value().fromBase58String() @@ -150,6 +167,7 @@ func withdrawRevoked() = { # Create vesting to address for attached amount of WX @Callable(i) func createDepositFor(address: String, blocksDuration: Int) = { + strict checkForceStop = isForceStopped() let amount = i.payments[0].amount let endHeight = height + blocksDuration @@ -174,6 +192,7 @@ func createDepositFor(address: String, blocksDuration: Int) = { @Callable(i) func increaseDepositFor(address: String) = { + strict checkForceStop = isForceStopped() let availableToClaim = availableToClaimNow(address) strict forceClaim = if (availableToClaim > 0) then { claimInternal(address) @@ -209,6 +228,7 @@ func increaseDepositFor(address: String) = { @Callable(i) func revokeDepositFor(address: String) = { + strict checkForceStop = isForceStopped() let totalAmount = getUserTotalAmount(address) let remainingAmount = getUserRemainingAmount(address) let claimedWXAmount = getUserClaimedAmount(address) @@ -235,17 +255,20 @@ func revokeDepositFor(address: String) = { @Callable(i) func claim() = { + strict checkForceStop = isForceStopped() claimInternal(i.caller.bytes.toBase58String()) } @Callable(i) func claimREADONLY(address: String) = { + strict checkForceStop = isForceStopped() let amount = availableToClaimNow(address) ([], amount) } @Callable(i) func stats(address: String) = { + strict checkForceStop = isForceStopped() ([], [ keyUserVestingStart(address).getInteger().value(), keyUserVestingEnd(address).getInteger().value(), diff --git a/ride/vesting_multiasset.ride b/ride/vesting_multiasset.ride index 35f6bf385..d1f3653fc 100644 --- a/ride/vesting_multiasset.ride +++ b/ride/vesting_multiasset.ride @@ -164,8 +164,24 @@ func claimFixedInternal(assetId: String, address: String, releaseBlock: Int) = { ] } +### Force Stop check function +func isForceStopped() = { + let keyForceStopContract = ["%s", "forceStopContract"].makeString(SEP) + let forceStopAddressString = this.getString(keyForceStopContract).valueOrElse("") + let forceStopAddress = forceStopAddressString.addressFromString().valueOrElse(this) + + let keyDisabledStatus = ["%s%s", "disabled", this.toString()].makeString(SEP) + let disabledStatus = forceStopAddress.getBoolean(keyDisabledStatus).valueOrElse(false) + + if (disabledStatus == true) + then "Force stopped".throw() + else false +} +### End Force Stop function + @Callable(i) func withdrawRevoked(assetId: String) = { + strict checkForceStop = isForceStopped() let amount = keyRevokedTotal(assetId).getInteger().valueOrElse(0) let asset = assetId.fromBase58String() @@ -183,6 +199,7 @@ func withdrawRevoked(assetId: String) = { # Create vesting to address for attached amount of asset @Callable(i) func createDepositFor(address: String, blocksDuration: Int) = { + strict checkForceStop = isForceStopped() let amount = i.payments[0].amount let assetId = i.payments[0].assetId.value().toBase58String() @@ -207,6 +224,7 @@ func createDepositFor(address: String, blocksDuration: Int) = { @Callable(i) func increaseDepositFor(address: String) = { + strict checkForceStop = isForceStopped() let amount = i.payments[0].amount let assetId = i.payments[0].assetId.value().toBase58String() @@ -242,6 +260,7 @@ func increaseDepositFor(address: String) = { @Callable(i) func revokeDepositFor(assetId: String, address: String) = { + strict checkForceStop = isForceStopped() let totalAmount = getUserTotalAmount(assetId, address) let remainingAmount = getUserRemainingAmount(assetId, address) let claimedWXAmount = getUserClaimedAmount(assetId, address) @@ -268,22 +287,26 @@ func revokeDepositFor(assetId: String, address: String) = { @Callable(i) func claim(assetId: String) = { + strict checkForceStop = isForceStopped() claimInternal(assetId, i.caller.bytes.toBase58String()) } @Callable(i) func claimFor(assetId: String, address: String) = { + strict checkForceStop = isForceStopped() claimInternal(assetId, address) } @Callable(i) func claimREADONLY(assetId: String, address: String) = { + strict checkForceStop = isForceStopped() let amount = availableToClaimNow(assetId, address) ([], amount) } @Callable(i) func createFixedDepositFor(address: String, releaseBlock: Int) = { + strict checkForceStop = isForceStopped() let amount = i.payments[0].amount let assetId = i.payments[0].assetId.value().toBase58String() let userFixedDeposit = keyUserFixedDeposit(assetId, address, releaseBlock) @@ -300,22 +323,26 @@ func createFixedDepositFor(address: String, releaseBlock: Int) = { @Callable(i) func claimFixed(assetId: String, releaseBlock: Int) = { + strict checkForceStop = isForceStopped() claimFixedInternal(assetId, i.caller.bytes.toBase58String(), releaseBlock) } @Callable(i) func claimFixedFor(assetId: String, address: String, releaseBlock: Int) = { + strict checkForceStop = isForceStopped() claimFixedInternal(assetId, address, releaseBlock) } @Callable(i) func claimFixedREADONLY(assetId: String, address: String, releaseBlock: Int) = { + strict checkForceStop = isForceStopped() let amount = availableToClaimFixed(assetId, address, releaseBlock) ([], amount) } @Callable(i) func revokeFixedDepositFor(assetId: String, address: String, releaseBlock: Int) = { + strict checkForceStop = isForceStopped() strict checks = [ i.mustAdmin(), releaseBlock > height || "can't revoke deposit in past".throwErr(), @@ -332,6 +359,7 @@ func revokeFixedDepositFor(assetId: String, address: String, releaseBlock: Int) @Callable(i) func denyAssetClaim(assetId: String) = { + strict checkForceStop = isForceStopped() strict checks = [ i.mustAdmin(), keyClaimDenied(assetId).getBoolean().valueOrElse(false) == false || "asset already denied".throwErr() @@ -342,6 +370,7 @@ func denyAssetClaim(assetId: String) = { @Callable(i) func allowAssetClaim(assetId: String) = { + strict checkForceStop = isForceStopped() strict checks = [ i.mustAdmin(), keyClaimDenied(assetId).getBoolean().valueOrElse(false) == true || "asset already allowed".throwErr() @@ -352,6 +381,7 @@ func allowAssetClaim(assetId: String) = { @Callable(i) func stats(assetId: String, address: String) = { + strict checkForceStop = isForceStopped() ([], [ keyUserVestingStart(assetId, address).getInteger().value(), keyUserVestingEnd(assetId, address).getInteger().value(), diff --git a/test/components/factory_v2/_hooks.mjs b/test/components/factory_v2/_hooks.mjs index e525a79e3..f6dfd188a 100644 --- a/test/components/factory_v2/_hooks.mjs +++ b/test/components/factory_v2/_hooks.mjs @@ -26,7 +26,7 @@ export const mochaHooks = { async beforeAll() { const spinner = ora('Initializing').start(); // setup accounts - const names = ['factory', 'store', 'lp', 'matcher', 'user', 'userpools', 'voting_emission']; + const names = ['factory', 'store', 'lp', 'matcher', 'user', 'userpools', 'voting_emission', 'forceStop']; this.accounts = Object.fromEntries(names.map((item) => [item, randomSeed(seedWordsCount)])); const seeds = Object.values(this.accounts); const amount = 1e10; @@ -48,6 +48,11 @@ export const mochaHooks = { type: 'string', value: address(this.accounts.voting_emission, chainId), }, + { + key: '%s__forceStopContract', + type: 'string', + value: address(this.accounts.forceStop, chainId), + }, ], chainId, }, this.accounts.factory); diff --git a/test/components/factory_v2/forceStop.spec.mjs b/test/components/factory_v2/forceStop.spec.mjs new file mode 100644 index 000000000..89b4050fa --- /dev/null +++ b/test/components/factory_v2/forceStop.spec.mjs @@ -0,0 +1,155 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import { address, publicKey } from '@waves/ts-lib-crypto'; +import { + invokeScript, issue, nodeInteraction, data, +} from '@waves/waves-transactions'; +import { create } from '@waves/node-api-js'; + +chai.use(chaiAsPromised); +const { expect } = chai; + +const { waitForTx } = nodeInteraction; + +const apiBase = process.env.API_NODE_URL; +const seed = 'waves private node seed with waves tokens'; +const chainId = 'R'; + +const api = create(apiBase); + +/** @typedef { + * Mocha.Suite & {accounts: Object., wxAssetId: string, usdnAssetId: string} + * } MochaSuiteModified + * */ + +describe('Factory V2 - forceStop', /** @this {MochaSuiteModified} */() => { + before(async function () { + const constructorInvokeTx = invokeScript({ + dApp: address(this.accounts.factory, chainId), + call: { + function: 'constructor', + args: [ + { type: 'string', value: '' }, // stakingContract + { type: 'string', value: '' }, // boostingContract + { type: 'string', value: '' }, // idoContract + { type: 'string', value: '' }, // teamContract + { type: 'string', value: '' }, // emissionContract + { type: 'string', value: '' }, // restContract + { type: 'string', value: '' }, // slpipageContract + { type: 'integer', value: 0 }, // priceDecimals + ], + }, + fee: 9e5, + chainId, + }, this.accounts.factory); + await api.transactions.broadcast(constructorInvokeTx, {}); + await waitForTx(constructorInvokeTx.id, { apiBase }); + + const constructorV2InvokeTx = invokeScript({ + dApp: address(this.accounts.factory, chainId), + call: { + function: 'constructorV2', + args: [ + { type: 'string', value: publicKey(this.accounts.matcher) }, // mathcherPub58Str + ], + }, + fee: 9e5, + chainId, + }, this.accounts.factory); + await api.transactions.broadcast(constructorV2InvokeTx, {}); + await waitForTx(constructorV2InvokeTx.id, { apiBase }); + + const constructorV3InvokeTx = invokeScript({ + dApp: address(this.accounts.factory, chainId), + call: { + function: 'constructorV3', + args: [ + { type: 'string', value: '' }, // daoContract + { type: 'string', value: '' }, // marketingContract + { type: 'string', value: '' }, // gwxRewardsContract + { type: 'string', value: '' }, // birdsContract + ], + }, + fee: 9e5, + chainId, + }, this.accounts.factory); + await api.transactions.broadcast(constructorV3InvokeTx, {}); + await waitForTx(constructorV3InvokeTx.id, { apiBase }); + + const constructorV4InvokeTx = invokeScript({ + dApp: address(this.accounts.factory, chainId), + call: { + function: 'constructorV4', + args: [ + { type: 'string', value: '' }, // legacyFactoryContract + { type: 'list', value: [] }, // legacyPools: List[String] + ], + }, + fee: 9e5, + chainId, + }, this.accounts.factory); + await api.transactions.broadcast(constructorV4InvokeTx, {}); + await waitForTx(constructorV4InvokeTx.id, { apiBase }); + + const constructorV5InvokeTx = invokeScript({ + dApp: address(this.accounts.factory, chainId), + call: { + function: 'constructorV5', + args: [ + { type: 'string', value: address(this.accounts.store, chainId) }, // assetsStoreContract + ], + }, + fee: 9e5, + chainId, + }, this.accounts.factory); + await api.transactions.broadcast(constructorV5InvokeTx, {}); + await waitForTx(constructorV5InvokeTx.id, { apiBase }); + + const dataTx = data({ + data: [ + { + key: `%s%s__disabled__${address(this.accounts.factory, chainId)}`, + type: 'boolean', + value: true, + }, + ], + chainId, + }, this.accounts.forceStop); + + await api.transactions.broadcast(dataTx, {}); + await waitForTx(dataTx.id, { apiBase }); + }); + + it('Factory function call should be rejected as force stop', async function () { + // issue USDN asset + const someAssetIssueTx = issue({ + name: 'some asset', + description: '', + quantity: 1e8, + decimals: 8, + chainId, + }, seed); + await api.transactions.broadcast(someAssetIssueTx, {}); + await waitForTx(someAssetIssueTx.id, { apiBase }); + + const activateNewPoolInvokeTx = invokeScript({ + dApp: address(this.accounts.factory, chainId), + call: { + function: 'activateNewPool', + args: [ + { type: 'string', value: address(this.accounts.lp, chainId) }, // poolAddress + { type: 'string', value: someAssetIssueTx.id }, // amountAssetStr + { type: 'string', value: this.usdnAssetId }, // priceAssetStr + { type: 'string', value: 'NEWLP' }, // lpAssetName + { type: 'string', value: '' }, // lpAssetDescr + { type: 'integer', value: 0 }, // poolWeight + { type: 'string', value: 'VLT' }, // poolType + { type: 'string', value: '' }, // logo + ], + }, + fee: 1e8 + 9e5, + chainId, + }, this.accounts.factory); + return expect(api.transactions.broadcast(activateNewPoolInvokeTx, {})).to.be.rejectedWith('factory_v2.ride: Force stopped'); + }); +}); diff --git a/test/components/forcestop/RejectForceStopByNotAdmin.spec.mjs b/test/components/forcestop/RejectForceStopByNotAdmin.spec.mjs new file mode 100644 index 000000000..19674ad6f --- /dev/null +++ b/test/components/forcestop/RejectForceStopByNotAdmin.spec.mjs @@ -0,0 +1,44 @@ +import chai, { expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { + invokeScript, +} from '@waves/waves-transactions'; +import { create } from '@waves/node-api-js'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +// const { expect } = chai; + +const apiBase = process.env.API_NODE_URL; +const chainId = 'R'; + +const api = create(apiBase); + +/** @typedef { + * Mocha.Suite & {accounts: Object., wxAssetId: string, usdnAssetId: string} + * } MochaSuiteModified + * */ + +describe('ForceStop - force stop contract', /** @this {MochaSuiteModified} */() => { + it('Force stop contract is rejected if caller is not an Admin', async function () { + const poolAddress = this.accounts.dapp1.addr; + + const forceStopInvokeTx = invokeScript({ + dApp: this.accounts.forceStop.addr, + call: { + function: 'forceStopContract', + args: [ + { type: 'string', value: poolAddress }, + { type: 'boolean', value: true }, + ], + }, + fee: 1e8 + 9e5, + chainId, + }, this.accounts.user1.seed); + + await expect(api.transactions.broadcast(forceStopInvokeTx, {})) + .to.be + .rejectedWith('Permission denied'); + }); +}); diff --git a/test/components/forcestop/_hooks.mjs b/test/components/forcestop/_hooks.mjs new file mode 100644 index 000000000..6227e260f --- /dev/null +++ b/test/components/forcestop/_hooks.mjs @@ -0,0 +1,54 @@ +import { address, randomSeed, publicKey } from '@waves/ts-lib-crypto'; +import { + data, + massTransfer, +} from '@waves/waves-transactions'; +import { format } from 'path'; +import { setScriptFromFile } from '../../utils/utils.mjs'; + +import { + broadcastAndWait, chainId, baseSeed, +} from '../../utils/api.mjs'; + +const seedWordsCount = 5; +const ridePath = '../ride'; +const forceStopPath = format({ dir: ridePath, base: 'forcestop.ride' }); + +export const mochaHooks = { + async beforeAll() { + const names = [ + 'forceStop', + 'admin1', + 'admin2', + 'user1', + 'dapp1', + ]; + this.accounts = Object.fromEntries(names.map((item) => { + const itemSeed = randomSeed(seedWordsCount); + return [ + item, + { seed: itemSeed, addr: address(itemSeed, chainId), publicKey: publicKey(itemSeed) }, + ]; + })); + const amount = 100e8; + const massTransferTx = massTransfer({ + transfers: Object.values(this.accounts).map((item) => ({ recipient: item.addr, amount })), + chainId, + }, baseSeed); + await broadcastAndWait(massTransferTx); + + await broadcastAndWait(data({ + additionalFee: 4e5, + data: [ + { + key: '%s__forceStopPermission', + type: 'string', + value: `${this.accounts.admin1.addr}__${this.accounts.admin2.addr}`, + }, + ], + chainId, + }, this.accounts.forceStop.seed)); + + await setScriptFromFile(forceStopPath, this.accounts.forceStop.seed); + }, +}; diff --git a/test/components/forcestop/forceStopByAdmin.spec.mjs b/test/components/forcestop/forceStopByAdmin.spec.mjs new file mode 100644 index 000000000..1263c0e52 --- /dev/null +++ b/test/components/forcestop/forceStopByAdmin.spec.mjs @@ -0,0 +1,53 @@ +import chai, { expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { + invokeScript, nodeInteraction, +} from '@waves/waves-transactions'; +import { create } from '@waves/node-api-js'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +// const { expect } = chai; + +const { waitForTx } = nodeInteraction; + +const apiBase = process.env.API_NODE_URL; +const chainId = 'R'; + +const api = create(apiBase); + +/** @typedef { + * Mocha.Suite & {accounts: Object., wxAssetId: string, usdnAssetId: string} + * } MochaSuiteModified + * */ + +describe('ForceStop - force stop contract', /** @this {MochaSuiteModified} */() => { + it('Force stop contract by Admin', async function () { + const poolAddress = this.accounts.dapp1.addr; + + const forceStopInvokeTx = invokeScript({ + dApp: this.accounts.forceStop.addr, + call: { + function: 'forceStopContract', + args: [ + { type: 'string', value: poolAddress }, + { type: 'boolean', value: true }, + ], + }, + fee: 1e8 + 9e5, + chainId, + }, this.accounts.admin1.seed); + + await api.transactions.broadcast(forceStopInvokeTx, {}); + const { stateChanges } = await waitForTx(forceStopInvokeTx.id, { apiBase }); + + expect(stateChanges.data).to.deep.eql([ + { + key: `%s%s__disabled__${poolAddress}`, + type: 'boolean', + value: true, + }, + ]); + }); +}); diff --git a/test/components/forcestop/forceStopByManager.spec.mjs b/test/components/forcestop/forceStopByManager.spec.mjs new file mode 100644 index 000000000..4b15aa298 --- /dev/null +++ b/test/components/forcestop/forceStopByManager.spec.mjs @@ -0,0 +1,53 @@ +import chai, { expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { + invokeScript, nodeInteraction, +} from '@waves/waves-transactions'; +import { create } from '@waves/node-api-js'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +// const { expect } = chai; + +const { waitForTx } = nodeInteraction; + +const apiBase = process.env.API_NODE_URL; +const chainId = 'R'; + +const api = create(apiBase); + +/** @typedef { + * Mocha.Suite & {accounts: Object., wxAssetId: string, usdnAssetId: string} + * } MochaSuiteModified + * */ + +describe('ForceStop - force stop contract', /** @this {MochaSuiteModified} */() => { + it('Force stop contract by Manager', async function () { + const poolAddress = this.accounts.dapp1.addr; + + const forceStopInvokeTx = invokeScript({ + dApp: this.accounts.forceStop.addr, + call: { + function: 'forceStopContract', + args: [ + { type: 'string', value: poolAddress }, + { type: 'boolean', value: true }, + ], + }, + fee: 1e8 + 9e5, + chainId, + }, this.accounts.forceStop.seed); + + await api.transactions.broadcast(forceStopInvokeTx, {}); + const { stateChanges } = await waitForTx(forceStopInvokeTx.id, { apiBase }); + + expect(stateChanges.data).to.deep.eql([ + { + key: `%s%s__disabled__${poolAddress}`, + type: 'boolean', + value: true, + }, + ]); + }); +}); diff --git a/test/components/lp/_hooks.mjs b/test/components/lp/_hooks.mjs index c5c14e20b..28050b7aa 100644 --- a/test/components/lp/_hooks.mjs +++ b/test/components/lp/_hooks.mjs @@ -29,6 +29,7 @@ export const mochaHooks = { const names = [ 'lp', 'factoryV2', + 'forceStop', 'feeCollector', 'staking', 'slippage', diff --git a/test/components/lp/forceStop_factory.mjs b/test/components/lp/forceStop_factory.mjs new file mode 100644 index 000000000..dc7673aa6 --- /dev/null +++ b/test/components/lp/forceStop_factory.mjs @@ -0,0 +1,73 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import { address, publicKey } from '@waves/ts-lib-crypto'; +import { invokeScript, data, nodeInteraction as ni } from '@waves/waves-transactions'; +import { create } from '@waves/node-api-js'; + +chai.use(chaiAsPromised); +const { expect } = chai; + +const apiBase = process.env.API_NODE_URL; +const chainId = 'R'; + +const api = create(apiBase); + +describe('lp: forceStop_factory.mjs', /** @this {MochaSuiteModified} */() => { + it( + 'should reject if pool force stopped in ForceStop contract', + async function () { + const usdnAmount = 10e6; + const shibAmount = 10e2; + const shouldAutoStake = false; + + const lp = address(this.accounts.lp, chainId); + const factoryAddress = address(this.accounts.factoryV2, chainId); + const forceStopAddress = address(this.accounts.forceStop, chainId); + const factoryDataTx = data({ + additionalFee: 4e5, + data: [ + { + key: '%s__forceStopContract', + type: 'string', + value: forceStopAddress, + }, + ], + chainId, + senderPublicKey: publicKey(this.accounts.factoryV2), + }, this.accounts.manager); + await api.transactions.broadcast(factoryDataTx, {}); + await ni.waitForTx(factoryDataTx.id, { apiBase }); + + const forceStopDataTx = data({ + additionalFee: 4e5, + data: [ + { + key: `%s%s__disabled__${factoryAddress}`, + type: 'boolean', + value: true, + }, + ], + chainId, + }, this.accounts.forceStop); + await api.transactions.broadcast(forceStopDataTx, {}); + await ni.waitForTx(forceStopDataTx.id, { apiBase }); + + const put = invokeScript({ + dApp: lp, + payment: [ + { assetId: this.shibAssetId, amount: shibAmount }, + { assetId: this.usdnAssetId, amount: usdnAmount }, + ], + call: { + function: 'put', + args: [ + { type: 'integer', value: 0 }, + { type: 'boolean', value: shouldAutoStake }, + ], + }, + chainId, + }, this.accounts.user1); + return expect(api.transactions.broadcast(put, {})).to.be.rejectedWith('Force stopped'); + }, + ); +}); diff --git a/test/components/lp/forceStop_pool.mjs b/test/components/lp/forceStop_pool.mjs new file mode 100644 index 000000000..451009358 --- /dev/null +++ b/test/components/lp/forceStop_pool.mjs @@ -0,0 +1,73 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import { address, publicKey } from '@waves/ts-lib-crypto'; +import { invokeScript, data, nodeInteraction as ni } from '@waves/waves-transactions'; +import { create } from '@waves/node-api-js'; + +chai.use(chaiAsPromised); +const { expect } = chai; + +const apiBase = process.env.API_NODE_URL; +const chainId = 'R'; + +const api = create(apiBase); + +describe('lp: forceStop_pool.mjs', /** @this {MochaSuiteModified} */() => { + it( + 'should reject if pool force stopped in ForceStop contract', + async function () { + const usdnAmount = 10e6; + const shibAmount = 10e2; + const shouldAutoStake = false; + + const lp = address(this.accounts.lp, chainId); + // const factoryAddress = address(this.accounts.factoryV2, chainId); + const forceStopAddress = address(this.accounts.forceStop, chainId); + const factoryDataTx = data({ + additionalFee: 4e5, + data: [ + { + key: '%s__forceStopContract', + type: 'string', + value: forceStopAddress, + }, + ], + chainId, + senderPublicKey: publicKey(this.accounts.factoryV2), + }, this.accounts.manager); + await api.transactions.broadcast(factoryDataTx, {}); + await ni.waitForTx(factoryDataTx.id, { apiBase }); + + const forceStopDataTx = data({ + additionalFee: 4e5, + data: [ + { + key: `%s%s__disabled__${lp}`, + type: 'boolean', + value: true, + }, + ], + chainId, + }, this.accounts.forceStop); + await api.transactions.broadcast(forceStopDataTx, {}); + await ni.waitForTx(forceStopDataTx.id, { apiBase }); + + const put = invokeScript({ + dApp: lp, + payment: [ + { assetId: this.shibAssetId, amount: shibAmount }, + { assetId: this.usdnAssetId, amount: usdnAmount }, + ], + call: { + function: 'put', + args: [ + { type: 'integer', value: 0 }, + { type: 'boolean', value: shouldAutoStake }, + ], + }, + chainId, + }, this.accounts.user1); + return expect(api.transactions.broadcast(put, {})).to.be.rejectedWith('Force stopped'); + }, + ); +});