diff --git a/ride/futures_account.ride b/ride/futures_account.ride new file mode 100644 index 00000000..e2318e1b --- /dev/null +++ b/ride/futures_account.ride @@ -0,0 +1,183 @@ +{-# STDLIB_VERSION 7 #-} +{-# CONTENT_TYPE DAPP #-} +{-# SCRIPT_TYPE ACCOUNT #-} + +let separator = "__" +let chainId = this.bytes.drop(1).take(1) +let chainIdW = base16'57' +let contractFilename = "futures_account.ride" + +let kMultisig = "%s__multisig" +func kStatus(dapp: String, txId: String) = ["%s__status", dapp, txId].makeString(separator) + +let kShutdown = "%s__shutdown" + +func wrapErr(s: String) = { + contractFilename + ": " + s +} + +func throwErr(s: String) = { + throw(wrapErr(s)) +} + +func mustAddress(i: Invocation, address: Address) = { + i.caller == address || throwErr("permission denied") +} + +func mustThis(i: Invocation) = { + mustAddress(i, this) +} + +let wavesString = "WAVES" + +func parseAssetId(input: String) = { + if (input == wavesString) then unit else input.fromBase58String() +} + +func assetIdToString(input: ByteVector|Unit) = { + if (input == unit) then wavesString else input.value().toBase58String() +} + +let kFactoryPublicKey = "%s__factoryPublicKey" + +func kAccountAddressToRequestId(accountAddress: Address) = { + ["%s%s", accountAddress.toString(), "accountAddressToRequestId"].makeString(separator) +} +func kAccountOwner(requestId: String) = { + ["%s%s", requestId, "ownerPublicKey"].makeString(separator) +} +func kRequestAmountAssetId(requestId: String) = { + ["%s%s", requestId, "amountAssetId"].makeString(separator) +} +func kRequestPriceAssetId(requestId: String) = { + ["%s%s", requestId, "priceAssetId"].makeString(separator) +} + +let factoryAddress = addressFromPublicKey( + this.getBinary(kFactoryPublicKey).valueOrErrorMessage( + wrapErr("factory public key is not defined") + ) +) + +let shutdown = factoryAddress.getBoolean(kShutdown).valueOrElse(false) + +let kCalculatorAddress = "%s__calculatorAddress" +let calculatorAddressOption = match factoryAddress.getString(kCalculatorAddress) { + case s: String => s.addressFromString() + case _: Unit => unit +} +let calculatorAddress = calculatorAddressOption.valueOrErrorMessage(wrapErr("invalid calculator address")) + +let kMatcherPublicKey = "%s__matcherPublicKey" +let matcherPublicKeyOption = match factoryAddress.getString(kMatcherPublicKey) { + case s: String => s.fromBase58String() + case _: Unit => unit +} +let matcherPublicKey = matcherPublicKeyOption.valueOrErrorMessage(wrapErr("invalid matcher public key")) + +let requestId = factoryAddress.getStringValue(kAccountAddressToRequestId(this)) +let ownerPublicKey = factoryAddress.getBinaryValue(kAccountOwner(requestId)) +let ownerAddress = addressFromPublicKey(ownerPublicKey) +let amountAssetId = factoryAddress.getStringValue(kRequestAmountAssetId(requestId)).parseAssetId() +let priceAssetId = factoryAddress.getStringValue(kRequestPriceAssetId(requestId)).parseAssetId() + +func mustFactory(i: Invocation) = { + mustAddress(i, factoryAddress) +} + +func mustCalculator(i: Invocation) = { + mustAddress(i, calculatorAddress) +} + +func mustOwner(i: Invocation) = { + mustAddress(i, ownerAddress) +} + +@Callable(i) +func stringEntry(key: String, val: String) = + if (!shutdown && i.mustCalculator()) then ([StringEntry(key, val)], key) else throwErr("not allowed") + +@Callable(i) +func integerEntry(key: String, val: Int) = + if (!shutdown && i.mustCalculator()) then ([IntegerEntry(key, val)], key) else throwErr("not allowed") + +@Callable(i) +func booleanEntry(key: String, val: Boolean) = + if (!shutdown && i.mustCalculator()) then ([BooleanEntry(key, val)], key) else throwErr("not allowed") + +@Callable(i) +func binaryEntry(key: String, val: ByteVector) = + if (!shutdown && i.mustCalculator()) then ([BinaryEntry(key, val)], key) else throwErr("not allowed") + +@Callable(i) +func deleteEntry(key: String) = + if (!shutdown && i.mustCalculator()) then ([DeleteEntry(key)], key) else throwErr("not allowed") + +@Callable(i) +func reissue(assetId: ByteVector, amount: Int, reissuable: Boolean) = + if (!shutdown && i.mustCalculator()) then ([Reissue(assetId, amount, reissuable)], amount) else throwErr("not allowed") + +@Callable(i) +func burn(assetId: ByteVector, amount: Int) = + if (!shutdown && i.mustCalculator()) then ([Burn(assetId, amount)], amount) else throwErr("not allowed") + +@Callable(i) +func transferAsset(recipientBytes: ByteVector, amount: Int, assetId: ByteVector) = + if (!shutdown && i.mustCalculator()) then ([ScriptTransfer(Address(recipientBytes), amount, assetId)], amount) else throwErr("not allowed") + +@Callable(i) +func transferWaves(recipientBytes: ByteVector, amount: Int) = + if (!shutdown && i.mustCalculator()) then ([ScriptTransfer(Address(recipientBytes), amount, unit)], amount) else throwErr("not allowed") + +@Callable(i) +func init(factoryPublicKey: ByteVector, creatorPublicKey: ByteVector) = { + strict checkCaller = i.mustThis() + + # throws if accounts is not ok + strict completeRequest = addressFromPublicKey( + factoryPublicKey + ).invoke("call", ["addAccount", [creatorPublicKey.toBase58String()]], []) + + ([ + BinaryEntry(kFactoryPublicKey, factoryPublicKey) + ], unit) +} + +@Verifier(tx) +func verify() = { + let factoryPublicKeyOption = this.getBinary(kFactoryPublicKey) + match tx { + case o: Order => { + strict checks = [ + !shutdown || throwErr("not allowed"), + o.matcherPublicKey == matcherPublicKey || throwErr( + "matcher public key must be " + matcherPublicKey.toBase58String() + ), + o.assetPair.amountAsset == amountAssetId || throwErr( + "amount asset id have must be " + amountAssetId.assetIdToString() + ), + o.assetPair.priceAsset == priceAssetId || throwErr( + "price asset id have must be" + priceAssetId.assetIdToString() + ), + sigVerify(tx.bodyBytes, tx.proofs[0], ownerPublicKey) || throwErr( + "tx have must be signed by account owner " + ownerPublicKey.toBase58String() + ) + ] + + true + } + case _ => if (factoryPublicKeyOption.isDefined()) then { + match factoryAddress.getString(kMultisig) { + case multisig: String => { + let statusKey = kStatus(this.toString(), tx.id.toBase58String()) + let status = multisig.addressFromStringValue().getBoolean(statusKey).valueOrElse(false) + + status + } + case _ => false + } + } else { + sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) + } + } +} diff --git a/ride/futures_calculator.ride b/ride/futures_calculator.ride new file mode 100644 index 00000000..06cfd2be --- /dev/null +++ b/ride/futures_calculator.ride @@ -0,0 +1,902 @@ +{-# STDLIB_VERSION 7 #-} +{-# CONTENT_TYPE DAPP #-} +{-# SCRIPT_TYPE ACCOUNT #-} + +let separator = "__" +let chainId = this.bytes.drop(1).take(1) +let chainIdW = base16'57' +let contractFilename = "futures_calculator.ride" +let mult8 = 100_000_000 +let mult18BigInt = 1_000_000_000_000_000_000.toBigInt() +let wavesDecimals = 8 +let usdtDecimals = 6 +let wavesString = "WAVES" +let queueItemSize = 32 +let big0 = 0.toBigInt() +let INDEX_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + +let kMultisig = "%s__multisig" +func kStatus(dapp: String, txId: String) = ["%s__status", dapp, txId].makeString(separator) + +let kShutdown = "%s__shutdown" +let kPublicKeys = "%s__publicKeys" +let kMatcherPublicKey = "%s__matcherPublicKey" + +func toX18(origVal: Int, origScaleMult: Int) = fraction(origVal.toBigInt(), mult18BigInt, origScaleMult.toBigInt()) +func fromX18(val: BigInt, resultScaleMult: Int) = fraction(val, resultScaleMult.toBigInt(), mult18BigInt).toInt() + +func validateAddress(address: String) = { + addressFromString(address).isDefined() +} + +func wrapErr(s: String) = { + contractFilename + ": " + s +} + +func throwErr(s: String) = { + throw(wrapErr(s)) +} + +func ensurePositive(v: Int, s: String) = { + if (v >= 0) then v else throwErr(s + " value should be positive") +} + +func parseAssetId(input: String) = { + if (input == wavesString) then unit else input.fromBase58String() +} + +func assetIdToString(input: ByteVector|Unit) = { + if (input == unit) then wavesString else input.value().toBase58String() +} + +func getAssetInfoOrFail(assetId: ByteVector) = { + assetInfo(assetId).valueOrErrorMessage(wrapErr("invalid asset info")) +} + +func getAssetDecimalsOrFail(assetId: ByteVector|Unit) = { + match assetId { + case _: Unit => wavesDecimals + case assetId: ByteVector => getAssetInfoOrFail(assetId).decimals + } +} + +func getAssetBalance(assetId: ByteVector|Unit, address: Address) = { + match assetId { + case _: Unit => address.wavesBalance().available + case assetId: ByteVector => address.assetBalance(assetId) + } +} + +func pow10(n: Int) = pow(10, 0, n, 0, 0, DOWN) + +func pow10Decimals(assetId: ByteVector|Unit) = pow10(getAssetDecimalsOrFail(assetId)) + +let kFactoryAddress = "%s__factoryAddress" +let factoryAddressOption = match this.getString(kFactoryAddress) { + case s: String => s.addressFromString() + case _: Unit => unit +} +let factoryAddress = factoryAddressOption.valueOrErrorMessage(wrapErr("invalid factory address")) + +let kUsdtAssetId = "%s__usdtAssetId" +let usdtAssetIdOption = match factoryAddress.getString(kUsdtAssetId) { + case s: String => parseAssetId(s) + case _: Unit => unit +} +let usdtAssetId = usdtAssetIdOption.valueOrErrorMessage(wrapErr("invalid usdt asset id")) + +let kPricesAddress = "%s__calculatorAddress" + +let shutdown = factoryAddress.getBoolean(kShutdown).valueOrElse(false) + +func mustAddress(caller: Address, address: Address) = { + caller == address || throwErr("permission denied") +} + +func mustThis(caller: Address) = { + mustAddress(caller, this) +} + +func mustFactory(caller: Address) = { + mustAddress(caller, factoryAddress) +} + +func mustAdmin(callerPublicKey: ByteVector) = { + let multisigAddressString = factoryAddress.getString(kMultisig) + let isAdmin = match (multisigAddressString) { + case addressString:String => { + let multisig = addressString.addressFromStringValue() + let publicKeysList = multisig.getStringValue(kPublicKeys).split(separator) + + containsElement(publicKeysList, callerPublicKey.toBase58String()) + } + case _ => callerPublicKey.addressFromPublicKey() == factoryAddress + } + + isAdmin || throwErr("not allowed") +} + +let kAccountScript = "%s__accountScript" +func accountScript() = factoryAddress.getBinary( + kAccountScript +).valueOrErrorMessage(wrapErr("account script is not set")) + +let kRewardAmount = "%s__rewardAmount" +func rewardAmount() = factoryAddress.getInteger( + kRewardAmount +).valueOrErrorMessage(wrapErr("reward amount is not set")) + +let kAccountsLimit = "%s__accountsLimit" +let accountsLimitDefault = 20 +func accountsLimit() = factoryAddress.getInteger( + kAccountsLimit +).valueOrElse(accountsLimitDefault) + +func kDeposited(accountAddress: Address) = ["%s%s", "deposited", accountAddress.toString()].makeString(separator) +func depositedOption(accountAddress: Address) = factoryAddress.getInteger( + kDeposited(accountAddress) +) +func kCredit(accountAddress: Address, assetId: ByteVector|Unit) = ["%s%s%s", "credit", accountAddress.toString(), assetId.assetIdToString()].makeString(separator) +func creditOption(accountAddress: Address, assetId: ByteVector|Unit) = factoryAddress.getInteger( + kCredit(accountAddress, assetId) +) +func kLeverage(accountAddress: Address) = ["%s%s", "leverage", accountAddress.toString()].makeString(separator) +func kRequestLeverage(requestId: ByteVector) = ["%s%s", "requestLeverage", requestId.toBase58String()].makeString(separator) +func kSyntheticAssetId(baseAssetId: ByteVector|Unit) = ["%s%s", "syntheticAssetId", baseAssetId.assetIdToString()].makeString(separator) +func kBaseAssetId(syntheticAssetId: ByteVector|Unit) = ["%s%s", "baseAssetId", syntheticAssetId.assetIdToString()].makeString(separator) + +let REQUEST_STATUS_EMPTY = 0 +let REQUEST_STATUS_READY = 1 +func kRequestStatus(requestId: ByteVector) = { + ["%s%s", requestId.toBase58String(), "status"].makeString(separator) +} + +func kAccountCreatorPublicKey(accountAddress: Address) = { + ["%s%s", accountAddress.toString(), "creatorPublicKey"].makeString(separator) +} +func kRequestOwnerPublicKey(requestId: ByteVector) = { + ["%s%s", requestId.toBase58String(), "ownerPublicKey"].makeString(separator) +} +func kRequestAmountAssetId(requestId: ByteVector) = { + ["%s%s", requestId.toBase58String(), "amountAssetId"].makeString(separator) +} +func kRequestPriceAssetId(requestId: ByteVector) = { + ["%s%s", requestId.toBase58String(), "priceAssetId"].makeString(separator) +} +func kRequestIdToAccountPublicKey(requestId: ByteVector) = { + ["%s%s", requestId.toBase58String(), "requestIdToAccountPublicKey"].makeString(separator) +} +func kAccountAddressToRequestId(accountAddress: Address) = { + ["%s%s", accountAddress.toString(), "accountAddressToRequestId"].makeString(separator) +} + +func kRequestsQueue() = { + ["%s", "requestsQueue"].makeString(separator) +} +func requestsQueue() = factoryAddress.getBinary(kRequestsQueue()).valueOrElse(base58'') + +func kRequestsByOwner(ownerAddress: Address) = { + ["%s%s", "requests", ownerAddress.toString()].makeString(separator) +} +func requestsByOwner(ownerAddress: Address) = factoryAddress.getBinary( + kRequestsByOwner(ownerAddress) +).valueOrElse(base58'') + +func kPairAllowed(amountAssetId: ByteVector|Unit, priceAssetId: ByteVector|Unit) = { + ["%s%s%s", assetIdToString(amountAssetId), assetIdToString(priceAssetId), "pairAllowed"].makeString(separator) +} +func pairAllowed(amountAssetId: ByteVector|Unit, priceAssetId: ByteVector|Unit) = { + factoryAddress.getBoolean( + kPairAllowed(amountAssetId, priceAssetId) + ).valueOrElse(false) +} + +func kPrice(assetId: ByteVector|Unit) = { + ["%s", assetIdToString(assetId)].makeString(separator) +} + +# Pair settings +func kPairPricesListKey(amountAssetId: ByteVector|Unit, priceAssetId: ByteVector|Unit) = { + ["%s%s%s", assetIdToString(amountAssetId), assetIdToString(priceAssetId), "pairSettingPrices"].makeString(separator) +} + +func getPairPricesList(amountAssetId: ByteVector|Unit, priceAssetId: ByteVector|Unit) = { + match (factoryAddress.getString(kPairPricesListKey(amountAssetId, priceAssetId))) { + case s:String => s.split(separator) + case _ => [] + } +} + +func kPairSettingsKey(amountAssetId: ByteVector|Unit, priceAssetId: ByteVector|Unit, priceString: String) = { + ["%s%s%d%s", assetIdToString(amountAssetId), assetIdToString(priceAssetId), priceString, "settings"].makeString(separator) +} + +func getCurrentPrice(assetId: ByteVector|Unit) = { + let matcherPublicKey = factoryAddress.getString(kMatcherPublicKey).valueOrErrorMessage( + wrapErr("invalid matcher public key") + ).fromBase58String() + let matcherAddress = addressFromPublicKey(matcherPublicKey) + let price = matcherAddress.getInteger(kPrice(assetId)).valueOrErrorMessage( + wrapErr("invalid price, assetId = " + assetIdToString(assetId)) + ) + + price +} + +func calcTotalCredit(creditA: BigInt, creditB: BigInt, currentPrice: BigInt) = { + fraction(creditA, currentPrice, mult18BigInt) + creditB +} + +func calcTotalBalance(balanceA: BigInt, balanceB: BigInt, currentPrice: BigInt) = { + fraction(balanceA, currentPrice, mult18BigInt) + balanceB +} + +func calcPnl(totalBalance: BigInt, totalCredit: BigInt) = { + totalBalance - totalCredit +} + +func calcCreditAvailable(deposit: BigInt, leverage: BigInt, totalCredit: BigInt) = { + fraction(deposit, leverage, mult18BigInt) - totalCredit +} + +func calcRealInCredit(credit: BigInt, balance: BigInt) = { + if (credit > big0) then credit - balance else big0 +} + +func calcFree(credit: BigInt, balance: BigInt) = { + if (credit > big0) then balance - credit else big0 +} + +func calcShortPrice(free: BigInt, realInCredit: BigInt) = { + if (realInCredit > big0) then max([big0, fraction(free, mult18BigInt, realInCredit)]) else big0 +} + +func calcLongPrice(free: BigInt, realInCredit: BigInt) = { + if (realInCredit > big0) then max([big0, fraction(realInCredit, mult18BigInt, free)]) else big0 +} + +func calcStartMargin(realInCreditA: BigInt, realInCreditB: BigInt, currentPrice: BigInt, settingsMargin: BigInt) = { + fraction(fraction(realInCreditA, currentPrice, mult18BigInt) + realInCreditB, settingsMargin, mult18BigInt) +} + +func calcMarginSupply(settingsMarginSupply: BigInt, settingsMargin: BigInt, startMargin: BigInt) = { + fraction(settingsMarginSupply, startMargin, settingsMargin) +} + +func calcLiquidationPrice( + deposit: BigInt, + marginSupply: BigInt, + realInCreditA: BigInt, + realInCreditB: BigInt, + shortPrice: BigInt, + longPrice: BigInt +) = { + let liquidationPriceA = if (realInCreditA > big0) then { + (deposit - marginSupply) / realInCreditA + shortPrice + } else big0 + let liquidationPriceB = if (realInCreditB > big0) then { + longPrice - (deposit - marginSupply) / (realInCreditA / longPrice) + } else big0 + liquidationPriceA + liquidationPriceB +} + +func getRequestId(accountAddress: Address) = { + let requestId = factoryAddress.getString( + kAccountAddressToRequestId(accountAddress) + ).valueOrErrorMessage(wrapErr("invalid account address: request id is undefined")).fromBase58String() + + requestId +} + +func getAccountAssets(accountAddress: Address) = { + let requestId = getRequestId(accountAddress) + let amountAssetId = factoryAddress.getString( + kRequestAmountAssetId(requestId) + ).valueOrErrorMessage(wrapErr("invalid amount asset id")).parseAssetId() + let priceAssetId = factoryAddress.getString( + kRequestPriceAssetId(requestId) + ).valueOrErrorMessage(wrapErr("invalid amount price id")).parseAssetId() + + (amountAssetId, priceAssetId) +} + +func getAccountOwnerPublicKey(accountAddress: Address) = { + let requestId = getRequestId(accountAddress) + + let requestOwnerPublicKey = factoryAddress.getBinary( + kRequestOwnerPublicKey(requestId) + ).valueOrErrorMessage(wrapErr("invalid amount asset id")) + + requestOwnerPublicKey +} + +func getAccountInfoInternal(accountAddress: Address) = { + let (amountAssetId, priceAssetId) = getAccountAssets(accountAddress) + let leverage = factoryAddress.getInteger(kLeverage(accountAddress)).valueOrErrorMessage( + wrapErr("leverage should be defined") + ) + + let requestId = getRequestId(accountAddress) + let accountPublicKey = factoryAddress.getBinary(kRequestIdToAccountPublicKey(requestId)).valueOrErrorMessage("kRequestIdToAccountPublicKey not found") + let deposited = depositedOption(accountAddress).valueOrElse(0) + let currentPrice = getCurrentPrice(amountAssetId).toX18(pow10Decimals(priceAssetId)) + let creditA = factoryAddress.getInteger(kCredit(accountAddress, amountAssetId)).valueOrElse(0) + let creditB = factoryAddress.getInteger(kCredit(accountAddress, priceAssetId)).valueOrElse(0) + let status = factoryAddress.getInteger(kRequestStatus(requestId)).valueOrErrorMessage("status not found. RequestId = " + requestId.toBase58String()) + + let synthAmountAssetId = factoryAddress.getString(kSyntheticAssetId(amountAssetId)) + let synthAmountAssetBalance = match (synthAmountAssetId) { + case s:String => getAssetBalance(s.parseAssetId(), accountAddress) + case _ => 0 + } + + let synthPriceAssetId = factoryAddress.getString(kSyntheticAssetId(priceAssetId)) + let synthPriceAssetBalance = match (synthPriceAssetId) { + case s:String => getAssetBalance(s.parseAssetId(), accountAddress) + case _ => 0 + } + + ( + amountAssetId.assetIdToString(), + priceAssetId.assetIdToString(), + accountAddress.toString(), + accountPublicKey.toBase58String(), + leverage, + requestId.toBase58String(), + deposited, + creditA, + creditB, + synthAmountAssetBalance, + synthPriceAssetBalance, + status + ) +} + +# Deposit USDT +# args: [accountAddress] +func depositINTERNAL(callerPublicKey: ByteVector, args: List[String], i: Invocation) = { + let payment = i.payments[0] + let accountAddress = addressFromString(args[0]).valueOrErrorMessage( + wrapErr("invalid account address") + ) + strict ckecks = [ + !shutdown || throwErr("not allowed"), + i.caller.mustFactory(), + i.payments.size() == 1 || throwErr("1 payment is required"), + payment.assetId == usdtAssetId || throwErr("invalid asset"), + # check if account is ready + factoryAddress.getInteger( + kRequestStatus(getRequestId(accountAddress)) + ) == REQUEST_STATUS_READY || throwErr("account is not ready") + ] + + let actions = [ + ScriptTransfer(accountAddress, payment.amount, payment.assetId) + ] + + let factoryActions = [ + factoryAddress.invoke( + "integerEntry", + [ + kDeposited(accountAddress), + depositedOption(accountAddress).valueOrElse(0) + payment.amount + ], [] + ) + ] + + (actions, factoryActions) +} + +# args: [accountAddress, assetId, amount] +func borrowINTERNAL(callerPublicKey: ByteVector, args: List[String], i: Invocation) = { + let accountAddress = addressFromString(args[0]).valueOrErrorMessage( + wrapErr("invalid account address") + ) + let assetIdRequested = parseAssetId(args[1]) + let amountRequested = parseInt(args[2]).valueOrErrorMessage( + wrapErr("invalid requested amount") + ).ensurePositive("requested amount") + let accountOwnerPublicKey = getAccountOwnerPublicKey(accountAddress) + let userAddress = addressFromPublicKey(callerPublicKey) + + strict ckecks = [ + !shutdown || throwErr("not allowed"), + i.caller.mustFactory(), + callerPublicKey == accountOwnerPublicKey || throwErr( + "can be called by account owner only" + ) + ] + + let amountAssetId = getAccountAssets(accountAddress)._1 + let deposited = depositedOption(accountAddress).valueOrElse(0).toX18(pow10Decimals(usdtAssetId)) + let currentPrice = getCurrentPrice(amountAssetId).toX18(pow10Decimals(usdtAssetId)) + let creditA = factoryAddress.getInteger(kCredit(accountAddress, amountAssetId)).valueOrElse(0).toX18(pow10Decimals(amountAssetId)) + let creditB = factoryAddress.getInteger(kCredit(accountAddress, usdtAssetId)).valueOrElse(0).toX18(pow10Decimals(usdtAssetId)) + let totalCredit = calcTotalCredit(creditA, creditB, currentPrice) + let leverage = factoryAddress.getInteger(kLeverage(accountAddress)).valueOrErrorMessage( + wrapErr("leverage should be defined") + ).toX18(1) + let creditAvailableUsdt = calcCreditAvailable(deposited, leverage, totalCredit) + + let creditAvailable = if (assetIdRequested == amountAssetId) then { + fraction(creditAvailableUsdt, mult18BigInt, currentPrice).fromX18(pow10Decimals(amountAssetId)) + } else if (assetIdRequested == usdtAssetId) then { + creditAvailableUsdt.fromX18(pow10Decimals(usdtAssetId)) + } else throwErr("invalid requested asset id") + + let syntheticAssetId = factoryAddress.getString( + kSyntheticAssetId(assetIdRequested) + ).valueOrErrorMessage( + wrapErr("synthetic asset for requested asset is undefined") + ).parseAssetId() + + let amountToSend = if (amountRequested <= creditAvailable) then { + amountRequested + } else throwErr("credit available = " + creditAvailable.toString()) + + let factoryActions = [ + factoryAddress.invoke( + "integerEntry", + [ + kCredit(accountAddress, assetIdRequested), + creditOption(accountAddress, assetIdRequested).valueOrElse(0) + amountToSend + ], [] + ), + factoryAddress.invoke( + "transferAsset", + [ + accountAddress.bytes, + amountToSend, + syntheticAssetId + ], [] + ) + ] + + (nil, [factoryActions, creditAvailable]) +} + +@Callable(i) +func init( + factoryAddressStr: String +) = { + strict checkCaller = i.caller.mustThis() + + ([ + StringEntry(kFactoryAddress, factoryAddressStr) + ], unit) +} + +# TODO: Validate assetIdStrings +# TODO: price asset id should be usdt? +# TODO: validate leverage +# called by user +# additional fee in payment +# args: [amountAssetId, priceAssetId, leverage] +@Callable(i) +func requestAccount(callerPublicKey: ByteVector, args: List[String]) = { + strict ckecks = [ + !shutdown || throwErr("not allowed"), + i.caller.mustFactory() + ] + let amountAssetIdStr = args[0] + let priceAssetIdStr = args[1] + let leverage = parseInt(args[2]).valueOrErrorMessage( + wrapErr("invalid leverage") + ) + let userAddress = addressFromPublicKey(callerPublicKey) + let requestId = sha256(i.transactionId) + let amountAssetId = parseAssetId(amountAssetIdStr) + let priceAssetId = parseAssetId(priceAssetIdStr) + let userRequestsNumber = kRequestsByOwner(userAddress).size() / queueItemSize + + strict checks = [ + i.payments.size() == 1 || throwErr("1 payment is required"), + i.payments[0].assetId == unit || throwErr("invalid asset"), + i.payments[0].amount == rewardAmount() || throwErr("invalid amount"), + pairAllowed(amountAssetId, priceAssetId) || throwErr("pair is not allowed"), + leverage == 2 || throwErr("leverage is not allowed"), + factoryAddress.getInteger(kRequestStatus(requestId)) == unit || throwErr("account is already exists"), + userRequestsNumber < accountsLimit() || throwErr("accounts limit is " + accountsLimit().toString()) + ] + + # add request to queue + let actions = [ + ScriptTransfer(factoryAddress, rewardAmount(), unit) + ] + + (actions, [ + factoryAddress.invoke("integerEntry", [kRequestStatus(requestId), REQUEST_STATUS_EMPTY], []), + factoryAddress.invoke("binaryEntry", [kRequestsQueue(), requestsQueue() + requestId], []), + factoryAddress.invoke("binaryEntry", [kRequestsByOwner(userAddress), requestsByOwner(userAddress) + requestId], []), + factoryAddress.invoke("binaryEntry", [kRequestOwnerPublicKey(requestId), callerPublicKey], []), + factoryAddress.invoke("stringEntry", [kRequestAmountAssetId(requestId), amountAssetIdStr], []), + factoryAddress.invoke("stringEntry", [kRequestPriceAssetId(requestId), priceAssetIdStr], []), + factoryAddress.invoke("integerEntry", [kRequestLeverage(requestId), leverage], []) + ]) +} + +# called by user +# args: [accountAddress, amountAssetId, priceAssetId, leverage] +@Callable(i) +func editAccount(callerPublicKey: ByteVector, args: List[String]) = { + let accountAddress = addressFromString(args[0]).valueOrErrorMessage( + wrapErr("invalid account address") + ) + let amountAssetIdStr = args[1] + let priceAssetIdStr = args[2] + let leverage = parseInt(args[3]).valueOrErrorMessage( + wrapErr("invalid leverage") + ) + let requestId = getRequestId(accountAddress) + let ownerPublicKey = getAccountOwnerPublicKey(accountAddress) + let amountAssetId = parseAssetId(amountAssetIdStr) + let priceAssetId = parseAssetId(priceAssetIdStr) + + strict checks = [ + !shutdown || throwErr("not allowed"), + i.caller.mustFactory(), + callerPublicKey == ownerPublicKey || throwErr("caller is not an account owner"), + pairAllowed(amountAssetId, priceAssetId) || throwErr("pair is not allowed"), + leverage == 2 || throwErr("leverage is not allowed"), + factoryAddress.getInteger( + kRequestStatus(requestId) + ) == REQUEST_STATUS_READY || throwErr("account is not ready") + ] + + (nil, [ + factoryAddress.invoke("stringEntry", [kRequestAmountAssetId(requestId), amountAssetIdStr], []), + factoryAddress.invoke("stringEntry", [kRequestPriceAssetId(requestId), priceAssetIdStr], []), + factoryAddress.invoke("integerEntry", [kRequestLeverage(requestId), leverage], []) + ]) +} + +# called by account script +# additional fee is sent to recipient +# args: [creatorPublicKey] +@Callable(i) +func addAccount(callerPublicKey: ByteVector, args: List[String]) = { + strict ckecks = [ + !shutdown || throwErr("not allowed"), + i.caller.mustFactory() + ] + let creatorPublicKey = args[0].fromBase58String() + let accountPublicKey = callerPublicKey + let accountAddress = addressFromPublicKey(callerPublicKey) + let creatorAddress = addressFromPublicKey(creatorPublicKey) + + strict checks = [ + factoryAddress.getBinary(kAccountCreatorPublicKey(accountAddress)) == unit || throwErr("account is already exists"), + match scriptHash(accountAddress) { + case b: ByteVector => b == blake2b256_32Kb(accountScript()) + case _ => false + } || throwErr("invalid script") + ] + + let (actions, factoryActions) = if (requestsQueue().size() == 0) then { # if the requests queue is empty + # then add account to accounts queue + (throwErr("requests queue is empty"), nil) + } else { # if the requests queue is not empty + # then fulfill next request + let requestId = requestsQueue().take(queueItemSize) + let leverage = factoryAddress.getInteger(kRequestLeverage(requestId)).valueOrErrorMessage( + wrapErr("request's leverage is undefined") + ) + ( + [], + [ + factoryAddress.invoke("integerEntry", [kRequestStatus(requestId), REQUEST_STATUS_READY], []), + factoryAddress.invoke("binaryEntry", [kRequestIdToAccountPublicKey(requestId), accountPublicKey], []), + factoryAddress.invoke("binaryEntry", [kRequestsQueue(), requestsQueue().drop(queueItemSize)], []), + factoryAddress.invoke("stringEntry", [kAccountAddressToRequestId(accountAddress), requestId.toBase58String()], []), + factoryAddress.invoke("transferWaves", [creatorAddress.bytes, rewardAmount()], []), + factoryAddress.invoke("integerEntry", [kLeverage(accountAddress), leverage], []) + ] + ) + } + + (actions, factoryActions ++ [ + factoryAddress.invoke("binaryEntry", [kAccountCreatorPublicKey(accountAddress), creatorPublicKey], []) + ]) +} + +# TODO: validate accounts relations. (!) +# TODO: validate asset id +# TODO: ensure amount is positive +# args: [accountAddress, amount, assetId] +@Callable(i) +func withdraw(callerPublicKey: ByteVector, args: List[String]) = { + strict checks = [ + !shutdown || throwErr("not allowed"), + i.caller.mustFactory() + ] + let userAddress = addressFromPublicKey(callerPublicKey) + let accountAddress = addressFromString(args[0]).valueOrErrorMessage( + wrapErr("invalid account address") + ) + let amount = parseInt(args[1]).valueOrErrorMessage( + wrapErr("invalid amount") + ) + let assetId = parseAssetId(args[2]) + + let invocations = [ + accountAddress.invoke("transferAsset", [userAddress.bytes, amount, assetId], []) + ] + + (nil, invocations) +} + +# Deposit USDT +# args: [accountAddress] +@Callable(i) +func deposit(callerPublicKey: ByteVector, args: List[String]) = { + let (actions, factoryActions) = depositINTERNAL(callerPublicKey, args, i) + + (actions, factoryActions) +} + +# args: [accountAddress, assetId, amount] +@Callable(i) +func borrow(callerPublicKey: ByteVector, args: List[String]) = { + let (actions, factoryActions) = borrowINTERNAL(callerPublicKey, args, i) + + (actions, factoryActions) +} + +# args: [accountAddress, assetId, amount] +@Callable(i) +func depositAndBorrow(callerPublicKey: ByteVector, args: List[String]) = { + let accountAddress = args[0] + let (depositActions, depositFactoryActions) = depositINTERNAL(callerPublicKey, [accountAddress], i) + let (borrowActions, borrowFactoryActions) = borrowINTERNAL(callerPublicKey, args, i) + + (depositActions ++ borrowActions, depositFactoryActions ++ borrowFactoryActions) +} + +# TODO: not implemented +@Callable(i) +func repay(callerPublicKey: ByteVector, args: List[String]) = { + strict ckecks = [ + !shutdown || throwErr("not allowed"), + i.caller.mustFactory() + ] + + (nil, unit) +} + +# args: [amountAssetId, priceAssetId, allow] +@Callable(i) +func setPairAllowance(callerPublicKey: ByteVector, args: List[String]) = { + strict ckecks = [ + !shutdown || throwErr("not allowed"), + i.caller.mustFactory(), + callerPublicKey.mustAdmin() + ] + let amountAssetIdStr = args[0] + let priceAssetIdStr = args[1] + let allowStr = args[2] + let amountAssetId = parseAssetId(amountAssetIdStr) + let priceAssetId = parseAssetId(priceAssetIdStr) + let allow = allowStr == "true" + + let invocations = [ + factoryAddress.invoke("booleanEntry", [kPairAllowed(amountAssetId, priceAssetId), allow], []) + ] + + (nil, invocations) +} + +# args: [assetId] +@Callable(i) +func addSyntheticAsset(callerPublicKey: ByteVector, args: List[String]) = { + let baseAssetId = parseAssetId(args[0]) + let syntheticAssetId = parseAssetId(args[1]) + + strict ckecks = [ + !shutdown || throwErr("not allowed"), + i.caller.mustFactory(), + callerPublicKey.mustAdmin(), + factoryAddress.getString(kSyntheticAssetId(baseAssetId)) == unit || throwErr( + "invalid base asset" + ), + factoryAddress.getString(kBaseAssetId(syntheticAssetId)) == unit || throwErr( + "invalid synthetic asset" + ) + ] + + let invocations = [ + factoryAddress.invoke("stringEntry", [kSyntheticAssetId(baseAssetId), assetIdToString(syntheticAssetId)], []), + factoryAddress.invoke("stringEntry", [kBaseAssetId(syntheticAssetId), assetIdToString(baseAssetId)], []) + ] + + (nil, invocations) +} + +@Callable(i) +func doShutdown(callerPublicKey: ByteVector, args: List[String]) = { + strict checks = [ + i.caller.mustFactory(), + callerPublicKey.mustAdmin() + ] + + let invocations = [ + factoryAddress.invoke("booleanEntry", [kShutdown, true], []) + ] + + (nil, invocations) +} + +# args: [accountAddress] +@Callable(i) +func getAccountInfoREADONLY(callerPublicKey: ByteVector, args: List[String]) = { + strict checks = [ + i.caller.mustFactory() + ] + + let accountAddress = addressFromString(args[0]).valueOrErrorMessage( + wrapErr("invalid account address") + ) + + let data = getAccountInfoInternal(accountAddress) + + (nil, data) +} + +# args: [userAddress] +@Callable(i) +func getUserInfoREADONLY(callerPublicKey: ByteVector, args: List[String]) = { + strict checks = [ + i.caller.mustFactory() + ] + + let userAddress = addressFromString(args[0]).valueOrErrorMessage( + wrapErr("invalid user address") + ) + + let userRequests = factoryAddress.getBinary(kRequestsByOwner(userAddress)).valueOrElse(base58'') + let userRequestsNumber = userRequests.size() / queueItemSize + + func getAccountsData(acc: (List[ByteVector], ByteVector, Int), count: Int) = { + let (accDataList, rawBytes, maxSize) = acc + + if (count < maxSize) then { + let requestId = rawBytes.take(queueItemSize) + let newRawBytes = rawBytes.drop(queueItemSize) + let accountPublicKey = factoryAddress.getBinary(kRequestIdToAccountPublicKey(requestId)).valueOrErrorMessage("kRequestIdToAccountPublicKey not found") + let accountAddress = addressFromPublicKey(accountPublicKey) + let data = getAccountInfoInternal(accountAddress) + + (accDataList :+ data, newRawBytes, maxSize) + } else { + acc + } + } + + let (accDataList, _a, _b) = FOLD<20>(INDEX_LIST, ([], userRequests, userRequestsNumber), getAccountsData) + + (nil, accDataList) +} + +@Callable(i) +func getPairSettingsInfoREADONLY(callerPublicKey: ByteVector, args: List[String]) = { + let amountAsset = parseAssetId(args[0]) + let priceAsset = parseAssetId(args[1]) + + let pricesList = getPairPricesList(amountAsset, priceAsset) + + func getPairSettings(acc: List[(Int, Int, Int, Int)], pricesString: String) = { + let settingsKey = kPairSettingsKey(amountAsset, priceAsset, pricesString) + let settingsStringValue = factoryAddress.getStringValue(settingsKey) + let sList = settingsStringValue.split(separator) + + let price = pricesString.parseInt() + let maxLeverage = sList[1].parseInt() + let initialMargin = sList[2].parseInt() + let maintenanceMargin = sList[3].parseInt() + + acc :+ (price, maxLeverage, initialMargin, maintenanceMargin) + } + + let data = FOLD<20>(pricesList, [], getPairSettings) + + (nil, data) +} + +# args: [amountAssetId, priceAssetId, price, maxLeverage, initialMargin, maintenanceMargin] +@Callable(i) +func editPairSettings(callerPublicKey: ByteVector, args: List[String]) = { + strict checks = [ + i.caller.mustFactory(), + callerPublicKey.mustAdmin() + ] + + let amountAsset = parseAssetId(args[0]) + let priceAsset = parseAssetId(args[1]) + let price = args[2] + let maxLeverage = args[3] + let initialMargin = args[4] + let maintenanceMargin = args[5] + + let pairPricesListKey = kPairPricesListKey(amountAsset, priceAsset) + let settingsKey = kPairSettingsKey(amountAsset, priceAsset, price) + let pricesList = getPairPricesList(amountAsset, priceAsset) + + let newList = match(pricesList.indexOf(price)) { + case ind:Int => pricesList + case _ => pricesList :+ price + } + + strict check2 = [ + price.parseInt().valueOrErrorMessage(wrapErr("price value is not an Int")), + maxLeverage.valueOrErrorMessage(wrapErr("maxLeverage value is not an Int")), + initialMargin.valueOrErrorMessage(wrapErr("initialMargin value is not an Int")), + maintenanceMargin.valueOrErrorMessage(wrapErr("maintenanceMargin value is not an Int")), + newList.size() <= 20 || throwErr("exceeded max prices list size (20)") + ] + + let pairSettingValue = [ + "%s%s%s", + maxLeverage, + initialMargin, + maintenanceMargin + ].makeString(separator) + + + let invocations = [ + factoryAddress.invoke("stringEntry", [kPairPricesListKey(amountAsset, priceAsset), newList.makeString(separator)], []), + factoryAddress.invoke("stringEntry", [kPairSettingsKey(amountAsset, priceAsset, price), pairSettingValue], []) + ] + + (nil, invocations) +} + +# args: [amountAssetId, priceAssetId, price] +@Callable(i) +func deletePairSettings(callerPublicKey: ByteVector, args: List[String]) = { + strict checks = [ + i.caller.mustFactory(), + callerPublicKey.mustAdmin() + ] + + let amountAsset = parseAssetId(args[0]) + let priceAsset = parseAssetId(args[1]) + let price = args[2] + + let settingsKey = kPairSettingsKey(amountAsset, priceAsset, price) + let pairPricesListKey = kPairPricesListKey(amountAsset, priceAsset) + let pricesList = getPairPricesList(amountAsset, priceAsset) + + let newList = match(pricesList.indexOf(price)) { + case ind:Int => pricesList.removeByIndex(ind) + case _ => pricesList + } + + let invocations = [ + factoryAddress.invoke("deleteEntry", [settingsKey], []), + + if (newList.size() == 0) then { + factoryAddress.invoke("deleteEntry", [kPairPricesListKey(amountAsset, priceAsset)], []) + } else { + factoryAddress.invoke("stringEntry", [kPairPricesListKey(amountAsset, priceAsset), newList.makeString(separator)], []) + } + ] + + (nil, invocations) +} + + +@Verifier(tx) +func verify() = { + if (factoryAddressOption.isDefined() && factoryAddress.getString(kMultisig).isDefined()) then { + match factoryAddress.getString(kMultisig) { + case multisig: String => { + let statusKey = kStatus(this.toString(), tx.id.toBase58String()) + let status = multisig.addressFromStringValue().getBoolean(statusKey).valueOrElse(false) + + status + } + case _ => false + } + } else { + sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) + } +} diff --git a/ride/futures_factory.ride b/ride/futures_factory.ride new file mode 100644 index 00000000..c19351ae --- /dev/null +++ b/ride/futures_factory.ride @@ -0,0 +1,179 @@ +{-# STDLIB_VERSION 7 #-} +{-# CONTENT_TYPE DAPP #-} +{-# SCRIPT_TYPE ACCOUNT #-} + +let separator = "__" +let chainId = this.bytes.drop(1).take(1) +let chainIdW = base16'57' +let contractFilename = "futures_factory.ride" + +let kMultisig = "%s__multisig" +func kStatus(dapp: String, txId: String) = ["%s__status", dapp, txId].makeString(separator) + +let kShutdown = "%s__shutdown" + +let shutdown = getBoolean(kShutdown).valueOrElse(false) + +func validateAddress(address: String) = { + addressFromString(address).isDefined() +} + +func wrapErr(s: String) = { + contractFilename + ": " + s +} + +func throwErr(s: String) = { + throw(wrapErr(s)) +} + +let kMatcherPublicKey = "%s__matcherPublicKey" +let kCalculatorAddress = "%s__calculatorAddress" +let calculatorAddressOption = match this.getString(kCalculatorAddress) { + case s: String => s.addressFromString() + case _: Unit => unit +} +let calculatorAddress = calculatorAddressOption.valueOrErrorMessage(wrapErr("invalid calculator address")) + +let kAccountScript = "%s__accountScript" +let kRewardAmount = "%s__rewardAmount" + +func mustAddress(i: Invocation, address: Address) = { + i.caller == address || throwErr("permission denied") +} + +func mustThis(i: Invocation) = { + mustAddress(i, this) +} + +func mustCalculator(i: Invocation) = { + mustAddress(i, calculatorAddress) +} + +let wavesString = "WAVES" +let queueItemSize = 32 + +func parseAssetId(input: String) = { + if (input == wavesString) then unit else input.fromBase58String() +} + +func assetIdToString(input: ByteVector|Unit) = { + if (input == unit) then wavesString else input.value().toBase58String() +} + +@Callable(i) +func init( + calculatorAddressStr: String, + matcherPublicKeyStr: String, + accountScript: ByteVector, + rewardAmount: Int +) = { + strict checkCaller = i.mustThis() + + ([ + StringEntry(kCalculatorAddress, calculatorAddressStr), + StringEntry(kMatcherPublicKey, matcherPublicKeyStr), + BinaryEntry(kAccountScript, accountScript), + IntegerEntry(kRewardAmount, rewardAmount) + ], unit) +} + +@Callable(i) +func stringEntry(key: String, val: String) = + if (!shutdown && i.mustCalculator()) then ([StringEntry(key, val)], key) else throwErr("not allowed") + +@Callable(i) +func integerEntry(key: String, val: Int) = + if (!shutdown && i.mustCalculator()) then ([IntegerEntry(key, val)], key) else throwErr("not allowed") + +@Callable(i) +func booleanEntry(key: String, val: Boolean) = + if (!shutdown && i.mustCalculator()) then ([BooleanEntry(key, val)], key) else throwErr("not allowed") + +@Callable(i) +func binaryEntry(key: String, val: ByteVector) = + if (!shutdown && i.mustCalculator()) then ([BinaryEntry(key, val)], key) else throwErr("not allowed") + +@Callable(i) +func deleteEntry(key: String) = + if (!shutdown && i.mustCalculator()) then ([DeleteEntry(key)], key) else throwErr("not allowed") + +@Callable(i) +func reissue(assetId: ByteVector, amount: Int, reissuable: Boolean) = + if (!shutdown && i.mustCalculator()) then ([Reissue(assetId, amount, reissuable)], amount) else throwErr("not allowed") + +@Callable(i) +func burn(assetId: ByteVector, amount: Int) = + if (!shutdown && i.mustCalculator()) then ([Burn(assetId, amount)], amount) else throwErr("not allowed") + +@Callable(i) +func transferAsset(recipientBytes: ByteVector, amount: Int, assetId: ByteVector) = + if (!shutdown && i.mustCalculator()) then ([ScriptTransfer(Address(recipientBytes), amount, assetId)], amount) else throwErr("not allowed") + +@Callable(i) +func transferAssets(recipientBytes: ByteVector, assetsList: List[ByteVector], amountsList: List[Int]) = { + if (!shutdown && i.mustCalculator()) then { + func addNewTransfer(acc: (List[ScriptTransfer], Int), nextAssetId: ByteVector) = { + let (transfers, j) = acc + + let newTransfer = ScriptTransfer( + Address(recipientBytes), + amountsList[j], + assetsList[j] + ) + let updatedTransfers = transfers :+ newTransfer + (updatedTransfers, j + 1) + } + + let (assetsTransfers, _lastIndex) = FOLD<10>(assetsList, ([], 0), addNewTransfer) + (assetsTransfers, unit) + } else { + throwErr("not allowed") + } +} + +@Callable(i) +func transferWaves(recipientBytes: ByteVector, amount: Int) = + if (!shutdown && i.mustCalculator()) then ([ScriptTransfer(Address(recipientBytes), amount, unit)], amount) else throwErr("not allowed") + +@Callable(i) +func call(function: String, args: List[String]) = { + # To allow usage with node script evaluation (without payments) + # there is a bug(?) when function is called like expression + # Invocation structure doesn't contain "payments" key for some reason + let cleanPayments = if (i.callerPublicKey == base58'11111111111111111111111111111111') then [] else i.payments + let result = calculatorAddress.reentrantInvoke(function, [i.callerPublicKey, args], cleanPayments) + + (nil, result) +} + +@Callable(i) +func setMultisig(multisig: String) = { + strict checks = [ + i.mustThis(), + validateAddress(multisig) || throwErr("setMultisig: invalid multisig address"), + multisig != this.toString() || throwErr("setMultisig: factory address cannot be multisig address") + ] + + ([ + StringEntry(kMultisig, multisig) + ], unit) +} + +@Verifier(tx) +func verify() = { + let signCheck = sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) + + match this.getString(kMultisig) { + case multisig: String => { + if (multisig != this.toString() && validateAddress(multisig)) then { + let statusKey = kStatus(this.toString(), tx.id.toBase58String()) + let status = multisig.addressFromStringValue().getBoolean(statusKey).valueOrElse(false) + + status + } else { + signCheck + } + } + case _ => signCheck + } +} diff --git a/ride/multisig.ride b/ride/multisig.ride new file mode 100644 index 00000000..51b1eb3f --- /dev/null +++ b/ride/multisig.ride @@ -0,0 +1,196 @@ +{-# STDLIB_VERSION 7 #-} +{-# CONTENT_TYPE DAPP #-} +{-# SCRIPT_TYPE ACCOUNT #-} + +let separator = "__" +let contractFilename = "multisig.ride" + +let publicKeySize = 32 +let txIdSize = 32 +let maxOwners = 10 + +let kMultisig = "%s__multisig" +let kPublicKeys = "%s__publicKeys" +let kQuorum = "%s__quorum" + +func kConfirm(address: String, txId: String) = ["%s__confirm", address, txId].makeString(separator) +func kStatus(address: String, txId: String) = ["%s__status", address, txId].makeString(separator) + +func wrapErr(s: String) = { + contractFilename + ": " + s +} + +func throwErr(s: String) = { + throw(wrapErr(s)) +} + +func validateAddress(address: String) = { + addressFromString(address).isDefined() +} + +func validatePublicKey(publicKey: String) = { + fromBase58String(publicKey).size() == publicKeySize +} + +func validateOwner(owners: List[String], pk: String) = { + strict checks = [ + validatePublicKey(pk) || throwErr("invalid owner public key"), + owners.indexOf(pk) == owners.lastIndexOf(pk) || throwErr("must not contain duplicates") + ] + + owners +} + +@Callable(i) +func init(owners: List[String], quorum: Int) = { + strict checks = [ + i.caller == this || throwErr("init: not allowed"), + !getString(kMultisig).isDefined() || throwErr("init: already initialized"), + (owners.size() > 0 && owners.size() <= maxOwners) || throwErr("init: invalid owners"), + (quorum > 0 && quorum <= owners.size()) || throwErr("init: invalid quorum"), + # maxOwners = 10 + FOLD<10>(owners, owners, validateOwner) + ] + + ([ + StringEntry(kMultisig, this.toString()), + StringEntry(kPublicKeys, makeString(owners, separator)), + IntegerEntry(kQuorum, quorum) + ], unit) +} + +@Callable(i) +func addOwner(publicKey: String) = { + let publicKeysList = getStringValue(kPublicKeys).split(separator) + + strict checks = [ + i.caller == this || throwErr("addOwner: not allowed"), + validatePublicKey(publicKey) || throwErr("addOwner: invalid public key"), + !containsElement(publicKeysList, publicKey) || throwErr("addOwner: public key already added"), + publicKeysList.size() < maxOwners || throwErr("addOwner: too many owners") + ] + + let publicKeysListUpdated = publicKeysList :+ publicKey + let publicKeysUpdated = [ + StringEntry(kPublicKeys, publicKeysListUpdated.makeString(separator)) + ] + + (publicKeysUpdated, unit) +} + +@Callable(i) +func removeOwner(publicKey: String) = { + let quorum = getIntegerValue(kQuorum) + let publicKeys = getStringValue(kPublicKeys) + let publicKeysList = publicKeys.split(separator) + + strict checks = [ + i.caller == this || throwErr("removeOwner: not allowed"), + validatePublicKey(publicKey) || throwErr("removeOwner: invalid public key"), + publicKeysList.size() > 1 || throwErr("removeOwner: too few owners") + ] + + let index = indexOf(publicKeysList, publicKey).valueOrErrorMessage( + wrapErr("removeOwner: no such owner") + ) + let publicKeysListUpdated = removeByIndex(publicKeysList, index) + let publicKeysUpdated = [ + StringEntry(kPublicKeys, makeString(publicKeysListUpdated, separator)) + ] + + let quorumUpdated = if quorum > publicKeysListUpdated.size() then [ + IntegerEntry(kQuorum, publicKeysListUpdated.size()) + ] else [] + + (publicKeysUpdated ++ quorumUpdated, unit) +} + +@Callable(i) +func setQuorum(quorum: Int) = { + let publicKeys = getStringValue(kPublicKeys) + let publicKeysList = publicKeys.split(separator) + + strict checks = [ + i.caller == this || throwErr("setQuorum: not allowed"), + (quorum > 0 && quorum <= publicKeysList.size()) || throwErr("setQuorum: invalid quorum") + ] + + ([ + IntegerEntry(kQuorum, quorum) + ], unit) +} + +@Callable(i) +func confirmTransaction(address: String, txId: String) = { + let callerPublicKey = toBase58String(i.callerPublicKey) + let quorum = getIntegerValue(kQuorum) + let publicKeys = getStringValue(kPublicKeys) + let publicKeysList = publicKeys.split(separator) + let confirmationsKey = kConfirm(address, txId) + let confirmations = getString(confirmationsKey).valueOrElse("") + let statusKey = kStatus(address, txId) + + strict checks = [ + containsElement(publicKeysList, callerPublicKey) || throwErr("confirmTransaction: only admin"), + fromBase58String(txId).size() == txIdSize ||throwErr("confirmTransaction: invalid txId"), + validateAddress(address) || throwErr("confirmTransaction: invalid address"), + !contains(confirmations, callerPublicKey) || throwErr("confirmTransaction: already confirmed") + ] + + let (confirmationsCount, confirmationsUpdated) = if confirmations == "" then ( + 1, + callerPublicKey + ) else ( + 1 + confirmations.split(separator).size(), + confirmations + separator + callerPublicKey + ) + + ([ + StringEntry(confirmationsKey, confirmationsUpdated), + BooleanEntry(statusKey, confirmationsCount >= quorum) + ], unit) +} + +@Callable(i) +func revokeConfirmation(address: String, txId: String) = { + let callerPublicKey = toBase58String(i.callerPublicKey) + let quorum = getIntegerValue(kQuorum) + let publicKeys = getStringValue(kPublicKeys) + let publicKeysList = publicKeys.split(separator) + let confirmationsKey = kConfirm(address, txId) + let confirmations = getString(confirmationsKey).valueOrElse("") + let confirmationsList = confirmations.split(separator) + let statusKey = kStatus(address, txId) + let status = getBoolean(statusKey).valueOrElse(false) + + strict checks = [ + containsElement(publicKeysList, callerPublicKey) || throwErr("revokeConfirmation: only admin"), + fromBase58String(txId).size() == txIdSize || throwErr("revokeConfirmation: invalid txId"), + validateAddress(address) || throwErr("revokeConfirmation: invalid address"), + contains(confirmations, callerPublicKey) || throwErr("revokeConfirmation: not confirmed"), + !status || throwErr("revokeConfirmation: quorum already reached") + ] + + let confirmationsListUpdated = confirmationsList.removeByIndex( + confirmationsList.indexOf(callerPublicKey).value() + ) + let confirmationsCount = confirmationsListUpdated.size() + + ([ + StringEntry(confirmationsKey, makeString(confirmationsListUpdated, separator)), + BooleanEntry(statusKey, confirmationsCount >= quorum) + ], unit) +} + +@Verifier(tx) +func verify() = { + match getString(kMultisig) { + case multisig: String => { + let statusKey = kStatus(this.toString(), tx.id.toBase58String()) + let status = multisig.addressFromStringValue().getBoolean(statusKey).valueOrElse(false) + + status + } + case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) + } +} diff --git a/test/components/futures/_setup.mjs b/test/components/futures/_setup.mjs new file mode 100644 index 00000000..6c15dea1 --- /dev/null +++ b/test/components/futures/_setup.mjs @@ -0,0 +1,145 @@ +import wc from '@waves/ts-lib-crypto'; +import { + massTransfer, + invokeScript, + issue, +} from '@waves/waves-transactions'; +import { format } from 'path'; +import { table, getBorderCharacters } from 'table'; + +import { + chainId, broadcastAndWait, baseSeed, +} from '../../utils/api.mjs'; + +import { + compileScriptFromFile, + setScriptFromFile, +} from '../../utils/utils.mjs'; + +const nonceLength = 3; +const ridePath = '../ride'; +const factoryPath = format({ dir: ridePath, base: 'futures_factory.ride' }); +const calculatorPath = format({ dir: ridePath, base: 'futures_calculator.ride' }); +const accountPath = format({ dir: ridePath, base: 'futures_account.ride' }); +const multisigPath = format({ dir: ridePath, base: 'multisig.ride' }); + +export const setup = async ({ + rewardAmount = 1000, +} = {}) => { + const nonce = wc.random(nonceLength, 'Buffer').toString('hex'); + const names = [ + 'factory', + 'calculator', + 'matcher', + 'creator', + 'multisig', + 'user1', + 'user2', + 'account1', + 'account2', + 'admin1', + ]; + const accounts = Object.fromEntries(names.map((item) => { + const seed = `${item}#${nonce}`; + return [item, { seed, address: wc.address(seed, chainId), publicKey: wc.publicKey(seed) }]; + })); + const accountsInfo = Object.entries(accounts) + .map(([name, { address, publicKey }]) => [name, address, publicKey]); + console.log(table(accountsInfo, { + border: getBorderCharacters('norc'), + drawHorizontalLine: (index, size) => index === 0 || index === 1 || index === size, + header: { content: `pid: ${process.pid}, nonce: ${nonce}` }, + })); + const amount = 10e8; + const massTransferTx = massTransfer({ + transfers: Object.values(accounts).map(({ address }) => ({ recipient: address, amount })), + chainId, + }, baseSeed); + await broadcastAndWait(massTransferTx); + + const libraries = {}; + + const [ + { id: assetId1 }, + { id: assetId2 }, + { id: assetId3 }, + ] = await Promise.all([ + broadcastAndWait(issue({ + name: 'assetId1', + description: '', + quantity: 1e6 * 1e8, + decimals: 8, + reissuable: true, + chainId, + }, baseSeed)), + broadcastAndWait(issue({ + name: 'assetId2', + description: '', + quantity: 1e6 * 1e8, + decimals: 8, + reissuable: true, + chainId, + }, baseSeed)), + broadcastAndWait(issue({ + name: 'assetId3', + description: '', + quantity: 1e6 * 1e8, + decimals: 8, + reissuable: true, + chainId, + }, baseSeed)), + setScriptFromFile( + factoryPath, + accounts.factory.seed, + null, + libraries, + ), + setScriptFromFile( + calculatorPath, + accounts.calculator.seed, + null, + libraries, + ), + setScriptFromFile( + multisigPath, + accounts.multisig.seed, + null, + libraries, + ), + ]); + + const compact = true; + const { + base64: accountScript, + } = await compileScriptFromFile(accountPath, null, libraries, compact); + await broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'init', + args: [ + { type: 'string', value: accounts.calculator.address }, + { type: 'string', value: accounts.matcher.publicKey }, + { type: 'binary', value: accountScript }, + { type: 'integer', value: rewardAmount }, + ], + }, + additionalFee: 4e5, + chainId, + }, accounts.factory.seed)); + + await broadcastAndWait(invokeScript({ + dApp: accounts.calculator.address, + call: { + function: 'init', + args: [ + { type: 'string', value: accounts.factory.address }, + ], + }, + additionalFee: 4e5, + chainId, + }, accounts.calculator.seed)); + + return { + accounts, rewardAmount, assetId1, assetId2, assetId3, + }; +}; diff --git a/test/components/futures/account_permissions.spec.mjs b/test/components/futures/account_permissions.spec.mjs new file mode 100644 index 00000000..ad5e08cd --- /dev/null +++ b/test/components/futures/account_permissions.spec.mjs @@ -0,0 +1,286 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { + data, invokeScript, issue, setScript, +} from '@waves/waves-transactions'; +import { + base58Decode, base64Encode, +} from '@waves/ts-lib-crypto'; +import { + chainId, broadcastAndWait, api, broadcast, +} from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] futures: account permissions`, () => { + let accounts; + let assetIdOwned; + let rewardAmount; + let assetId1; + let assetId2; + let validScript; + + before(async () => { + ({ + accounts, rewardAmount, assetId1, assetId2, + } = await setup()); + + ({ id: assetIdOwned } = await broadcastAndWait(issue({ + name: 'ASSET', + description: '', + quantity: 1e6 * 1e8, + decimals: 8, + reissuable: true, + chainId, + }, accounts.account1.seed))); + + const kAccountScript = '%s__accountScript'; + validScript = await api.addresses.fetchDataKey( + accounts.factory.address, + kAccountScript, + ).then(({ value }) => value); + + await broadcastAndWait(data({ + data: [ + { key: `%s%s%s__${assetId1}__${assetId2}__pairAllowed`, type: 'boolean', value: true }, + ], + additionalFee: 4e5, + chainId, + }, accounts.factory.seed)); + + await broadcastAndWait(setScript({ + script: validScript, + chainId, + }, accounts.account1.seed)); + + await broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'requestAccount' }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + { type: 'string', value: '2' }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount }, + ], + chainId, + }, accounts.user1.seed)); + + await broadcastAndWait(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'init', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}` }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}` }, + ], + }, + payment: [], + chainId, + additionalFee: 4e5, + }, accounts.account1.seed)); + }); + + it('stringEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'stringEntry', + args: [ + { type: 'string', value: 'test' }, + { type: 'string', value: 'test' }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('integerEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'integerEntry', + args: [ + { type: 'string', value: 'test' }, + { type: 'integer', value: 0 }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('booleanEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'booleanEntry', + args: [ + { type: 'string', value: 'test' }, + { type: 'boolean', value: true }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('binaryEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'binaryEntry', + args: [ + { type: 'string', value: 'test' }, + { type: 'binary', value: 'base64:' }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('deleteEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'deleteEntry', + args: [ + { type: 'string', value: 'test' }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('reissue', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'reissue', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(assetIdOwned))}` }, + { type: 'integer', value: 0 }, + { type: 'boolean', value: true }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('burn', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'burn', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(assetIdOwned))}` }, + { type: 'integer', value: 0 }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('transferAsset', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'transferAsset', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.calculator.address))}` }, + { type: 'integer', value: 1 }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(assetIdOwned))}` }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('transferWaves', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'transferWaves', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.calculator.address))}` }, + { type: 'integer', value: 0 }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('init', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'init', + args: [ + { type: 'binary', value: 'base64:' }, + { type: 'binary', value: 'base64:' }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.account1.seed)).to.be.rejectedWith('Transaction is not allowed by account-script'); + }); +}); diff --git a/test/components/futures/calculator_permissions.spec.mjs b/test/components/futures/calculator_permissions.spec.mjs new file mode 100644 index 00000000..de9025ea --- /dev/null +++ b/test/components/futures/calculator_permissions.spec.mjs @@ -0,0 +1,164 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import { data, invokeScript } from '@waves/waves-transactions'; +import { + chainId, broadcast, broadcastAndWait, +} from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; + +chai.use(chaiAsPromised); +const { expect } = chai; + +describe(`[${process.pid}] futures: calculator permissions`, () => { + let accounts; + let rewardAmount; + let assetId1; + let assetId2; + + before(async () => { + ({ + accounts, rewardAmount, assetId1, assetId2, + } = await setup()); + + await broadcastAndWait(data({ + data: [ + { key: `%s%s%s__${assetId1}__${assetId2}__pairAllowed`, type: 'boolean', value: true }, + ], + additionalFee: 4e5, + chainId, + }, accounts.factory.seed)).catch(({ message }) => { throw new Error(message); }); + }); + + it('init', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.calculator.address, + call: { + function: 'init', + args: [ + { type: 'string', value: '' }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + }); + + it('request account', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.calculator.address, + call: { + function: 'requestAccount', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}` }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount }, + ], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + }); + + it('add account', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.calculator.address, + call: { + function: 'addAccount', + args: [ + { type: 'binary', value: 'base64:' }, + { type: 'list', value: [] }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + }); + + it('deposit', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.calculator.address, + call: { + function: 'deposit', + args: [ + { type: 'binary', value: 'base64:' }, + { type: 'list', value: [] }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + }); + + it('withdraw', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.calculator.address, + call: { + function: 'withdraw', + args: [ + { type: 'binary', value: 'base64:' }, + { type: 'list', value: [] }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + }); + + it('set pair allowance', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.calculator.address, + call: { + function: 'setPairAllowance', + args: [ + { type: 'binary', value: 'base64:' }, + { type: 'list', value: [] }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + }); + + it('do shutdown', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.calculator.address, + call: { + function: 'doShutdown', + args: [ + { type: 'binary', value: 'base64:' }, + { type: 'list', value: [] }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + }); +}); diff --git a/test/components/futures/factory_add_account.spec.mjs b/test/components/futures/factory_add_account.spec.mjs new file mode 100644 index 00000000..5d364c2f --- /dev/null +++ b/test/components/futures/factory_add_account.spec.mjs @@ -0,0 +1,183 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { data, invokeScript, setScript } from '@waves/waves-transactions'; +import { + base58Decode, base64Encode, +} from '@waves/ts-lib-crypto'; +import { + chainId, broadcastAndWait, api, +} from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; +import { compileScript } from '../../utils/utils.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] futures: add account`, () => { + let accounts; + let assetId1; + let assetId2; + let validScript; + let rewardAmount; + + before(async () => { + ({ + accounts, assetId1, assetId2, rewardAmount, + } = await setup()); + await broadcastAndWait(data({ + data: [ + { key: `%s%s%s__${assetId1}__${assetId2}__pairAllowed`, type: 'boolean', value: true }, + ], + chainId, + additionalFee: 400000, + }, accounts.factory.seed)).catch(({ message }) => { throw new Error(message); }); + + const kAccountScript = '%s__accountScript'; + validScript = await api.addresses.fetchDataKey( + accounts.factory.address, + kAccountScript, + ).then(({ value }) => value); + }); + + it('no script', async () => { + const { publicKey } = accounts.creator; + + return expect(broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'addAccount' }, + { + type: 'list', + value: [ + { type: 'string', value: publicKey }, + ], + }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, accounts.account1.seed))).to.be.rejectedWith('invalid script'); + }); + + it('invalid script', async () => { + const script = `{-# STDLIB_VERSION 7 #-} + {-# CONTENT_TYPE DAPP #-} + {-# SCRIPT_TYPE ACCOUNT #-} + `; + + await broadcastAndWait(setScript({ + script: `base64:${compileScript(script).base64}`, + chainId, + }, accounts.account1.seed)); + + const { publicKey } = accounts.creator; + + return expect(broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'addAccount' }, + { + type: 'list', + value: [ + { type: 'string', value: publicKey }, + ], + }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, accounts.account1.seed))).to.be.rejectedWith('invalid script'); + }); + + it('requests queue is empty', async () => { + await broadcastAndWait(setScript({ + script: validScript, + chainId, + }, accounts.account1.seed)).catch(({ message }) => { throw new Error(message); }); + + return expect(broadcastAndWait(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'init', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}` }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}` }, + ], + }, + payment: [], + chainId, + additionalFee: 4e5, + }, accounts.account1.seed))).to.be.rejectedWith('requests queue is empty'); + }); + + it('successfully init account', async () => { + await broadcastAndWait(setScript({ + script: validScript, + chainId, + }, accounts.account1.seed)).catch(({ message }) => { throw new Error(message); }); + + const requestInvokeTx = invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'requestAccount' }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + { type: 'string', value: '2' }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount }, + ], + chainId, + }, accounts.user1.seed); + + await broadcastAndWait(requestInvokeTx).catch(({ message }) => { throw new Error(message); }); + + await broadcastAndWait(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'init', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}` }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}` }, + ], + }, + payment: [], + chainId, + additionalFee: 4e5, + }, accounts.account1.seed)).catch(({ message }) => { throw new Error(message); }); + + const accountState = await api.addresses.data(accounts.account1.address); + expect(accountState).to.deep.equal([ + { + key: '%s__factoryPublicKey', + type: 'binary', + value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}`, + }, + ]); + + const factoryState = await api.addresses.data(accounts.factory.address); + expect(factoryState).to.deep.include.members([ + { + key: `%s%s__${accounts.account1.address}__creatorPublicKey`, + type: 'binary', + value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}`, + }, + ]); + }); +}); diff --git a/test/components/futures/factory_permissions.spec.mjs b/test/components/futures/factory_permissions.spec.mjs new file mode 100644 index 00000000..0065f382 --- /dev/null +++ b/test/components/futures/factory_permissions.spec.mjs @@ -0,0 +1,245 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import { invokeScript, issue } from '@waves/waves-transactions'; +import { base58Decode, base64Encode } from '@waves/ts-lib-crypto'; +import { + chainId, broadcast, broadcastAndWait, +} from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; + +chai.use(chaiAsPromised); +const { expect } = chai; + +describe(`[${process.pid}] futures: factory permissions`, () => { + let accounts; + let assetId; + + before(async () => { + ({ + accounts, + } = await setup()); + + ({ id: assetId } = await broadcastAndWait(issue({ + name: 'ASSET', + description: '', + quantity: 1e6 * 1e8, + decimals: 8, + reissuable: true, + additionalFee: 4e5, + chainId, + }, accounts.factory.seed))); + }); + + it('stringEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'stringEntry', + args: [ + { type: 'string', value: 'test' }, + { type: 'string', value: 'test' }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await fn(accounts.calculator.seed); + }); + + it('integerEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'integerEntry', + args: [ + { type: 'string', value: 'test' }, + { type: 'integer', value: 0 }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('booleanEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'booleanEntry', + args: [ + { type: 'string', value: 'test' }, + { type: 'boolean', value: true }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('binaryEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'binaryEntry', + args: [ + { type: 'string', value: 'test' }, + { type: 'binary', value: 'base64:' }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('deleteEntry', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'deleteEntry', + args: [ + { type: 'string', value: 'test' }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('reissue', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'reissue', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(assetId))}` }, + { type: 'integer', value: 0 }, + { type: 'boolean', value: true }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('burn', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'burn', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(assetId))}` }, + { type: 'integer', value: 0 }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('transferAsset', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'transferAsset', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.calculator.address))}` }, + { type: 'integer', value: 1 }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(assetId))}` }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('transferAssets', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'transferAssets', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.calculator.address))}` }, + { type: 'list', value: [] }, + { type: 'list', value: [] }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('transferWaves', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'transferWaves', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.calculator.address))}` }, + { type: 'integer', value: 0 }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.calculator.seed)).to.be.fulfilled; + }); + + it('init', async () => { + const fn = async (caller) => broadcast(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'init', + args: [ + { type: 'string', value: '' }, + { type: 'string', value: '' }, + { type: 'binary', value: 'base64:' }, + { type: 'integer', value: 0 }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, caller)); + + await expect(fn(accounts.user1.seed)).to.be.rejectedWith('permission denied'); + await expect(fn(accounts.factory.seed)).to.be.fulfilled; + }); +}); diff --git a/test/components/futures/factory_readonly.spec.mjs b/test/components/futures/factory_readonly.spec.mjs new file mode 100644 index 00000000..a2278455 --- /dev/null +++ b/test/components/futures/factory_readonly.spec.mjs @@ -0,0 +1,161 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { + invokeScript, data, setScript, +} from '@waves/waves-transactions'; +import { + base58Decode, base58Encode, sha256, +} from '@waves/ts-lib-crypto'; +import { + chainId, broadcastAndWait, api, +} from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] futures: factory readonly`, () => { + let accounts; + let assetId1; + let assetId2; + let requestId; + let validScript; + let rewardAmount; + const leverage = 2; + + before(async () => { + ({ + accounts, assetId1, assetId2, rewardAmount, + } = await setup()); + + const kAccountScript = '%s__accountScript'; + validScript = await api.addresses.fetchDataKey( + accounts.factory.address, + kAccountScript, + ).then(({ value }) => value); + + await broadcastAndWait(data({ + data: [ + { key: `%s%s%s__${assetId1}__${assetId2}__pairAllowed`, type: 'boolean', value: true }, + { key: '%s__usdtAssetId', type: 'string', value: assetId2 }, + ], + chainId, + additionalFee: 4e5, + }, accounts.factory.seed)).catch(({ message }) => { throw new Error(message); }); + + await broadcastAndWait(setScript({ + script: validScript, + chainId, + }, accounts.account1.seed)).catch(({ message }) => { throw new Error(message); }); + + const requestInvokeTx = invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'requestAccount' }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + { type: 'string', value: `${leverage}` }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount }, + ], + chainId, + }, accounts.user1.seed); + + await broadcastAndWait(requestInvokeTx).catch(({ message }) => { throw new Error(message); }); + + requestId = base58Encode(sha256([ + ...base58Decode(requestInvokeTx.id), + ])); + + await broadcastAndWait(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'init', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}` }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}` }, + ], + }, + payment: [], + chainId, + additionalFee: 4e5, + }, accounts.account1.seed)).catch(({ message }) => { throw new Error(message); }); + }); + + it('getAccountInfoREADONLY', async () => { + const functionName = 'getAccountInfoREADONLY'; + + const expr = `call(\"${functionName}\", [\"${accounts.account1.address}\"])`; /* eslint-disable-line */ + console.log(expr); + const response = await api.utils.fetchEvaluate( + accounts.factory.address, + expr, + ); + const checkData = response.result.value._2; /* eslint-disable-line */ + + return expect(checkData).to.eql({ + type: 'Tuple', + value: { + _1: { + type: 'String', + value: assetId1, + }, + _2: { + type: 'String', + value: assetId2, + }, + _3: { + type: 'String', + value: accounts.account1.address, + }, + _4: { + type: 'String', + value: accounts.account1.publicKey, + }, + _5: { + type: 'Int', + value: leverage, + }, + _6: { + type: 'String', + value: requestId, + }, + _7: { + type: 'Int', + value: 0, + }, + _8: { + type: 'Int', + value: 0, + }, + _9: { + type: 'Int', + value: 0, + }, + _10: { + type: 'Int', + value: 0, + }, + _11: { + type: 'Int', + value: 0, + }, + _12: { + type: 'Int', + value: 1, + }, + }, + }); + }); +}); diff --git a/test/components/futures/factory_request_account.spec.mjs b/test/components/futures/factory_request_account.spec.mjs new file mode 100644 index 00000000..c42b7502 --- /dev/null +++ b/test/components/futures/factory_request_account.spec.mjs @@ -0,0 +1,269 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { + data, invokeScript, setScript, transfer, +} from '@waves/waves-transactions'; +import { + base58Decode, base64Encode, base58Encode, sha256, +} from '@waves/ts-lib-crypto'; +import { + chainId, broadcastAndWait, baseSeed, api, +} from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] futures: factory request account`, () => { + let accounts; + let rewardAmount; + let assetId1; + let assetId2; + let validScript; + let requestId; + + before(async () => { + ({ + accounts, rewardAmount, assetId1, assetId2, + } = await setup()); + + const kAccountScript = '%s__accountScript'; + validScript = await api.addresses.fetchDataKey( + accounts.factory.address, + kAccountScript, + ).then(({ value }) => value); + }); + + it('1 payment is required', async () => { + const functionName = 'requestAccount'; + + return expect(broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: functionName }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + { type: 'string', value: '2' }, + ], + }, + ], + }, + payment: [], + chainId, + }, accounts.user1.seed))).to.be.rejectedWith('1 payment is required'); + }); + + it('invalid asset', async () => { + await broadcastAndWait(transfer({ + recipient: accounts.user1.address, + assetId: assetId1, + amount: 1e8, + }, baseSeed)); + + const functionName = 'requestAccount'; + + return expect(broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: functionName }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + ], + }, + ], + }, + payment: [ + { assetId: assetId1, amount: rewardAmount }, + ], + chainId, + }, accounts.user1.seed))).to.be.rejectedWith('invalid asset'); + }); + + it('invalid amount', async () => { + const functionName = 'requestAccount'; + + return expect(broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: functionName }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount - 1 }, + ], + chainId, + }, accounts.user1.seed))).to.be.rejectedWith('invalid amount'); + }); + + it('pair is not allowed', async () => { + const functionName = 'requestAccount'; + + return expect(broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: functionName }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount }, + ], + chainId, + }, accounts.user1.seed))).to.be.rejectedWith('pair is not allowed'); + }); + + it('successfully create request', async () => { + await broadcastAndWait(data({ + data: [ + { key: `%s%s%s__${assetId1}__${assetId2}__pairAllowed`, type: 'boolean', value: true }, + ], + chainId, + additionalFee: 400000, + }, accounts.factory.seed)).catch(({ message }) => { throw new Error(message); }); + + const requestInvokeTx = invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'requestAccount' }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + { type: 'string', value: '2' }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount }, + ], + chainId, + }, accounts.user1.seed); + + await broadcastAndWait(requestInvokeTx).catch(({ message }) => { throw new Error(message); }); + + requestId = base58Encode(sha256([ + ...base58Decode(requestInvokeTx.id), + ])); + + const accountStatusEmpty = 0; + + const factoryState = await api.addresses.data(accounts.factory.address); + + const expected = [ + { + key: `%s%s__${requestId}__status`, + type: 'integer', + value: accountStatusEmpty, + }, + { + key: '%s__requestsQueue', + type: 'binary', + value: `base64:${base64Encode(base58Decode(requestId))}`, + }, + { + key: `%s%s__${requestId}__ownerPublicKey`, + type: 'binary', + value: `base64:${base64Encode(base58Decode(accounts.user1.publicKey))}`, + }, + { + key: `%s%s__${requestId}__amountAssetId`, + type: 'string', + value: assetId1, + }, + { + key: `%s%s__${requestId}__priceAssetId`, + type: 'string', + value: assetId2, + }, + ]; + + expect(factoryState).to.containSubset(expected); + }); + + it('add account after request was created', async () => { + await broadcastAndWait(setScript({ + script: validScript, + chainId, + }, accounts.account1.seed)).catch(({ message }) => { throw new Error(message); }); + + await broadcastAndWait(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'init', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}` }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}` }, + ], + }, + payment: [], + chainId, + additionalFee: 4e5, + }, accounts.account1.seed)).catch(({ message }) => { throw new Error(message); }); + + const accountStatusReady = 1; + + const factoryState = await api.addresses.data(accounts.factory.address); + + expect(factoryState).to.containSubset([ + { + key: `%s%s__${accounts.account1.address}__accountAddressToRequestId`, + type: 'string', + value: requestId, + }, + { + key: `%s%s__${accounts.account1.address}__creatorPublicKey`, + type: 'binary', + value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}`, + }, + { + key: `%s%s__${requestId}__status`, + type: 'integer', + value: accountStatusReady, + }, + { + key: `%s%s__${requestId}__requestIdToAccountPublicKey`, + type: 'binary', + value: `base64:${base64Encode(base58Decode(accounts.account1.publicKey))}`, + }, + { + key: '%s__requestsQueue', + type: 'binary', + value: 'base64:', + }, + ]); + // TODO: check factory and creator balances + }); +}); diff --git a/test/components/futures/factory_user_readonly.spec.mjs b/test/components/futures/factory_user_readonly.spec.mjs new file mode 100644 index 00000000..ca01fdba --- /dev/null +++ b/test/components/futures/factory_user_readonly.spec.mjs @@ -0,0 +1,147 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { + invokeScript, data, setScript, +} from '@waves/waves-transactions'; +import { + base58Decode, base64Encode, +} from '@waves/ts-lib-crypto'; +import { + chainId, broadcastAndWait, api, +} from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] futures: factory readonly`, () => { + let accounts; + let assetId1; + let assetId2; + let assetId3; + let validScript; + let rewardAmount; + const leverage = 2; + + before(async () => { + ({ + accounts, rewardAmount, assetId1, assetId2, assetId3, + } = await setup()); + + const kAccountScript = '%s__accountScript'; + validScript = await api.addresses.fetchDataKey( + accounts.factory.address, + kAccountScript, + ).then(({ value }) => value); + + await broadcastAndWait(data({ + data: [ + { key: `%s%s%s__${assetId1}__${assetId2}__pairAllowed`, type: 'boolean', value: true }, + { key: `%s%s%s__${assetId3}__${assetId2}__pairAllowed`, type: 'boolean', value: true }, + { key: '%s__usdtAssetId', type: 'string', value: assetId2 }, + ], + chainId, + additionalFee: 4e5, + }, accounts.factory.seed)).catch(({ message }) => { throw new Error(message); }); + + await broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'requestAccount' }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + { type: 'string', value: `${leverage}` }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount }, + ], + chainId, + }, accounts.user1.seed)).catch(({ message }) => { throw new Error(message); }); + + await broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'requestAccount' }, + { + type: 'list', + value: [ + { type: 'string', value: assetId3 }, + { type: 'string', value: assetId2 }, + { type: 'string', value: `${leverage}` }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount }, + ], + chainId, + }, accounts.user1.seed)).catch(({ message }) => { throw new Error(message); }); + + // Account 1 + await broadcastAndWait(setScript({ + script: validScript, + chainId, + }, accounts.account1.seed)).catch(({ message }) => { throw new Error(message); }); + + await broadcastAndWait(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'init', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}` }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}` }, + ], + }, + payment: [], + chainId, + additionalFee: 4e5, + }, accounts.account1.seed)).catch(({ message }) => { throw new Error(message); }); + + // Account 2 + await broadcastAndWait(setScript({ + script: validScript, + chainId, + }, accounts.account2.seed)).catch(({ message }) => { throw new Error(message); }); + + await broadcastAndWait(invokeScript({ + dApp: accounts.account2.address, + call: { + function: 'init', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}` }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}` }, + ], + }, + payment: [], + chainId, + additionalFee: 4e5, + }, accounts.account2.seed)).catch(({ message }) => { throw new Error(message); }); + }); + + it('getUserInfoREADONLY', async () => { + const functionName = 'getUserInfoREADONLY'; + + const expr = `call(\"${functionName}\", [\"${accounts.user1.address}\"])`; /* eslint-disable-line */ + console.log(expr); + const response = await api.utils.fetchEvaluate( + accounts.factory.address, + expr, + ); + const checkData = response.result.value._2; /* eslint-disable-line */ + + return expect(checkData.value).to.be.lengthOf(2); + }); +}); diff --git a/test/components/futures/shutdown.spec.mjs b/test/components/futures/shutdown.spec.mjs new file mode 100644 index 00000000..7624dbdc --- /dev/null +++ b/test/components/futures/shutdown.spec.mjs @@ -0,0 +1,240 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { + data, invokeScript, setScript, +} from '@waves/waves-transactions'; +import { + base58Decode, base64Encode, +} from '@waves/ts-lib-crypto'; +import { + chainId, broadcastAndWait, api, +} from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; +import { confirmTransaction, init } from '../multisig/contract/multisig.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] futures: shutdown`, () => { + let accounts; + let rewardAmount; + let assetId1; + let assetId2; + let validScript; + + before(async () => { + ({ + accounts, rewardAmount, assetId1, assetId2, + } = await setup()); + + const owners = [ + accounts.admin1.publicKey, + ]; + const quorum = 1; + + await init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + }); + + const kAccountScript = '%s__accountScript'; + validScript = await api.addresses.fetchDataKey( + accounts.factory.address, + kAccountScript, + ).then(({ value }) => value); + + await broadcastAndWait(data({ + data: [ + { key: `%s%s%s__${assetId1}__${assetId2}__pairAllowed`, type: 'boolean', value: true }, + ], + chainId, + additionalFee: 4e5, + }, accounts.factory.seed)); + + await broadcastAndWait(setScript({ + script: validScript, + chainId, + }, accounts.account1.seed)); + + await broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'requestAccount' }, + { + type: 'list', + value: [ + { type: 'string', value: assetId1 }, + { type: 'string', value: assetId2 }, + { type: 'string', value: '2' }, + ], + }, + ], + }, + payment: [ + { assetId: null, amount: rewardAmount }, + ], + chainId, + }, accounts.user1.seed)); + + await broadcastAndWait(invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'init', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.factory.publicKey))}` }, + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.creator.publicKey))}` }, + ], + }, + payment: [], + chainId, + additionalFee: 4e5, + }, accounts.account1.seed)); + + await broadcastAndWait(invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'setMultisig', + args: [ + { type: 'string', value: accounts.multisig.address }, + ], + }, + additionalFee: 4e5, + chainId, + }, accounts.factory.seed)); + }); + + it('doShutdown should throw if not admin', async () => { + const tx = invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'doShutdown' }, + { type: 'list', value: [] }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, accounts.user1.seed); + + await expect(broadcastAndWait(tx)).to.be.rejectedWith('not allowed'); + }); + + it('successfully do shutdown', async () => { + const tx = invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'doShutdown' }, + { type: 'list', value: [] }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, accounts.admin1.seed); + + await expect(broadcastAndWait(tx)).to.be.fulfilled; + }); + + it('transferWaves in account should throw if shutdown', async () => { + const tx = invokeScript({ + dApp: accounts.account1.address, + call: { + function: 'transferWaves', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.calculator.address))}` }, + { type: 'integer', value: 1 }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, accounts.calculator.seed); + + await expect(broadcastAndWait(tx)).to.be.rejectedWith('not allowed'); + }); + + it('deposit should throw if shutdown', async () => { + const tx = invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'call', + args: [ + { type: 'string', value: 'deposit' }, + { type: 'list', value: [] }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, accounts.user1.seed); + + await expect(broadcastAndWait(tx)).to.be.rejectedWith('not allowed'); + }); + + it('successfully cancel shutdown', async () => { + const tx = data({ + data: [ + { key: '%s__shutdown' }, + ], + additionalFee: 4e5, + chainId, + }, accounts.factory.seed); + + await expect(broadcastAndWait(tx)).to.be.rejectedWith('Transaction is not allowed by account-script'); + }); + + it('successfully cancel shutdown', async () => { + const tx = data({ + data: [ + { key: '%s__shutdown' }, + ], + additionalFee: 4e5, + chainId, + }, accounts.factory.seed); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin1.seed, + address: accounts.factory.address, + txId: tx.id, + }); + + await expect(broadcastAndWait(tx)).to.be.fulfilled; + }); + + it('transferWaves in factory should work if no shutdown', async () => { + const tx = invokeScript({ + dApp: accounts.factory.address, + call: { + function: 'transferWaves', + args: [ + { type: 'binary', value: `base64:${base64Encode(base58Decode(accounts.calculator.address))}` }, + { type: 'integer', value: 1 }, + ], + }, + payment: [], + additionalFee: 4e5, + chainId, + }, accounts.calculator.seed); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin1.seed, + address: accounts.calculator.address, + txId: tx.id, + }); + + await expect(broadcastAndWait(tx)).to.be.fulfilled; + }); +}); diff --git a/test/components/multisig/_setup.mjs b/test/components/multisig/_setup.mjs new file mode 100644 index 00000000..b2554743 --- /dev/null +++ b/test/components/multisig/_setup.mjs @@ -0,0 +1,62 @@ +import wc from '@waves/ts-lib-crypto'; +import { + massTransfer, +} from '@waves/waves-transactions'; +import { format } from 'path'; +import { table, getBorderCharacters } from 'table'; + +import { + chainId, broadcastAndWait, baseSeed, +} from '../../utils/api.mjs'; + +import { + setScriptFromFile, +} from '../../utils/utils.mjs'; + +const nonceLength = 3; +const ridePath = '../ride'; +const multisigPath = format({ dir: ridePath, base: 'multisig.ride' }); + +export const setup = async () => { + const nonce = wc.random(nonceLength, 'Buffer').toString('hex'); + const names = [ + 'multisig', + 'dapp', + 'admin0', + 'admin1', + 'admin2', + 'admin3', + ]; + const accounts = Object.fromEntries(names.map((item) => { + const seed = `${item}#${nonce}`; + return [item, { seed, address: wc.address(seed, chainId), publicKey: wc.publicKey(seed) }]; + })); + const accountsInfo = Object.entries(accounts) + .map(([name, { address, publicKey }]) => [name, address, publicKey]); + console.log(table(accountsInfo, { + border: getBorderCharacters('norc'), + drawHorizontalLine: (index, size) => index === 0 || index === 1 || index === size, + header: { content: `pid: ${process.pid}, nonce: ${nonce}` }, + })); + const amount = 10e8; + const massTransferTx = massTransfer({ + transfers: Object.values(accounts).map(({ address }) => ({ recipient: address, amount })), + chainId, + }, baseSeed); + await broadcastAndWait(massTransferTx); + + const libraries = {}; + + await Promise.all([ + setScriptFromFile( + multisigPath, + accounts.multisig.seed, + null, + libraries, + ), + ]); + + return { + accounts, + }; +}; diff --git a/test/components/multisig/add_owner.spec.mjs b/test/components/multisig/add_owner.spec.mjs new file mode 100644 index 00000000..2f696949 --- /dev/null +++ b/test/components/multisig/add_owner.spec.mjs @@ -0,0 +1,129 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { api, broadcastAndWait } from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; +import { + addOwner, confirmTransaction, init, kPublicKeys, +} from './contract/multisig.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] multisig: add owner`, () => { + let accounts; + + before(async () => { + ({ accounts } = await setup()); + + const owners = [ + accounts.admin0.publicKey, + accounts.admin1.publicKey, + accounts.admin2.publicKey, + ]; + const quorum = 1; + + await init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + }); + }); + + it('invalid caller', async () => { + const { publicKey } = accounts.admin0; + + return expect(addOwner({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + publicKey, + additionalFee: 4e5, + })).to.be.rejectedWith('addOwner: not allowed'); + }); + + it('should throw if tx is not confirmed', async () => { + const publicKey = '1111'; + + return expect(addOwner({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + })).to.be.rejectedWith('Transaction is not allowed by account-script'); + }); + + it('invalid public key', async () => { + const publicKey = '1111'; + + const tx = await addOwner({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + return expect(broadcastAndWait(tx)).to.be.rejectedWith('addOwner: invalid public key'); + }); + + it('throw if public key is already addred', async () => { + const { publicKey } = accounts.admin2; + + const tx = await addOwner({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + return expect(broadcastAndWait(tx)).to.be.rejectedWith('addOwner: public key already added'); + }); + + it('should successfully add public key', async () => { + const { publicKey } = accounts.admin3; + + const tx = await addOwner({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + await broadcastAndWait(tx); + + const { value: publicKeys } = await api.addresses.fetchDataKey( + accounts.multisig.address, + kPublicKeys, + ); + + expect(publicKeys).to.contain(publicKey); + }); + + // TODO: check if too many owners +}); diff --git a/test/components/multisig/confirm_transaction.spec.mjs b/test/components/multisig/confirm_transaction.spec.mjs new file mode 100644 index 00000000..1d412f11 --- /dev/null +++ b/test/components/multisig/confirm_transaction.spec.mjs @@ -0,0 +1,177 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { api, broadcastAndWait, separator } from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; +import { + confirmTransaction, init, kConfirm, kStatus, setQuorum, +} from './contract/multisig.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] multisig: confirm transaction`, () => { + let accounts; + + before(async () => { + ({ accounts } = await setup()); + + const owners = [ + accounts.admin0.publicKey, + accounts.admin1.publicKey, + accounts.admin2.publicKey, + ]; + const quorum = 2; + + await init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + }); + }); + + it('invalid caller', async () => { + const { address } = accounts.multisig; + const txId = '1111'; + + return expect(confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin3.seed, + address, + txId, + additionalFee: 4e5, + })).to.be.rejectedWith('confirmTransaction: only admin'); + }); + + it('should throw if tx id is invalid', async () => { + const { address } = accounts.multisig; + const txId = '1111'; + + return expect(confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId, + additionalFee: 4e5, + })).to.be.rejectedWith('confirmTransaction: invalid txId'); + }); + + it('should throw if address is invalid', async () => { + const quorum = 2; + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + const address = '1111'; + + return expect(confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId: tx.id, + additionalFee: 4e5, + })).to.be.rejectedWith('confirmTransaction: invalid address'); + }); + + it('should successfully confirm transaction', async () => { + const quorum = 2; + + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + const { address } = accounts.multisig; + const txId = tx.id; + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId, + }); + + const [ + { value: status0 }, + { value: confirm0 }, + ] = await api.addresses.data( + accounts.multisig.address, + { + key: [ + encodeURIComponent(kStatus(address, txId)), + encodeURIComponent(kConfirm(address, txId)), + ], + }, + ); + + expect(status0).to.equal(false); + expect(confirm0).to.equal(accounts.admin0.publicKey); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin2.seed, + address, + txId, + }); + + const [ + { value: status2 }, + { value: confirm2 }, + ] = await api.addresses.data( + accounts.multisig.address, + { + key: [ + encodeURIComponent(kStatus(address, txId)), + encodeURIComponent(kConfirm(address, txId)), + ], + }, + ); + + expect(status2).to.equal(true); + expect(confirm2).to.equal([ + accounts.admin0.publicKey, + accounts.admin2.publicKey, + ].join(separator)); + + return expect(broadcastAndWait(tx)).to.be.fulfilled; + }); + + it('should throw if owner already confirmed tx', async () => { + const quorum = 2; + + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + const { address } = accounts.multisig; + const txId = tx.id; + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId, + }); + + expect(confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId, + })).to.be.rejectedWith('confirmTransaction: already confirmed'); + }); +}); diff --git a/test/components/multisig/contract/multisig.mjs b/test/components/multisig/contract/multisig.mjs new file mode 100644 index 00000000..1b79908c --- /dev/null +++ b/test/components/multisig/contract/multisig.mjs @@ -0,0 +1,192 @@ +import { invokeScript } from '@waves/waves-transactions'; +import { broadcastAndWait, chainId, separator } from '../../../utils/api.mjs'; + +export const kPublicKeys = '%s__publicKeys'; +export const kQuorum = '%s__quorum'; +export const kMultisig = '%s__multisig'; + +export const kStatus = (address, txId) => ['%s__status', address, txId].join(separator); +export const kConfirm = (address, txId) => ['%s__confirm', address, txId].join(separator); + +export const init = async ({ + dApp, + caller, + owners = [], + quorum = 0, + payments = [], + additionalFee = 0, + dry = false, +}) => { + const tx = invokeScript( + { + dApp, + call: { + function: 'init', + args: [ + { + type: 'list', + value: owners.map((value) => ({ type: 'string', value })), + }, + { type: 'integer', value: quorum }, + ], + }, + payment: payments, + additionalFee, + chainId, + }, + caller, + ); + if (dry) { + return tx; + } + return broadcastAndWait(tx); +}; + +export const confirmTransaction = async ({ + dApp, + caller, + address, + txId, + payments = [], + additionalFee = 0, + dry = false, +}) => { + const tx = invokeScript( + { + dApp, + call: { + function: 'confirmTransaction', + args: [ + { type: 'string', value: address }, + { type: 'string', value: txId }, + ], + }, + payment: payments, + additionalFee, + chainId, + }, + caller, + ); + if (dry) { + return tx; + } + return broadcastAndWait(tx); +}; + +export const revokeConfirmation = async ({ + dApp, + caller, + address, + txId, + payments = [], + additionalFee = 0, + dry = false, +}) => { + const tx = invokeScript( + { + dApp, + call: { + function: 'revokeConfirmation', + args: [ + { type: 'string', value: address }, + { type: 'string', value: txId }, + ], + }, + payment: payments, + additionalFee, + chainId, + }, + caller, + ); + if (dry) { + return tx; + } + return broadcastAndWait(tx); +}; + +export const addOwner = async ({ + dApp, + caller, + publicKey, + payments = [], + additionalFee = 0, + dry = false, +}) => { + const tx = invokeScript( + { + dApp, + call: { + function: 'addOwner', + args: [ + { type: 'string', value: publicKey }, + ], + }, + payment: payments, + additionalFee, + chainId, + }, + caller, + ); + if (dry) { + return tx; + } + return broadcastAndWait(tx); +}; + +export const removeOwner = async ({ + dApp, + caller, + publicKey, + payments = [], + additionalFee = 0, + dry = false, +}) => { + const tx = invokeScript( + { + dApp, + call: { + function: 'removeOwner', + args: [ + { type: 'string', value: publicKey }, + ], + }, + payment: payments, + additionalFee, + chainId, + }, + caller, + ); + if (dry) { + return tx; + } + return broadcastAndWait(tx); +}; + +export const setQuorum = async ({ + dApp, + caller, + quorum, + payments = [], + additionalFee = 0, + dry = false, +}) => { + const tx = invokeScript( + { + dApp, + call: { + function: 'setQuorum', + args: [ + { type: 'integer', value: quorum }, + ], + }, + payment: payments, + additionalFee, + chainId, + }, + caller, + ); + if (dry) { + return tx; + } + return broadcastAndWait(tx); +}; diff --git a/test/components/multisig/init.spec.mjs b/test/components/multisig/init.spec.mjs new file mode 100644 index 00000000..46d769b9 --- /dev/null +++ b/test/components/multisig/init.spec.mjs @@ -0,0 +1,165 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { api, broadcastAndWait, separator } from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; +import { + confirmTransaction, init, kMultisig, kPublicKeys, kQuorum, +} from './contract/multisig.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] multisig: init`, () => { + let accounts; + + before(async () => { + ({ accounts } = await setup()); + }); + + it('invalid caller', async () => { + const owners = []; + const quorum = 0; + + return expect(init({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + owners, + quorum, + additionalFee: 4e5, + })).to.be.rejectedWith('not allowed'); + }); + + it('should throw if owners size <= 0', async () => { + const owners = []; + const quorum = 0; + + return expect(init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + })).to.be.rejectedWith('invalid owners'); + }); + + it('should throw if owners size > maxOwners', async () => { + const maxOwners = 10; + const owners = Array(maxOwners + 1).fill(accounts.admin0.publicKey); + const quorum = 0; + + return expect(init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + })).to.be.rejectedWith('invalid owners'); + }); + + it('should throw id there are duplicates in owners', async () => { + const owners = [ + accounts.admin0.publicKey, + accounts.admin1.publicKey, + accounts.admin1.publicKey, + accounts.admin2.publicKey, + ]; + const quorum = 3; + + return expect(init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + })).to.be.rejectedWith('must not contain duplicates'); + }); + + it('invalid quorum', async () => { + const owners = [accounts.admin0.publicKey]; + const quorum = 0; + + return expect(init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + })).to.be.rejectedWith('invalid quorum'); + }); + + it('invalid owner public key', async () => { + const owners = [accounts.admin0.publicKey, '1111']; + const quorum = 1; + + return expect(init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + })).to.be.rejectedWith('invalid owner public key'); + }); + + it('successfull init', async () => { + const owners = [ + accounts.admin0.publicKey, + accounts.admin1.publicKey, + accounts.admin2.publicKey, + ]; + + const targetQuorum = 1; + await expect(init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum: targetQuorum, + additionalFee: 4e5, + })).to.be.fulfilled; + + const [ + { value: multisig }, + { value: publicKeys }, + { value: quorum }, + ] = await api.addresses.data( + accounts.multisig.address, + { + key: [ + encodeURIComponent(kMultisig), + encodeURIComponent(kPublicKeys), + encodeURIComponent(kQuorum), + ], + }, + ); + + expect(multisig).to.equal(accounts.multisig.address); + expect(publicKeys).to.equal(owners.join(separator)); + expect(quorum).to.equal(targetQuorum); + }); + + it('already initialized', async () => { + const owners = [ + accounts.admin0.publicKey, + ]; + const quorum = 1; + + const tx = await init({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + return expect(broadcastAndWait(tx)).to.be.rejectedWith('already initialized'); + }); +}); diff --git a/test/components/multisig/mock/dapp.ride b/test/components/multisig/mock/dapp.ride new file mode 100644 index 00000000..048e789d --- /dev/null +++ b/test/components/multisig/mock/dapp.ride @@ -0,0 +1,73 @@ +{-# STDLIB_VERSION 7 #-} +{-# CONTENT_TYPE DAPP #-} +{-# SCRIPT_TYPE ACCOUNT #-} + +let separator = "__" +let contractFilename = "dapp.ride" + +let kMultisig = "%s__multisig" +let kPublicKeys = "%s__publicKeys" +func kStatus(dapp: String, txId: String) = ["%s__status", dapp, txId].makeString(separator) + +func validateAddress(address: String) = { + addressFromString(address).isDefined() +} + +func wrapErr(s: String) = { + contractFilename + ": " + s +} + +func throwErr(s: String) = { + throw(wrapErr(s)) +} + +func mustAddress(i: Invocation, address: Address) = { + i.caller == address || throwErr("permission denied") +} + +func mustThis(i: Invocation) = { + mustAddress(i, this) +} + +func mustAdmin(callerPublicKey: ByteVector) = { + let multisig = getStringValue(kMultisig).addressFromStringValue() + let publicKeysList = multisig.getStringValue(kPublicKeys).split(separator) + + containsElement( + publicKeysList, callerPublicKey.toBase58String() + ) || throwErr("not allowed") +} + +@Callable(i) +func test() = { + strict checks = [ + i.callerPublicKey.mustAdmin() + ] + + (nil, unit) +} + +@Callable(i) +func setMultisig(multisig: String) = { + strict checks = [ + i.mustThis(), + validateAddress(multisig) || throwErr("setMultisig: invalid multisig address") + ] + + ([ + StringEntry(kMultisig, multisig) + ], unit) +} + +@Verifier(tx) +func verify() = { + match getString(kMultisig) { + case multisig: String => { + let statusKey = kStatus(this.toString(), tx.id.toBase58String()) + let status = multisig.addressFromStringValue().getBoolean(statusKey).valueOrElse(false) + + status + } + case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) + } +} diff --git a/test/components/multisig/remove_owner.spec copy.mjs b/test/components/multisig/remove_owner.spec copy.mjs new file mode 100644 index 00000000..4431faa1 --- /dev/null +++ b/test/components/multisig/remove_owner.spec copy.mjs @@ -0,0 +1,200 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { api, broadcastAndWait } from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; +import { + confirmTransaction, init, kPublicKeys, kQuorum, removeOwner, setQuorum, +} from './contract/multisig.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] multisig: remove owner`, () => { + let accounts; + + before(async () => { + ({ accounts } = await setup()); + + const owners = [ + accounts.admin0.publicKey, + accounts.admin1.publicKey, + accounts.admin2.publicKey, + ]; + const quorum = 1; + + await init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + }); + }); + + it('invalid caller', async () => { + const { publicKey } = accounts.admin0; + + return expect(removeOwner({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + publicKey, + additionalFee: 4e5, + })).to.be.rejectedWith('removeOwner: not allowed'); + }); + + it('should throw if tx is not confirmed', async () => { + const publicKey = '1111'; + + return expect(removeOwner({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + })).to.be.rejectedWith('Transaction is not allowed by account-script'); + }); + + it('invalid public key', async () => { + const publicKey = '1111'; + + const tx = await removeOwner({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + return expect(broadcastAndWait(tx)).to.be.rejectedWith('removeOwner: invalid public key'); + }); + + it('throw if no such owner', async () => { + const { publicKey } = accounts.admin3; + + const tx = await removeOwner({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + return expect(broadcastAndWait(tx)).to.be.rejectedWith('removeOwner: no such owner'); + }); + + it('should successfully remove owner', async () => { + const { publicKey } = accounts.admin2; + + const tx = await removeOwner({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + await broadcastAndWait(tx); + + const { value: publicKeys } = await api.addresses.fetchDataKey( + accounts.multisig.address, + kPublicKeys, + ); + + expect(publicKeys).to.not.contain(publicKey); + }); + + it('quorum should be decreased', async () => { + const quorum = 2; + const setQuorumTx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: setQuorumTx.id, + }); + + await broadcastAndWait(setQuorumTx); + + const { publicKey } = accounts.admin1; + const removeOwnerTx = await removeOwner({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: removeOwnerTx.id, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin1.seed, + address: accounts.multisig.address, + txId: removeOwnerTx.id, + }); + + await broadcastAndWait(removeOwnerTx); + + const { value: quorumNew } = await api.addresses.fetchDataKey( + accounts.multisig.address, + kQuorum, + ); + + expect(quorumNew).to.equal(quorum - 1); + }); + + it('throw if too few owners', async () => { + const { publicKey } = accounts.admin0; + + const tx = await removeOwner({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + publicKey, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + return expect(broadcastAndWait(tx)).to.be.rejectedWith('removeOwner: too few owners'); + }); +}); diff --git a/test/components/multisig/revoke_confirmation.spec.mjs b/test/components/multisig/revoke_confirmation.spec.mjs new file mode 100644 index 00000000..86284109 --- /dev/null +++ b/test/components/multisig/revoke_confirmation.spec.mjs @@ -0,0 +1,200 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { api } from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; +import { + confirmTransaction, init, kConfirm, kStatus, revokeConfirmation, setQuorum, +} from './contract/multisig.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] multisig: revoke confirmation`, () => { + let accounts; + + before(async () => { + ({ accounts } = await setup()); + + const owners = [ + accounts.admin0.publicKey, + accounts.admin1.publicKey, + accounts.admin2.publicKey, + ]; + const quorum = 2; + + await init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + }); + }); + + it('invalid caller', async () => { + const { address } = accounts.multisig; + const txId = '1111'; + + return expect(revokeConfirmation({ + dApp: accounts.multisig.address, + caller: accounts.admin3.seed, + address, + txId, + additionalFee: 4e5, + })).to.be.rejectedWith('revokeConfirmation: only admin'); + }); + + it('should throw if tx id is invalid', async () => { + const { address } = accounts.multisig; + const txId = '1111'; + + return expect(revokeConfirmation({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId, + additionalFee: 4e5, + })).to.be.rejectedWith('revokeConfirmation: invalid txId'); + }); + + it('should throw if address is invalid', async () => { + const quorum = 2; + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + const address = '1111'; + + return expect(revokeConfirmation({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId: tx.id, + additionalFee: 4e5, + })).to.be.rejectedWith('revokeConfirmation: invalid address'); + }); + + it('should throw if owner did not confirm the tx', async () => { + const quorum = 2; + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + const { address } = accounts.multisig; + + return expect(revokeConfirmation({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId: tx.id, + additionalFee: 4e5, + })).to.be.rejectedWith('revokeConfirmation: not confirmed'); + }); + + it('should successfully revoke confirmation', async () => { + const quorum = 1; + + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + const { address } = accounts.multisig; + const txId = tx.id; + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId, + }); + + const [ + { value: statusBefore }, + { value: confirmBefore }, + ] = await api.addresses.data( + accounts.multisig.address, + { + key: [ + encodeURIComponent(kStatus(address, txId)), + encodeURIComponent(kConfirm(address, txId)), + ], + }, + ); + + expect(statusBefore).to.equal(false); + expect(confirmBefore).to.equal(accounts.admin0.publicKey); + + await revokeConfirmation({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId, + }); + + const [ + { value: statusAfter }, + { value: confirmAfter }, + ] = await api.addresses.data( + accounts.multisig.address, + { + key: [ + encodeURIComponent(kStatus(address, txId)), + encodeURIComponent(kConfirm(address, txId)), + ], + }, + ); + + expect(statusAfter).to.equal(false); + expect(confirmAfter).to.equal(''); + }); + + it('should throw if quorum is reached', async () => { + const quorum = 1; + + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + const { address } = accounts.multisig; + const txId = tx.id; + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin1.seed, + address, + txId, + }); + + expect(revokeConfirmation({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address, + txId, + })).to.be.rejectedWith('revokeConfirmation: quorum already reached'); + }); +}); diff --git a/test/components/multisig/set_multisig.spec.mjs b/test/components/multisig/set_multisig.spec.mjs new file mode 100644 index 00000000..43c1c053 --- /dev/null +++ b/test/components/multisig/set_multisig.spec.mjs @@ -0,0 +1,169 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { data, invokeScript } from '@waves/waves-transactions'; +import { api, broadcastAndWait, chainId } from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; +import { + confirmTransaction, init, kMultisig, +} from './contract/multisig.mjs'; +import { setScriptFromFile } from '../../utils/utils.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] multisig: set multisig`, () => { + let accounts; + + before(async () => { + ({ accounts } = await setup()); + + const owners = [ + accounts.admin0.publicKey, + accounts.admin1.publicKey, + accounts.admin2.publicKey, + ]; + const quorum = 1; + + await init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + }); + + await setScriptFromFile( + 'components/multisig/mock/dapp.ride', + accounts.dapp.seed, + ); + }); + + it('tx allowed if multisig is not set', async () => { + await expect(broadcastAndWait(data({ + data: [], + additionalFee: 4e5, + chainId, + }, accounts.dapp.seed))).to.be.fulfilled; + }); + + it('admin function should throw if multisig is not set', async () => { + await expect(broadcastAndWait(invokeScript({ + dApp: accounts.dapp.address, + call: { + function: 'test', + }, + additionalFee: 4e5, + chainId, + }, accounts.dapp.seed))).to.be.rejectedWith('value by key \'%s__multisig\' not found'); + }); + + it('set multisig should throw if caller is not this', async () => { + await expect(broadcastAndWait(invokeScript({ + dApp: accounts.dapp.address, + call: { + function: 'setMultisig', + args: [ + { type: 'string', value: accounts.multisig.address }, + ], + }, + additionalFee: 4e5, + chainId, + }, accounts.admin0.seed))).to.be.rejectedWith('permission denied'); + }); + + it('set multisig should throw if address is not valid', async () => { + await expect(broadcastAndWait(invokeScript({ + dApp: accounts.dapp.address, + call: { + function: 'setMultisig', + args: [ + { type: 'string', value: '1111' }, + ], + }, + additionalFee: 4e5, + chainId, + }, accounts.dapp.seed))).to.be.rejectedWith('setMultisig: invalid multisig address'); + }); + + it('should successfully set multisig', async () => { + await expect(broadcastAndWait(invokeScript({ + dApp: accounts.dapp.address, + call: { + function: 'setMultisig', + args: [ + { type: 'string', value: accounts.multisig.address }, + ], + }, + additionalFee: 4e5, + chainId, + }, accounts.dapp.seed))).to.be.fulfilled; + + const [ + { value: multisig }, + ] = await api.addresses.data( + accounts.dapp.address, + { + key: [ + encodeURIComponent(kMultisig), + ], + }, + ); + + expect(multisig).to.equal(accounts.multisig.address); + }); + + it('tx should throw if not confirmed', async () => { + await expect(broadcastAndWait(data({ + data: [], + additionalFee: 4e5, + chainId, + }, accounts.dapp.seed))).to.be.rejectedWith('Transaction is not allowed by account-script'); + }); + + it('admin function should throw if invalid caller', async () => { + await expect(broadcastAndWait(invokeScript({ + dApp: accounts.dapp.address, + call: { + function: 'test', + }, + additionalFee: 4e5, + chainId, + }, accounts.admin3.seed))).to.be.rejectedWith('not allowed'); + }); + + it('successfully call admin function', async () => { + await expect(broadcastAndWait(invokeScript({ + dApp: accounts.dapp.address, + call: { + function: 'test', + }, + additionalFee: 4e5, + chainId, + }, accounts.admin0.seed))).to.be.fulfilled; + }); + + it('should successfully set multisig again if confirmed', async () => { + const tx = invokeScript({ + dApp: accounts.dapp.address, + call: { + function: 'setMultisig', + args: [ + { type: 'string', value: accounts.multisig.address }, + ], + }, + additionalFee: 4e5, + chainId, + }, accounts.dapp.seed); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.dapp.address, + txId: tx.id, + }); + + await expect(broadcastAndWait(tx)).to.be.fulfilled; + }); +}); diff --git a/test/components/multisig/set_quorum.spec.mjs b/test/components/multisig/set_quorum.spec.mjs new file mode 100644 index 00000000..79c54c87 --- /dev/null +++ b/test/components/multisig/set_quorum.spec.mjs @@ -0,0 +1,126 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import { api, broadcastAndWait } from '../../utils/api.mjs'; +import { setup } from './_setup.mjs'; +import { + confirmTransaction, init, kQuorum, setQuorum, +} from './contract/multisig.mjs'; + +chai.use(chaiAsPromised); +chai.use(chaiSubset); +const { expect } = chai; + +describe(`[${process.pid}] multisig: set quorum`, () => { + let accounts; + + before(async () => { + ({ accounts } = await setup()); + + const owners = [ + accounts.admin0.publicKey, + accounts.admin1.publicKey, + accounts.admin2.publicKey, + ]; + const quorum = 1; + + await init({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + owners, + quorum, + additionalFee: 4e5, + }); + }); + + it('invalid caller', async () => { + const quorum = 2; + + return expect(setQuorum({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + quorum, + additionalFee: 4e5, + })).to.be.rejectedWith('setQuorum: not allowed'); + }); + + it('should throw if tx is not confirmed', async () => { + const quorum = 2; + + return expect(setQuorum({ + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + })).to.be.rejectedWith('Transaction is not allowed by account-script'); + }); + + it('quorum should be > 0', async () => { + const quorum = 0; + + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + return expect(broadcastAndWait(tx)).to.be.rejectedWith('setQuorum: invalid quorum'); + }); + + it('quorum should be <= owners number', async () => { + const ownersNumber = 3; + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum: ownersNumber + 1, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + return expect(broadcastAndWait(tx)).to.be.rejectedWith('setQuorum: invalid quorum'); + }); + + it('should successfully set quorum', async () => { + const quorum = 2; + + const tx = await setQuorum({ + dry: true, + dApp: accounts.multisig.address, + caller: accounts.multisig.seed, + quorum, + additionalFee: 4e5, + }); + + await confirmTransaction({ + dApp: accounts.multisig.address, + caller: accounts.admin0.seed, + address: accounts.multisig.address, + txId: tx.id, + }); + + await broadcastAndWait(tx); + + const { value: quorumNew } = await api.addresses.fetchDataKey( + accounts.multisig.address, + kQuorum, + ); + + expect(quorumNew).to.equal(quorum); + }); +}); diff --git a/test/package.json b/test/package.json index 0c7c66b7..016375d5 100644 --- a/test/package.json +++ b/test/package.json @@ -29,6 +29,8 @@ "test-l2mp-staking": "mocha -r dotenv/config --parallel --require components/l2mp_staking/_hooks.mjs components/l2mp_staking", "test-l2mp-swap": "mocha -r dotenv/config --parallel --require components/l2mp_swap/_hooks.mjs components/l2mp_swap", "test-wxdao": "mocha -r dotenv/config --parallel components/wxdao", + "test-futures": "mocha -r dotenv/config --parallel components/futures", + "test-multisig": "mocha -r dotenv/config --parallel components/multisig", "ci-test": "node_modules/.bin/concurrently --max-processes 8 --timings npm:test-*", "exchange-test": "mocha -r dotenv/config --parallel --require components/exchange/_hooks.mjs components/exchange", "test": "API_NODE_URL=http://localhost:6869 npm run ci-test", @@ -41,6 +43,7 @@ "dependencies": { "@types/chai": "^4.3.0", "@types/chai-as-promised": "^7.1.5", + "@types/chai-subset": "^1.3.5", "@types/mocha": "^9.1.0", "@waves/bignumber": "^1.1.1", "@waves/node-api-js": "^1.3.0", diff --git a/test/pnpm-lock.yaml b/test/pnpm-lock.yaml index a69fdb63..3d1459b6 100644 --- a/test/pnpm-lock.yaml +++ b/test/pnpm-lock.yaml @@ -1,70 +1,100 @@ -lockfileVersion: 5.4 - -specifiers: - '@types/chai': ^4.3.0 - '@types/chai-as-promised': ^7.1.5 - '@types/mocha': ^9.1.0 - '@waves/bignumber': ^1.1.1 - '@waves/node-api-js': ^1.3.0 - '@waves/ride-js': ^2.2.0 - '@waves/ts-lib-crypto': ^1.4.4-beta.1 - '@waves/ts-types': ^1.1.0 - '@waves/waves-transactions': ^4.2.5-beta.3 - axios: ^0.26.1 - chai: ^4.3.6 - chai-as-promised: ^7.1.1 - concurrently: ^7.1.0 - dotenv: ^16.0.3 - eslint: ^8.10.0 - eslint-config-airbnb-base: ^15.0.0 - eslint-plugin-import: ^2.25.4 - level: ^8.0.0 - mocha: ^9.2.1 - ora: ^6.1.0 - prompts: ^2.4.2 - table: ^6.8.0 +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false dependencies: - '@types/chai': 4.3.0 - '@types/chai-as-promised': 7.1.5 - '@types/mocha': 9.1.0 - '@waves/bignumber': 1.1.1 - '@waves/node-api-js': 1.3.0 - '@waves/ride-js': 2.2.0 - '@waves/ts-lib-crypto': 1.4.4-beta.1 - '@waves/ts-types': 1.1.0 - '@waves/waves-transactions': 4.2.5-beta.3 - axios: 0.26.1 - chai: 4.3.6 - chai-as-promised: 7.1.1_chai@4.3.6 - concurrently: 7.1.0 - eslint: 8.10.0 - eslint-config-airbnb-base: 15.0.0_rnagsyfcubvpoxo2ynj23pim7u - eslint-plugin-import: 2.25.4_eslint@8.10.0 - level: 8.0.0 - mocha: 9.2.1 - ora: 6.1.0 - prompts: 2.4.2 - table: 6.8.0 + '@types/chai': + specifier: ^4.3.0 + version: 4.3.0 + '@types/chai-as-promised': + specifier: ^7.1.5 + version: 7.1.5 + '@types/chai-subset': + specifier: ^1.3.5 + version: 1.3.5 + '@types/mocha': + specifier: ^9.1.0 + version: 9.1.0 + '@waves/bignumber': + specifier: ^1.1.1 + version: 1.1.1 + '@waves/node-api-js': + specifier: ^1.3.0 + version: 1.3.0 + '@waves/ride-js': + specifier: ^2.2.7 + version: 2.2.9 + '@waves/ts-lib-crypto': + specifier: ^1.4.4-beta.1 + version: 1.4.4-beta.1 + '@waves/ts-types': + specifier: ^1.1.0 + version: 1.1.0 + '@waves/waves-transactions': + specifier: ^4.2.5-beta.3 + version: 4.2.5-beta.3 + axios: + specifier: ^0.26.1 + version: 0.26.1 + chai: + specifier: ^4.3.6 + version: 4.3.6 + chai-as-promised: + specifier: ^7.1.1 + version: 7.1.1(chai@4.3.6) + chai-subset: + specifier: ^1.6.0 + version: 1.6.0 + concurrently: + specifier: ^7.1.0 + version: 7.1.0 + eslint: + specifier: ^8.10.0 + version: 8.10.0 + eslint-config-airbnb-base: + specifier: ^15.0.0 + version: 15.0.0(eslint-plugin-import@2.25.4)(eslint@8.10.0) + eslint-plugin-import: + specifier: ^2.25.4 + version: 2.25.4(eslint@8.10.0) + level: + specifier: ^8.0.0 + version: 8.0.0 + mocha: + specifier: ^9.2.1 + version: 9.2.1 + ora: + specifier: ^6.1.0 + version: 6.1.0 + prompts: + specifier: ^2.4.2 + version: 2.4.2 + table: + specifier: ^6.8.0 + version: 6.8.0 devDependencies: - dotenv: 16.0.3 + dotenv: + specifier: ^16.0.3 + version: 16.0.3 packages: - /@babel/code-frame/7.18.6: + /@babel/code-frame@7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.18.6 dev: false - /@babel/helper-validator-identifier/7.18.6: + /@babel/helper-validator-identifier@7.18.6: resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==} engines: {node: '>=6.9.0'} dev: false - /@babel/highlight/7.18.6: + /@babel/highlight@7.18.6: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} dependencies: @@ -73,12 +103,12 @@ packages: js-tokens: 4.0.0 dev: false - /@eslint/eslintrc/1.2.0: + /@eslint/eslintrc@1.2.0: resolution: {integrity: sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.3 + debug: 4.3.3(supports-color@8.1.1) espree: 9.3.1 globals: 13.12.1 ignore: 4.0.6 @@ -90,22 +120,22 @@ packages: - supports-color dev: false - /@humanwhocodes/config-array/0.9.5: + /@humanwhocodes/config-array@0.9.5: resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==} engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.3 + debug: 4.3.3(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color dev: false - /@humanwhocodes/object-schema/1.2.1: + /@humanwhocodes/object-schema@1.2.1: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: false - /@jest/environment/27.5.1: + /@jest/environment@27.5.1: resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -115,7 +145,7 @@ packages: jest-mock: 27.5.1 dev: false - /@jest/fake-timers/27.5.1: + /@jest/fake-timers@27.5.1: resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -127,7 +157,7 @@ packages: jest-util: 27.5.1 dev: false - /@jest/globals/27.5.1: + /@jest/globals@27.5.1: resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -136,7 +166,7 @@ packages: expect: 27.5.1 dev: false - /@jest/types/27.5.1: + /@jest/types@27.5.1: resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -147,143 +177,149 @@ packages: chalk: 4.1.2 dev: false - /@protobufjs/aspromise/1.1.2: + /@protobufjs/aspromise@1.1.2: resolution: {integrity: sha1-m4sMxmPWaafY9vXQiToU00jzD78=} dev: false - /@protobufjs/base64/1.1.2: + /@protobufjs/base64@1.1.2: resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} dev: false - /@protobufjs/codegen/2.0.4: + /@protobufjs/codegen@2.0.4: resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} dev: false - /@protobufjs/eventemitter/1.1.0: + /@protobufjs/eventemitter@1.1.0: resolution: {integrity: sha1-NVy8mLr61ZePntCV85diHx0Ga3A=} dev: false - /@protobufjs/fetch/1.1.0: + /@protobufjs/fetch@1.1.0: resolution: {integrity: sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=} dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/inquire': 1.1.0 dev: false - /@protobufjs/float/1.0.2: + /@protobufjs/float@1.0.2: resolution: {integrity: sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=} dev: false - /@protobufjs/inquire/1.1.0: + /@protobufjs/inquire@1.1.0: resolution: {integrity: sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=} dev: false - /@protobufjs/path/1.1.2: + /@protobufjs/path@1.1.2: resolution: {integrity: sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=} dev: false - /@protobufjs/pool/1.1.0: + /@protobufjs/pool@1.1.0: resolution: {integrity: sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=} dev: false - /@protobufjs/utf8/1.1.0: + /@protobufjs/utf8@1.1.0: resolution: {integrity: sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=} dev: false - /@sinonjs/commons/1.8.3: + /@sinonjs/commons@1.8.3: resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==} dependencies: type-detect: 4.0.8 dev: false - /@sinonjs/fake-timers/8.1.0: + /@sinonjs/fake-timers@8.1.0: resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==} dependencies: '@sinonjs/commons': 1.8.3 dev: false - /@types/base64-js/1.3.0: + /@types/base64-js@1.3.0: resolution: {integrity: sha512-ZmI0sZGAUNXUfMWboWwi4LcfpoVUYldyN6Oe0oJ5cCsHDU/LlRq8nQKPXhYLOx36QYSW9bNIb1vvRrD6K7Llgw==} dev: false - /@types/chai-as-promised/7.1.5: + /@types/chai-as-promised@7.1.5: resolution: {integrity: sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==} dependencies: '@types/chai': 4.3.0 dev: false - /@types/chai/4.3.0: + /@types/chai-subset@1.3.5: + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + dependencies: + '@types/chai': 4.3.0 + dev: false + + /@types/chai@4.3.0: resolution: {integrity: sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==} dev: false - /@types/istanbul-lib-coverage/2.0.4: + /@types/istanbul-lib-coverage@2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} dev: false - /@types/istanbul-lib-report/3.0.0: + /@types/istanbul-lib-report@3.0.0: resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} dependencies: '@types/istanbul-lib-coverage': 2.0.4 dev: false - /@types/istanbul-reports/3.0.1: + /@types/istanbul-reports@3.0.1: resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} dependencies: '@types/istanbul-lib-report': 3.0.0 dev: false - /@types/json5/0.0.29: + /@types/json5@0.0.29: resolution: {integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=} dev: false - /@types/long/4.0.1: + /@types/long@4.0.1: resolution: {integrity: sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==} dev: false - /@types/mocha/9.1.0: + /@types/mocha@9.1.0: resolution: {integrity: sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==} dev: false - /@types/node-fetch/2.6.1: + /@types/node-fetch@2.6.1: resolution: {integrity: sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==} dependencies: '@types/node': 18.0.1 form-data: 3.0.1 dev: false - /@types/node/17.0.21: + /@types/node@17.0.21: resolution: {integrity: sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==} dev: false - /@types/node/18.0.1: + /@types/node@18.0.1: resolution: {integrity: sha512-CmR8+Tsy95hhwtZBKJBs0/FFq4XX7sDZHlGGf+0q+BRZfMbOTkzkj0AFAuTyXbObDIoanaBBW0+KEW+m3N16Wg==} dev: false - /@types/stack-utils/2.0.1: + /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: false - /@types/yargs-parser/21.0.0: + /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} dev: false - /@types/yargs/16.0.4: + /@types/yargs@16.0.4: resolution: {integrity: sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==} dependencies: '@types/yargs-parser': 21.0.0 dev: false - /@ungap/promise-all-settled/1.1.2: + /@ungap/promise-all-settled@1.1.2: resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} dev: false - /@waves/bignumber/1.1.1: + /@waves/bignumber@1.1.1: resolution: {integrity: sha512-WUY0R0y0Rd92nbyQHbIFDXCWh2YMtf5FYtpoTv4yRomM75cRLJ0/NIQ828guUXLKeVytKzWgvDYj1CZfxatDkg==} dependencies: bignumber.js: 9.0.2 dev: false - /@waves/marshall/0.14.0: + /@waves/marshall@0.14.0: resolution: {integrity: sha512-zcmDEwlD3dgzaTX6d2UM57KaGO6DK759b9EfdGa48UzwsjLdqX+v/6hrcqZEPUYMeymD6fO8O4/S+0RDxue8Wg==} dependencies: '@types/base64-js': 1.3.0 @@ -293,7 +329,7 @@ packages: long: 4.0.0 dev: false - /@waves/node-api-js/1.3.0: + /@waves/node-api-js@1.3.0: resolution: {integrity: sha512-FEI42KM1C6hE541kexV/eqWDeBrVxeMswZbHQ9kRlFdso/kKmouhhjV73NI/zFCSwMzbFI4YDe2ElOSim0DyEA==} dependencies: '@types/node-fetch': 2.6.1 @@ -305,19 +341,19 @@ packages: - encoding dev: false - /@waves/parse-json-bignumber/1.0.3: + /@waves/parse-json-bignumber@1.0.3: resolution: {integrity: sha512-zBHIQUjjMYMQXNQcwJwzNShUZnoTM6JfVJDwa0eDGUVk+JAKVGiXxv/k29Ng9TsIDi97hwVravlPPwfZcy4XXQ==} dev: false - /@waves/protobuf-serialization/1.4.1-beta.1: + /@waves/protobuf-serialization@1.4.1-beta.1: resolution: {integrity: sha512-IjEwyWmjyesjURvhvB2DK/QZ8mKFuBg7zz5SvgG6q8/ofnC4oplSOsWsgT2DjOG4OzGaMUv7Kkv/ZV1qTQxO9g==} dependencies: '@types/long': 4.0.1 protobufjs: 6.11.2 dev: false - /@waves/ride-js/2.2.0: - resolution: {integrity: sha512-a46cZYAE9Cp2DjXPcqrx4CQRtTGLIr+gIvPomRCkDqZpgtd2DvSh6alLJryYMSeMHlgugmZv/qos5Z7OMAs4eQ==} + /@waves/ride-js@2.2.9: + resolution: {integrity: sha512-XSdF27XYyw2CDPSxov+dpx1C1e4q2B/nHRUiOO6l6qEY7W9ita4Zer+YSHgamqPmjsQyfCjNj6CujFZ7UJtdXg==} dependencies: '@jest/globals': 27.5.1 '@waves/ts-lib-crypto': 1.4.3 @@ -326,29 +362,29 @@ packages: - supports-color dev: false - /@waves/ts-lib-crypto/1.4.3: + /@waves/ts-lib-crypto@1.4.3: resolution: {integrity: sha512-2pKgyvtLapgM5vpaUEYzX7NYe2bkB+HdWn9W/4d7UFKwyg6zoOYhRQWyb6GuLi3OLHTETgiqpcMZvciFA0Ds6g==} dependencies: js-sha3: 0.8.0 node-forge: 0.8.5 dev: false - /@waves/ts-lib-crypto/1.4.4-beta.1: + /@waves/ts-lib-crypto@1.4.4-beta.1: resolution: {integrity: sha512-tlvThkMCoCDicOznW82wDZWQqfAWcm6ulQnuNzR++X9o0EOHM3Cj8LlS2pkgF0YjZrqEYHTp/4e0RXXYVY+dpw==} dependencies: js-sha3: 0.8.0 node-forge: 0.10.0 dev: false - /@waves/ts-types/1.0.6-beta.4: + /@waves/ts-types@1.0.6-beta.4: resolution: {integrity: sha512-TyFzgYiWkJ5PF7F3XxAE1dudti5a9MVh2BgtGhYKIrSanXNPXhT9KYYRkQRIemYP+HQ4y89rEBf/CUszTdnaag==} dev: false - /@waves/ts-types/1.1.0: + /@waves/ts-types@1.1.0: resolution: {integrity: sha512-SGHj4cIIvMAhDPiDhbpEzP2UqNF3VgTssGf6UaJ7vwzxq0W1pqz2lKMDe9pZup9p9rEETGW4Yy3+K1G7OGOLxA==} dev: false - /@waves/waves-transactions/4.2.5-beta.3: + /@waves/waves-transactions@4.2.5-beta.3: resolution: {integrity: sha512-LiEya9gV0KAaxEVusKEPiXb6fZYuVOUePUfC9YjasODjGT4KLOFp5Po3u746Rt7T04S/3cUaxk19WhdfbpGBtQ==} dependencies: '@waves/marshall': 0.14.0 @@ -363,7 +399,7 @@ packages: - supports-color dev: false - /abstract-level/1.0.3: + /abstract-level@1.0.3: resolution: {integrity: sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==} engines: {node: '>=12'} dependencies: @@ -376,7 +412,7 @@ packages: queue-microtask: 1.2.3 dev: false - /acorn-jsx/5.3.2_acorn@8.7.0: + /acorn-jsx@5.3.2(acorn@8.7.0): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -384,13 +420,13 @@ packages: acorn: 8.7.0 dev: false - /acorn/8.7.0: + /acorn@8.7.0: resolution: {integrity: sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==} engines: {node: '>=0.4.0'} hasBin: true dev: false - /ajv/6.12.6: + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: fast-deep-equal: 3.1.3 @@ -399,7 +435,7 @@ packages: uri-js: 4.4.1 dev: false - /ajv/8.11.0: + /ajv@8.11.0: resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} dependencies: fast-deep-equal: 3.1.3 @@ -408,41 +444,41 @@ packages: uri-js: 4.4.1 dev: false - /ansi-colors/4.1.1: + /ansi-colors@4.1.1: resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} engines: {node: '>=6'} dev: false - /ansi-regex/5.0.1: + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} dev: false - /ansi-regex/6.0.1: + /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} dev: false - /ansi-styles/3.2.1: + /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} dependencies: color-convert: 1.9.3 dev: false - /ansi-styles/4.3.0: + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} dependencies: color-convert: 2.0.1 dev: false - /ansi-styles/5.2.0: + /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} dev: false - /anymatch/3.1.2: + /anymatch@3.1.2: resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} engines: {node: '>= 8'} dependencies: @@ -450,11 +486,11 @@ packages: picomatch: 2.3.1 dev: false - /argparse/2.0.1: + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: false - /array-includes/3.1.4: + /array-includes@3.1.4: resolution: {integrity: sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==} engines: {node: '>= 0.4'} dependencies: @@ -465,7 +501,7 @@ packages: is-string: 1.0.7 dev: false - /array.prototype.flat/1.2.5: + /array.prototype.flat@1.2.5: resolution: {integrity: sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==} engines: {node: '>= 0.4'} dependencies: @@ -474,20 +510,20 @@ packages: es-abstract: 1.19.1 dev: false - /assertion-error/1.1.0: + /assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: false - /astral-regex/2.0.0: + /astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} dev: false - /asynckit/0.4.0: + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: false - /axios/0.19.2: + /axios@0.19.2: resolution: {integrity: sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==} deprecated: Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410 dependencies: @@ -496,7 +532,7 @@ packages: - supports-color dev: false - /axios/0.26.1: + /axios@0.26.1: resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} dependencies: follow-redirects: 1.14.9 @@ -504,24 +540,24 @@ packages: - debug dev: false - /balanced-match/1.0.2: + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: false - /base64-js/1.5.1: + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: false - /bignumber.js/9.0.2: + /bignumber.js@9.0.2: resolution: {integrity: sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==} dev: false - /binary-extensions/2.2.0: + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} dev: false - /bl/5.0.0: + /bl@5.0.0: resolution: {integrity: sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==} dependencies: buffer: 6.0.3 @@ -529,21 +565,21 @@ packages: readable-stream: 3.6.0 dev: false - /brace-expansion/1.1.11: + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 dev: false - /braces/3.0.2: + /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} dependencies: fill-range: 7.0.1 dev: false - /browser-level/1.0.1: + /browser-level@1.0.1: resolution: {integrity: sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==} dependencies: abstract-level: 1.0.3 @@ -552,40 +588,40 @@ packages: run-parallel-limit: 1.1.0 dev: false - /browser-stdout/1.3.1: + /browser-stdout@1.3.1: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} dev: false - /buffer/6.0.3: + /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 dev: false - /call-bind/1.0.2: + /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: function-bind: 1.1.1 get-intrinsic: 1.1.1 dev: false - /callsites/3.1.0: + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} dev: false - /camelcase/6.3.0: + /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} dev: false - /catering/2.1.1: + /catering@2.1.1: resolution: {integrity: sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==} engines: {node: '>=6'} dev: false - /chai-as-promised/7.1.1_chai@4.3.6: + /chai-as-promised@7.1.1(chai@4.3.6): resolution: {integrity: sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==} peerDependencies: chai: '>= 2.1.2 < 5' @@ -594,7 +630,12 @@ packages: check-error: 1.0.2 dev: false - /chai/4.3.6: + /chai-subset@1.6.0: + resolution: {integrity: sha512-K3d+KmqdS5XKW5DWPd5sgNffL3uxdDe+6GdnJh3AYPhwnBGRY5urfvfcbRtWIvvpz+KxkL9FeBB6MZewLUNwug==} + engines: {node: '>=4'} + dev: false + + /chai@4.3.6: resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==} engines: {node: '>=4'} dependencies: @@ -607,7 +648,7 @@ packages: type-detect: 4.0.8 dev: false - /chalk/2.4.2: + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} dependencies: @@ -616,7 +657,7 @@ packages: supports-color: 5.5.0 dev: false - /chalk/4.1.2: + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} dependencies: @@ -624,16 +665,16 @@ packages: supports-color: 7.2.0 dev: false - /chalk/5.0.1: + /chalk@5.0.1: resolution: {integrity: sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} dev: false - /check-error/1.0.2: + /check-error@1.0.2: resolution: {integrity: sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=} dev: false - /chokidar/3.5.3: + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} dependencies: @@ -648,11 +689,11 @@ packages: fsevents: 2.3.2 dev: false - /ci-info/3.3.2: + /ci-info@3.3.2: resolution: {integrity: sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==} dev: false - /classic-level/1.2.0: + /classic-level@1.2.0: resolution: {integrity: sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg==} engines: {node: '>=12'} requiresBuild: true @@ -664,19 +705,19 @@ packages: node-gyp-build: 4.4.0 dev: false - /cli-cursor/4.0.0: + /cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: restore-cursor: 4.0.0 dev: false - /cli-spinners/2.6.1: + /cli-spinners@2.6.1: resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} engines: {node: '>=6'} dev: false - /cliui/7.0.4: + /cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} dependencies: string-width: 4.2.3 @@ -684,44 +725,44 @@ packages: wrap-ansi: 7.0.0 dev: false - /clone/1.0.4: + /clone@1.0.4: resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=} engines: {node: '>=0.8'} dev: false - /color-convert/1.9.3: + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 dev: false - /color-convert/2.0.1: + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 dev: false - /color-name/1.1.3: + /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} dev: false - /color-name/1.1.4: + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: false - /combined-stream/1.0.8: + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 dev: false - /concat-map/0.0.1: + /concat-map@0.0.1: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} dev: false - /concurrently/7.1.0: + /concurrently@7.1.0: resolution: {integrity: sha512-Bz0tMlYKZRUDqJlNiF/OImojMB9ruKUz6GCfmhFnSapXgPe+3xzY4byqoKG9tUZ7L2PGEUjfLPOLfIX3labnmw==} engines: {node: ^12.20.0 || ^14.13.0 || >=16.0.0} hasBin: true @@ -736,11 +777,11 @@ packages: yargs: 16.2.0 dev: false - /confusing-browser-globals/1.0.11: + /confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} dev: false - /cross-spawn/7.0.3: + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} dependencies: @@ -749,12 +790,12 @@ packages: which: 2.0.2 dev: false - /date-fns/2.28.0: + /date-fns@2.28.0: resolution: {integrity: sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==} engines: {node: '>=0.11'} dev: false - /debug/2.6.9: + /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: supports-color: '*' @@ -765,7 +806,7 @@ packages: ms: 2.0.0 dev: false - /debug/3.1.0: + /debug@3.1.0: resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} peerDependencies: supports-color: '*' @@ -776,7 +817,7 @@ packages: ms: 2.0.0 dev: false - /debug/3.2.7: + /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: supports-color: '*' @@ -787,19 +828,7 @@ packages: ms: 2.1.3 dev: false - /debug/4.3.3: - resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - dev: false - - /debug/4.3.3_supports-color@8.1.1: + /debug@4.3.3(supports-color@8.1.1): resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} engines: {node: '>=6.0'} peerDependencies: @@ -812,74 +841,74 @@ packages: supports-color: 8.1.1 dev: false - /decamelize/4.0.0: + /decamelize@4.0.0: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} engines: {node: '>=10'} dev: false - /deep-eql/3.0.1: + /deep-eql@3.0.1: resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==} engines: {node: '>=0.12'} dependencies: type-detect: 4.0.8 dev: false - /deep-is/0.1.4: + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: false - /defaults/1.0.3: + /defaults@1.0.3: resolution: {integrity: sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=} dependencies: clone: 1.0.4 dev: false - /define-properties/1.1.3: + /define-properties@1.1.3: resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==} engines: {node: '>= 0.4'} dependencies: object-keys: 1.1.1 dev: false - /delayed-stream/1.0.0: + /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} dev: false - /diff-sequences/27.5.1: + /diff-sequences@27.5.1: resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dev: false - /diff/5.0.0: + /diff@5.0.0: resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} engines: {node: '>=0.3.1'} dev: false - /doctrine/2.1.0: + /doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} dependencies: esutils: 2.0.3 dev: false - /doctrine/3.0.0: + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} dependencies: esutils: 2.0.3 dev: false - /dotenv/16.0.3: + /dotenv@16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} dev: true - /emoji-regex/8.0.0: + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: false - /es-abstract/1.19.1: + /es-abstract@1.19.1: resolution: {integrity: sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==} engines: {node: '>= 0.4'} dependencies: @@ -905,7 +934,7 @@ packages: unbox-primitive: 1.0.1 dev: false - /es-to-primitive/1.2.1: + /es-to-primitive@1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} dependencies: @@ -914,27 +943,27 @@ packages: is-symbol: 1.0.4 dev: false - /escalade/3.1.1: + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} dev: false - /escape-string-regexp/1.0.5: + /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} dev: false - /escape-string-regexp/2.0.0: + /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} dev: false - /escape-string-regexp/4.0.0: + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} dev: false - /eslint-config-airbnb-base/15.0.0_rnagsyfcubvpoxo2ynj23pim7u: + /eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.25.4)(eslint@8.10.0): resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -943,13 +972,13 @@ packages: dependencies: confusing-browser-globals: 1.0.11 eslint: 8.10.0 - eslint-plugin-import: 2.25.4_eslint@8.10.0 + eslint-plugin-import: 2.25.4(eslint@8.10.0) object.assign: 4.1.2 object.entries: 1.1.5 semver: 6.3.0 dev: false - /eslint-import-resolver-node/0.3.6: + /eslint-import-resolver-node@0.3.6: resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} dependencies: debug: 3.2.7 @@ -958,7 +987,7 @@ packages: - supports-color dev: false - /eslint-module-utils/2.7.3_ulu2225r2ychl26a37c6o2rfje: + /eslint-module-utils@2.7.3(eslint-import-resolver-node@0.3.6): resolution: {integrity: sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==} engines: {node: '>=4'} peerDependencies: @@ -983,7 +1012,7 @@ packages: - supports-color dev: false - /eslint-plugin-import/2.25.4_eslint@8.10.0: + /eslint-plugin-import@2.25.4(eslint@8.10.0): resolution: {integrity: sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==} engines: {node: '>=4'} peerDependencies: @@ -999,7 +1028,7 @@ packages: doctrine: 2.1.0 eslint: 8.10.0 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.3_ulu2225r2ychl26a37c6o2rfje + eslint-module-utils: 2.7.3(eslint-import-resolver-node@0.3.6) has: 1.0.3 is-core-module: 2.8.1 is-glob: 4.0.3 @@ -1013,7 +1042,7 @@ packages: - supports-color dev: false - /eslint-scope/7.1.1: + /eslint-scope@7.1.1: resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -1021,7 +1050,7 @@ packages: estraverse: 5.3.0 dev: false - /eslint-utils/3.0.0_eslint@8.10.0: + /eslint-utils@3.0.0(eslint@8.10.0): resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: @@ -1031,17 +1060,17 @@ packages: eslint-visitor-keys: 2.1.0 dev: false - /eslint-visitor-keys/2.1.0: + /eslint-visitor-keys@2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} dev: false - /eslint-visitor-keys/3.3.0: + /eslint-visitor-keys@3.3.0: resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false - /eslint/8.10.0: + /eslint@8.10.0: resolution: {integrity: sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true @@ -1051,11 +1080,11 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.3 + debug: 4.3.3(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.10.0 + eslint-utils: 3.0.0(eslint@8.10.0) eslint-visitor-keys: 3.3.0 espree: 9.3.1 esquery: 1.4.0 @@ -1085,40 +1114,40 @@ packages: - supports-color dev: false - /espree/9.3.1: + /espree@9.3.1: resolution: {integrity: sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: acorn: 8.7.0 - acorn-jsx: 5.3.2_acorn@8.7.0 + acorn-jsx: 5.3.2(acorn@8.7.0) eslint-visitor-keys: 3.3.0 dev: false - /esquery/1.4.0: + /esquery@1.4.0: resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} engines: {node: '>=0.10'} dependencies: estraverse: 5.3.0 dev: false - /esrecurse/4.3.0: + /esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} dependencies: estraverse: 5.3.0 dev: false - /estraverse/5.3.0: + /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} dev: false - /esutils/2.0.3: + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} dev: false - /expect/27.5.1: + /expect@27.5.1: resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -1128,40 +1157,40 @@ packages: jest-message-util: 27.5.1 dev: false - /fast-deep-equal/3.1.3: + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: false - /fast-json-stable-stringify/2.1.0: + /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} dev: false - /fast-levenshtein/2.0.6: + /fast-levenshtein@2.0.6: resolution: {integrity: sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=} dev: false - /file-entry-cache/6.0.1: + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: flat-cache: 3.0.4 dev: false - /fill-range/7.0.1: + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 dev: false - /find-up/2.1.0: + /find-up@2.1.0: resolution: {integrity: sha1-RdG35QbHF93UgndaK3eSCjwMV6c=} engines: {node: '>=4'} dependencies: locate-path: 2.0.0 dev: false - /find-up/5.0.0: + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} dependencies: @@ -1169,7 +1198,7 @@ packages: path-exists: 4.0.0 dev: false - /flat-cache/3.0.4: + /flat-cache@3.0.4: resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: @@ -1177,16 +1206,16 @@ packages: rimraf: 3.0.2 dev: false - /flat/5.0.2: + /flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true dev: false - /flatted/3.2.5: + /flatted@3.2.5: resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==} dev: false - /follow-redirects/1.14.9: + /follow-redirects@1.14.9: resolution: {integrity: sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==} engines: {node: '>=4.0'} peerDependencies: @@ -1196,7 +1225,7 @@ packages: optional: true dev: false - /follow-redirects/1.5.10: + /follow-redirects@1.5.10: resolution: {integrity: sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==} engines: {node: '>=4.0'} dependencies: @@ -1205,7 +1234,7 @@ packages: - supports-color dev: false - /form-data/3.0.1: + /form-data@3.0.1: resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} engines: {node: '>= 6'} dependencies: @@ -1214,11 +1243,11 @@ packages: mime-types: 2.1.34 dev: false - /fs.realpath/1.0.0: + /fs.realpath@1.0.0: resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} dev: false - /fsevents/2.3.2: + /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] @@ -1226,24 +1255,24 @@ packages: dev: false optional: true - /function-bind/1.1.1: + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: false - /functional-red-black-tree/1.0.1: + /functional-red-black-tree@1.0.1: resolution: {integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=} dev: false - /get-caller-file/2.0.5: + /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} dev: false - /get-func-name/2.0.0: + /get-func-name@2.0.0: resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=} dev: false - /get-intrinsic/1.1.1: + /get-intrinsic@1.1.1: resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} dependencies: function-bind: 1.1.1 @@ -1251,7 +1280,7 @@ packages: has-symbols: 1.0.2 dev: false - /get-symbol-description/1.0.0: + /get-symbol-description@1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} dependencies: @@ -1259,21 +1288,21 @@ packages: get-intrinsic: 1.1.1 dev: false - /glob-parent/5.1.2: + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 dev: false - /glob-parent/6.0.2: + /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} dependencies: is-glob: 4.0.3 dev: false - /glob/7.2.0: + /glob@7.2.0: resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} dependencies: fs.realpath: 1.0.0 @@ -1284,75 +1313,75 @@ packages: path-is-absolute: 1.0.1 dev: false - /globals/13.12.1: + /globals@13.12.1: resolution: {integrity: sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 dev: false - /graceful-fs/4.2.10: + /graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} dev: false - /growl/1.10.5: + /growl@1.10.5: resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==} engines: {node: '>=4.x'} dev: false - /has-bigints/1.0.1: + /has-bigints@1.0.1: resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==} dev: false - /has-flag/3.0.0: + /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} dev: false - /has-flag/4.0.0: + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} dev: false - /has-symbols/1.0.2: + /has-symbols@1.0.2: resolution: {integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==} engines: {node: '>= 0.4'} dev: false - /has-tostringtag/1.0.0: + /has-tostringtag@1.0.0: resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.2 dev: false - /has/1.0.3: + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 dev: false - /he/1.2.0: + /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true dev: false - /ieee754/1.2.1: + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: false - /ignore/4.0.6: + /ignore@4.0.6: resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} engines: {node: '>= 4'} dev: false - /ignore/5.2.0: + /ignore@5.2.0: resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} engines: {node: '>= 4'} dev: false - /import-fresh/3.3.0: + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} dependencies: @@ -1360,23 +1389,23 @@ packages: resolve-from: 4.0.0 dev: false - /imurmurhash/0.1.4: + /imurmurhash@0.1.4: resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} engines: {node: '>=0.8.19'} dev: false - /inflight/1.0.6: + /inflight@1.0.6: resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} dependencies: once: 1.4.0 wrappy: 1.0.2 dev: false - /inherits/2.0.4: + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: false - /internal-slot/1.0.3: + /internal-slot@1.0.3: resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} engines: {node: '>= 0.4'} dependencies: @@ -1385,20 +1414,20 @@ packages: side-channel: 1.0.4 dev: false - /is-bigint/1.0.4: + /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: has-bigints: 1.0.1 dev: false - /is-binary-path/2.1.0: + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: binary-extensions: 2.2.0 dev: false - /is-boolean-object/1.1.2: + /is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} dependencies: @@ -1406,74 +1435,74 @@ packages: has-tostringtag: 1.0.0 dev: false - /is-buffer/2.0.5: + /is-buffer@2.0.5: resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} engines: {node: '>=4'} dev: false - /is-callable/1.2.4: + /is-callable@1.2.4: resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==} engines: {node: '>= 0.4'} dev: false - /is-core-module/2.8.1: + /is-core-module@2.8.1: resolution: {integrity: sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==} dependencies: has: 1.0.3 dev: false - /is-date-object/1.0.5: + /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 dev: false - /is-extglob/2.1.1: + /is-extglob@2.1.1: resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} engines: {node: '>=0.10.0'} dev: false - /is-fullwidth-code-point/3.0.0: + /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} dev: false - /is-glob/4.0.3: + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 dev: false - /is-interactive/2.0.0: + /is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} dev: false - /is-negative-zero/2.0.2: + /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} dev: false - /is-number-object/1.0.6: + /is-number-object@1.0.6: resolution: {integrity: sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 dev: false - /is-number/7.0.0: + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} dev: false - /is-plain-obj/2.1.0: + /is-plain-obj@2.1.0: resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} engines: {node: '>=8'} dev: false - /is-regex/1.1.4: + /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} dependencies: @@ -1481,45 +1510,45 @@ packages: has-tostringtag: 1.0.0 dev: false - /is-shared-array-buffer/1.0.1: + /is-shared-array-buffer@1.0.1: resolution: {integrity: sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==} dev: false - /is-string/1.0.7: + /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 dev: false - /is-symbol/1.0.4: + /is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.2 dev: false - /is-unicode-supported/0.1.0: + /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} dev: false - /is-unicode-supported/1.2.0: + /is-unicode-supported@1.2.0: resolution: {integrity: sha512-wH+U77omcRzevfIG8dDhTS0V9zZyweakfD01FULl97+0EHiJTTZtJqxPSkIIo/SDPv/i07k/C9jAPY+jwLLeUQ==} engines: {node: '>=12'} dev: false - /is-weakref/1.0.2: + /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: call-bind: 1.0.2 dev: false - /isexe/2.0.0: + /isexe@2.0.0: resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=} dev: false - /jest-diff/27.5.1: + /jest-diff@27.5.1: resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -1529,12 +1558,12 @@ packages: pretty-format: 27.5.1 dev: false - /jest-get-type/27.5.1: + /jest-get-type@27.5.1: resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dev: false - /jest-matcher-utils/27.5.1: + /jest-matcher-utils@27.5.1: resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -1544,7 +1573,7 @@ packages: pretty-format: 27.5.1 dev: false - /jest-message-util/27.5.1: + /jest-message-util@27.5.1: resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -1559,7 +1588,7 @@ packages: stack-utils: 2.0.5 dev: false - /jest-mock/27.5.1: + /jest-mock@27.5.1: resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -1567,7 +1596,7 @@ packages: '@types/node': 18.0.1 dev: false - /jest-util/27.5.1: + /jest-util@27.5.1: resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -1579,51 +1608,51 @@ packages: picomatch: 2.3.1 dev: false - /js-sha3/0.8.0: + /js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} dev: false - /js-tokens/4.0.0: + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: false - /js-yaml/4.1.0: + /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true dependencies: argparse: 2.0.1 dev: false - /json-schema-traverse/0.4.1: + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: false - /json-schema-traverse/1.0.0: + /json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} dev: false - /json-stable-stringify-without-jsonify/1.0.1: + /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=} dev: false - /json5/1.0.1: + /json5@1.0.1: resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} hasBin: true dependencies: minimist: 1.2.5 dev: false - /kleur/3.0.3: + /kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} dev: false - /level-supports/4.0.1: + /level-supports@4.0.1: resolution: {integrity: sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==} engines: {node: '>=12'} dev: false - /level-transcoder/1.0.1: + /level-transcoder@1.0.1: resolution: {integrity: sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==} engines: {node: '>=12'} dependencies: @@ -1631,7 +1660,7 @@ packages: module-error: 1.0.2 dev: false - /level/8.0.0: + /level@8.0.0: resolution: {integrity: sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==} engines: {node: '>=12'} dependencies: @@ -1639,7 +1668,7 @@ packages: classic-level: 1.2.0 dev: false - /levn/0.4.1: + /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} dependencies: @@ -1647,7 +1676,7 @@ packages: type-check: 0.4.0 dev: false - /locate-path/2.0.0: + /locate-path@2.0.0: resolution: {integrity: sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=} engines: {node: '>=4'} dependencies: @@ -1655,26 +1684,26 @@ packages: path-exists: 3.0.0 dev: false - /locate-path/6.0.0: + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} dependencies: p-locate: 5.0.0 dev: false - /lodash.merge/4.6.2: + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: false - /lodash.truncate/4.4.2: + /lodash.truncate@4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} dev: false - /lodash/4.17.21: + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: false - /log-symbols/4.1.0: + /log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} dependencies: @@ -1682,7 +1711,7 @@ packages: is-unicode-supported: 0.1.0 dev: false - /log-symbols/5.1.0: + /log-symbols@5.1.0: resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} engines: {node: '>=12'} dependencies: @@ -1690,17 +1719,17 @@ packages: is-unicode-supported: 1.2.0 dev: false - /long/4.0.0: + /long@4.0.0: resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} dev: false - /loupe/2.3.4: + /loupe@2.3.4: resolution: {integrity: sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==} dependencies: get-func-name: 2.0.0 dev: false - /micromatch/4.0.5: + /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} dependencies: @@ -1708,40 +1737,40 @@ packages: picomatch: 2.3.1 dev: false - /mime-db/1.51.0: + /mime-db@1.51.0: resolution: {integrity: sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==} engines: {node: '>= 0.6'} dev: false - /mime-types/2.1.34: + /mime-types@2.1.34: resolution: {integrity: sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==} engines: {node: '>= 0.6'} dependencies: mime-db: 1.51.0 dev: false - /mimic-fn/2.1.0: + /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} dev: false - /minimatch/3.0.4: + /minimatch@3.0.4: resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} dependencies: brace-expansion: 1.1.11 dev: false - /minimatch/3.1.2: + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: false - /minimist/1.2.5: + /minimist@1.2.5: resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} dev: false - /mocha/9.2.1: + /mocha@9.2.1: resolution: {integrity: sha512-T7uscqjJVS46Pq1XDXyo9Uvey9gd3huT/DD9cYBb4K2Xc/vbKRPUWK067bxDQRK0yIz6Jxk73IrnimvASzBNAQ==} engines: {node: '>= 12.0.0'} hasBin: true @@ -1750,7 +1779,7 @@ packages: ansi-colors: 4.1.1 browser-stdout: 1.3.1 chokidar: 3.5.3 - debug: 4.3.3_supports-color@8.1.1 + debug: 4.3.3(supports-color@8.1.1) diff: 5.0.0 escape-string-regexp: 4.0.0 find-up: 5.0.0 @@ -1772,38 +1801,38 @@ packages: yargs-unparser: 2.0.0 dev: false - /module-error/1.0.2: + /module-error@1.0.2: resolution: {integrity: sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==} engines: {node: '>=10'} dev: false - /ms/2.0.0: + /ms@2.0.0: resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} dev: false - /ms/2.1.2: + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: false - /ms/2.1.3: + /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: false - /nanoid/3.2.0: + /nanoid@3.2.0: resolution: {integrity: sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: false - /napi-macros/2.0.0: + /napi-macros@2.0.0: resolution: {integrity: sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==} dev: false - /natural-compare/1.4.0: + /natural-compare@1.4.0: resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} dev: false - /node-fetch/2.6.7: + /node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} peerDependencies: @@ -1815,36 +1844,36 @@ packages: whatwg-url: 5.0.0 dev: false - /node-forge/0.10.0: + /node-forge@0.10.0: resolution: {integrity: sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==} engines: {node: '>= 6.0.0'} dev: false - /node-forge/0.8.5: + /node-forge@0.8.5: resolution: {integrity: sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==} engines: {node: '>= 4.5.0'} dev: false - /node-gyp-build/4.4.0: + /node-gyp-build@4.4.0: resolution: {integrity: sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==} hasBin: true dev: false - /normalize-path/3.0.0: + /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} dev: false - /object-inspect/1.12.0: + /object-inspect@1.12.0: resolution: {integrity: sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==} dev: false - /object-keys/1.1.1: + /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} dev: false - /object.assign/4.1.2: + /object.assign@4.1.2: resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} engines: {node: '>= 0.4'} dependencies: @@ -1854,7 +1883,7 @@ packages: object-keys: 1.1.1 dev: false - /object.entries/1.1.5: + /object.entries@1.1.5: resolution: {integrity: sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==} engines: {node: '>= 0.4'} dependencies: @@ -1863,7 +1892,7 @@ packages: es-abstract: 1.19.1 dev: false - /object.values/1.1.5: + /object.values@1.1.5: resolution: {integrity: sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==} engines: {node: '>= 0.4'} dependencies: @@ -1872,20 +1901,20 @@ packages: es-abstract: 1.19.1 dev: false - /once/1.4.0: + /once@1.4.0: resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} dependencies: wrappy: 1.0.2 dev: false - /onetime/5.1.2: + /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 dev: false - /optionator/0.9.1: + /optionator@0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} dependencies: @@ -1897,7 +1926,7 @@ packages: word-wrap: 1.2.3 dev: false - /ora/6.1.0: + /ora@6.1.0: resolution: {integrity: sha512-CxEP6845hLK+NHFWZ+LplGO4zfw4QSfxTlqMfvlJ988GoiUeZDMzCvqsZkFHv69sPICmJH1MDxZoQFOKXerAVw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: @@ -1912,85 +1941,85 @@ packages: wcwidth: 1.0.1 dev: false - /p-limit/1.3.0: + /p-limit@1.3.0: resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} engines: {node: '>=4'} dependencies: p-try: 1.0.0 dev: false - /p-limit/3.1.0: + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 dev: false - /p-locate/2.0.0: + /p-locate@2.0.0: resolution: {integrity: sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=} engines: {node: '>=4'} dependencies: p-limit: 1.3.0 dev: false - /p-locate/5.0.0: + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} dependencies: p-limit: 3.1.0 dev: false - /p-try/1.0.0: + /p-try@1.0.0: resolution: {integrity: sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=} engines: {node: '>=4'} dev: false - /parent-module/1.0.1: + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} dependencies: callsites: 3.1.0 dev: false - /path-exists/3.0.0: + /path-exists@3.0.0: resolution: {integrity: sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=} engines: {node: '>=4'} dev: false - /path-exists/4.0.0: + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} dev: false - /path-is-absolute/1.0.1: + /path-is-absolute@1.0.1: resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} engines: {node: '>=0.10.0'} dev: false - /path-key/3.1.1: + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} dev: false - /path-parse/1.0.7: + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: false - /pathval/1.1.1: + /pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: false - /picomatch/2.3.1: + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} dev: false - /prelude-ls/1.2.1: + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} dev: false - /pretty-format/27.5.1: + /pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: @@ -1999,7 +2028,7 @@ packages: react-is: 17.0.2 dev: false - /prompts/2.4.2: + /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} dependencies: @@ -2007,7 +2036,7 @@ packages: sisteransi: 1.0.5 dev: false - /protobufjs/6.11.2: + /protobufjs@6.11.2: resolution: {integrity: sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==} hasBin: true requiresBuild: true @@ -2027,26 +2056,26 @@ packages: long: 4.0.0 dev: false - /punycode/2.1.1: + /punycode@2.1.1: resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} engines: {node: '>=6'} dev: false - /queue-microtask/1.2.3: + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: false - /randombytes/2.1.0: + /randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: safe-buffer: 5.2.1 dev: false - /react-is/17.0.2: + /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: false - /readable-stream/3.6.0: + /readable-stream@3.6.0: resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} engines: {node: '>= 6'} dependencies: @@ -2055,34 +2084,34 @@ packages: util-deprecate: 1.0.2 dev: false - /readdirp/3.6.0: + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 dev: false - /regexpp/3.2.0: + /regexpp@3.2.0: resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} engines: {node: '>=8'} dev: false - /require-directory/2.1.1: + /require-directory@2.1.1: resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} engines: {node: '>=0.10.0'} dev: false - /require-from-string/2.0.2: + /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} dev: false - /resolve-from/4.0.0: + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} dev: false - /resolve/1.22.0: + /resolve@1.22.0: resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} hasBin: true dependencies: @@ -2091,7 +2120,7 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: false - /restore-cursor/4.0.0: + /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: @@ -2099,54 +2128,54 @@ packages: signal-exit: 3.0.7 dev: false - /rimraf/3.0.2: + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: glob: 7.2.0 dev: false - /run-parallel-limit/1.1.0: + /run-parallel-limit@1.1.0: resolution: {integrity: sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==} dependencies: queue-microtask: 1.2.3 dev: false - /rxjs/6.6.7: + /rxjs@6.6.7: resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} engines: {npm: '>=2.0.0'} dependencies: tslib: 1.14.1 dev: false - /safe-buffer/5.2.1: + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: false - /semver/6.3.0: + /semver@6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true dev: false - /serialize-javascript/6.0.0: + /serialize-javascript@6.0.0: resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} dependencies: randombytes: 2.1.0 dev: false - /shebang-command/2.0.0: + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 dev: false - /shebang-regex/3.0.0: + /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} dev: false - /side-channel/1.0.4: + /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: call-bind: 1.0.2 @@ -2154,20 +2183,20 @@ packages: object-inspect: 1.12.0 dev: false - /signal-exit/3.0.7: + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: false - /sisteransi/1.0.5: + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: false - /slash/3.0.0: + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} dev: false - /slice-ansi/4.0.0: + /slice-ansi@4.0.0: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} dependencies: @@ -2176,18 +2205,18 @@ packages: is-fullwidth-code-point: 3.0.0 dev: false - /spawn-command/0.0.2-1: + /spawn-command@0.0.2-1: resolution: {integrity: sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=} dev: false - /stack-utils/2.0.5: + /stack-utils@2.0.5: resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==} engines: {node: '>=10'} dependencies: escape-string-regexp: 2.0.0 dev: false - /string-width/4.2.3: + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} dependencies: @@ -2196,77 +2225,77 @@ packages: strip-ansi: 6.0.1 dev: false - /string.prototype.trimend/1.0.4: + /string.prototype.trimend@1.0.4: resolution: {integrity: sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==} dependencies: call-bind: 1.0.2 define-properties: 1.1.3 dev: false - /string.prototype.trimstart/1.0.4: + /string.prototype.trimstart@1.0.4: resolution: {integrity: sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==} dependencies: call-bind: 1.0.2 define-properties: 1.1.3 dev: false - /string_decoder/1.3.0: + /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 dev: false - /strip-ansi/6.0.1: + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 dev: false - /strip-ansi/7.0.1: + /strip-ansi@7.0.1: resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 dev: false - /strip-bom/3.0.0: + /strip-bom@3.0.0: resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} engines: {node: '>=4'} dev: false - /strip-json-comments/3.1.1: + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} dev: false - /supports-color/5.5.0: + /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} dependencies: has-flag: 3.0.0 dev: false - /supports-color/7.2.0: + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 dev: false - /supports-color/8.1.1: + /supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} dependencies: has-flag: 4.0.0 dev: false - /supports-preserve-symlinks-flag/1.0.0: + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: false - /table/6.8.0: + /table@6.8.0: resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==} engines: {node: '>=10.0.0'} dependencies: @@ -2277,27 +2306,27 @@ packages: strip-ansi: 6.0.1 dev: false - /text-table/0.2.0: + /text-table@0.2.0: resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=} dev: false - /to-regex-range/5.0.1: + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 dev: false - /tr46/0.0.3: + /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: false - /tree-kill/1.2.2: + /tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true dev: false - /tsconfig-paths/3.12.0: + /tsconfig-paths@3.12.0: resolution: {integrity: sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==} dependencies: '@types/json5': 0.0.29 @@ -2306,32 +2335,32 @@ packages: strip-bom: 3.0.0 dev: false - /tslib/1.14.1: + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: false - /type-check/0.4.0: + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} dependencies: prelude-ls: 1.2.1 dev: false - /type-detect/4.0.8: + /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} dev: false - /type-fest/0.20.2: + /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} dev: false - /typed-ts-events/1.2.1: + /typed-ts-events@1.2.1: resolution: {integrity: sha512-+Fy9cqWA/Kv1QX0k6m5ZflGcG2jQSZQGr+jLGXYUM22yihhkHs243LEXvY4cs54lAVyj5gokm0TbgkmL4qDsTg==} dev: false - /unbox-primitive/1.0.1: + /unbox-primitive@1.0.1: resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==} dependencies: function-bind: 1.1.1 @@ -2340,38 +2369,38 @@ packages: which-boxed-primitive: 1.0.2 dev: false - /uri-js/4.4.1: + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.1.1 dev: false - /util-deprecate/1.0.2: + /util-deprecate@1.0.2: resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} dev: false - /v8-compile-cache/2.3.0: + /v8-compile-cache@2.3.0: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} dev: false - /wcwidth/1.0.1: + /wcwidth@1.0.1: resolution: {integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=} dependencies: defaults: 1.0.3 dev: false - /webidl-conversions/3.0.1: + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: false - /whatwg-url/5.0.0: + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 dev: false - /which-boxed-primitive/1.0.2: + /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: is-bigint: 1.0.4 @@ -2381,7 +2410,7 @@ packages: is-symbol: 1.0.4 dev: false - /which/2.0.2: + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true @@ -2389,16 +2418,16 @@ packages: isexe: 2.0.0 dev: false - /word-wrap/1.2.3: + /word-wrap@1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} dev: false - /workerpool/6.2.0: + /workerpool@6.2.0: resolution: {integrity: sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==} dev: false - /wrap-ansi/7.0.0: + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} dependencies: @@ -2407,21 +2436,21 @@ packages: strip-ansi: 6.0.1 dev: false - /wrappy/1.0.2: + /wrappy@1.0.2: resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} dev: false - /y18n/5.0.8: + /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} dev: false - /yargs-parser/20.2.4: + /yargs-parser@20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} engines: {node: '>=10'} dev: false - /yargs-unparser/2.0.0: + /yargs-unparser@2.0.0: resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} engines: {node: '>=10'} dependencies: @@ -2431,7 +2460,7 @@ packages: is-plain-obj: 2.1.0 dev: false - /yargs/16.2.0: + /yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} dependencies: @@ -2444,7 +2473,7 @@ packages: yargs-parser: 20.2.4 dev: false - /yocto-queue/0.1.0: + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: false diff --git a/test/utils/utils.mjs b/test/utils/utils.mjs index cff8f54a..dbdd2491 100644 --- a/test/utils/utils.mjs +++ b/test/utils/utils.mjs @@ -13,11 +13,11 @@ export const compileScript = ( script, transform = null, libraries = {}, + compact = false, ) => { let transformFunc = transform; if (!transform) transformFunc = (x) => x; const estimatorVersion = undefined; - const compact = false; const removeUnused = false; const compilation = ride.compile( transformFunc(script), @@ -37,9 +37,10 @@ export const compileScriptFromFile = async ( path, transform = null, libraries = {}, + compact = false, ) => { const script = await readFile(path, { encoding: 'utf-8' }); - const result = compileScript(script, transform, libraries); + const result = compileScript(script, transform, libraries, compact); return result; };