private static bool SetAssetSettings(byte[] assetId, byte[] settings)
        {
            if (VerifyOwner() == false || (assetId.Length != 20 && assetId.Length != 32))
            {
                return(false);
            }

            // Byte 0 (First byte): Transfer type to use. If not set, or set to 0, it will use NEP5 'transfer'
            // Byte 1-8: Withdraw fee in APH satoshis.
            Storage.Put(Storage.CurrentContext, PREFIX_ASSET_SETTINGS.Concat(assetId), settings);
            Runtime.Notify("setAssetSettings", settings);
            return(true);
        }
        private static bool SendNep5(byte[] toAddress, byte[] assetId, BigInteger amount)
        {
            byte[] assetSettings = Storage.Get(Storage.CurrentContext, PREFIX_ASSET_SETTINGS.Concat(assetId));
            if (assetSettings.Length == 0)
            {
                Runtime.Notify("sendNep5Fail", "Invalid Asset", toAddress, assetId, amount);
                return(false);
            }

            if (Nep5Transfer(assetId, ExecutionEngine.ExecutingScriptHash, toAddress, "transfer", amount) == false)
            {
                return(false);
            }

            AdjustTotalUserDexBalance(assetId, 0 - amount);

            // Runtime.Notify("SendNEP5Token() succeeded", assetId, toAddress, amount);
            return(true);
        }
        private static bool PullNep5(byte[] assetId, byte[] fromAddress, BigInteger amountToPull)
        {
            byte[] assetSettings = Storage.Get(Storage.CurrentContext, PREFIX_ASSET_SETTINGS.Concat(assetId));
            if (assetSettings.Length == 0)
            {
                Runtime.Notify("pullNep5Fail", "Invalid Asset", fromAddress, assetId, amountToPull);
                return(false);
            }
            var transferMethod = assetSettings.Take(1) == USER_ASSET_TYPE_NEP5_EXTENSIONS
                ? "transferFrom" : "transfer";

            if (Nep5Transfer(assetId, fromAddress, ExecutionEngine.ExecutingScriptHash, transferMethod, amountToPull) == false)
            {
                return(false);
            }

            AdjustTotalUserDexBalance(assetId, amountToPull);

            //Runtime.Notify("PullNEP5Token() succeeded", assetId, fromAddress, amountToPull);
            return(true);
        }
        private static bool ExecuteWithdraw()
        {
            Transaction tx         = (Transaction)ExecutionEngine.ScriptContainer;
            var         attributes = tx.GetAttributes();
            var         step       = EMPTY;
            var         toAddress  = EMPTY;
            var         assetId    = EMPTY;
            BigInteger  amount     = 0;

            // var step = ReadSignatureRequestType(tx);
            // var toAddress = ReadWithdrawToAddress(tx);
            // var assetId = ReadWithdrawAssetId(tx);
            // Use 1 loop to save gas.
            foreach (var attr in attributes)
            {
                if (attr.Usage == AttributeUsage_SignatureRequestType)
                {
                    step = attr.Data.Take(1);
                }
                else if (attr.Usage == AttributeUsage_WithdrawAddress)
                {
                    toAddress = attr.Data.Take(20);
                }
                else if (attr.Usage == AttributeUsage_WithdrawSystemAssetId)
                {
                    assetId = attr.Data.Take(32);
                }
                else if (attr.Usage == AttributeUsage_WithdrawNEP5AssetId)
                {
                    assetId = attr.Data.Take(20);
                }
                else if (attr.Usage == AttributeUsage_WithdrawAmount)
                {
                    amount = attr.Data.Take(8).AsBigInteger();
                }
            }

            object[] notifyArgs;

            if (step == SignatureRequestTypeWithdrawStepWithdraw)
            {
                if (assetId.Length == 20)
                {
                    byte[]     userAssetBalanceKey = toAddress.Concat(assetId);
                    BigInteger balance;
                    bool       isOwnerWithdrawingAph = false;
                    if (assetId == APH && toAddress == GetOwner())
                    {
                        isOwnerWithdrawingAph = true;
                        byte[] poolData = Storage.Get(Storage.CurrentContext, FEES_POOL_KEY);
                        balance = poolData.Range(8, 8).AsBigInteger();
                    }
                    else
                    {
                        balance = Storage.Get(Storage.CurrentContext, userAssetBalanceKey).AsBigInteger();
                    }

                    if (balance < amount)
                    {
                        notifyArgs = new object[] { null, "Withdraw NEP5 insufficent balance", toAddress, balance, amount };
                        goto NotifyWithdrawFail;
                    }

                    var transferSucceeded = SendNep5(toAddress, assetId, amount);
                    if (transferSucceeded == false)
                    {
                        notifyArgs = new object[] { null, "executeWithdraw NEP5 Transfer failed", toAddress, assetId, amount };
                        goto NotifyWithdrawFail;
                    }

                    balance -= amount;
                    if (isOwnerWithdrawingAph)
                    {
                        SetBalanceOfForWithdrawal(assetId, toAddress, balance);
                    }
                    else
                    {
                        if (balance <= 0)
                        {
                            Storage.Delete(Storage.CurrentContext, userAssetBalanceKey);
                        }
                        else
                        {
                            Storage.Put(Storage.CurrentContext, userAssetBalanceKey, balance.AsByteArray());
                        }
                    }
                }
                else
                {
                    TransactionInput[] inputs = tx.GetInputs();
                    if (assetId.Length != 32)
                    {
                        notifyArgs = new object[] { null, "Mark assetId len != 32", toAddress };
                        goto NotifyWithdrawFail;
                    }

                    DeleteUserValue(toAddress, assetId.Concat(POSTFIX_USER_ASSET_WITHDRAWING));
                    // NOTE: Could verify the amount passed equals the amount from the user value, but not required since
                    //       the amount from the Runtime.Notify is not used for non-nep5 withdraws.

                    // Delete the marked UTXOs
                    foreach (var input in inputs)
                    {
                        // Note this includes inputs not from the contract; but we won't have anything in storage for them
                        // in that case, so it won't be an issue.
                        Storage.Delete(Storage.CurrentContext, input.PrevHash.Concat(ShortToByteArray(input.PrevIndex)));
                    }
                }

                Runtime.Notify("withdraw", toAddress, assetId, amount);
                return(true);
            }


            if (step == SignatureRequestTypeWithdrawStepMark)
            {
                // ExecuteMarkStep -- INLINE to save space
                TransactionOutput[] outputs = tx.GetOutputs();

                if (assetId.Length == 20)
                {
                    // NOTE: this is probably dead code because verify step wouldn't let it get this far.
                    notifyArgs = new object[] { null, "Can't mark NEP5", toAddress };
                    goto NotifyWithdrawFail;
                }

                if (assetId.Length != 32)
                {
                    notifyArgs = new object[] { null, "Mark assetId len != 32", toAddress };
                    goto NotifyWithdrawFail;
                }

                byte[] utxoAmountKey = toAddress.Concat(assetId).Concat(POSTFIX_USER_ASSET_WITHDRAWING);
                var    withdrawingUTXOAmountBytes = Storage.Get(Storage.CurrentContext, utxoAmountKey);

                if (withdrawingUTXOAmountBytes.Length > 0)
                {
                    notifyArgs = new object[] { null, "Already withdrawing", toAddress, withdrawingUTXOAmountBytes };
                    goto NotifyWithdrawFail;
                }

                var balance = GetBalanceOfForWithdrawal(assetId, toAddress);
                if (balance < amount)
                {
                    notifyArgs = new object[] { null, "Insufficient balance", toAddress, balance, amount };
                    goto NotifyWithdrawFail;
                }

                // Check if there is a withdraw fee imposed
                byte[] assetSettings = Storage.Get(Storage.CurrentContext, PREFIX_ASSET_SETTINGS.Concat(assetId));
                if (assetSettings.Length > 8)
                {
                    BigInteger aphFee = assetSettings.Range(1, 8).AsBigInteger();
                    if (aphFee > MAX_APH_WITHDRAW_FEE)
                    {
                        aphFee = MAX_APH_WITHDRAW_FEE;
                    }
                    if (aphFee > 0)
                    {
                        // Everyone other than owner has to pay withdraw fee if one applies.
                        if (toAddress != GetOwner() && NoPullReduceBalanceOf(APH, toAddress, aphFee) == false)
                        {
                            notifyArgs = new object[] { null, "Insufficient APH for withdraw Fee", toAddress, aphFee };
                            goto NotifyWithdrawFail;
                        }
                    }
                }

                balance -= amount;
                SetBalanceOfForWithdrawal(assetId, toAddress, balance);

                var reserved = SetOutputReservedFor(tx, outputs, amount, toAddress);
                if (reserved == false)
                {
                    notifyArgs = new object[] { null, "Mark Step, failed to reserve output UTXO", toAddress };
                    goto NotifyWithdrawFail;
                }

                // MarkForWithdraw
                AdjustTotalUserDexBalance(assetId, 0 - amount);

                // Save the balance being withdrawn for this user in storage so it can be read later
                Storage.Put(Storage.CurrentContext, utxoAmountKey, amount.ToByteArray());

                Runtime.Notify("withdrawMark", toAddress, assetId, amount);
                return(true);
            }

            Runtime.Notify("withdrawFailInvalidStep", step);
            return(false);

NotifyWithdrawFail:
            notifyArgs[0] = "withdrawFail";
            Runtime.Notify(notifyArgs);
            return(false);
        }
Example #5
0
        private static bool SetMarket(object[] args)
        {
            string failReason;

            if (args.Length != 6)
            {
                // Runtime.Notify(setMarketFailStr, "requires six parameters", args.Length);
                return(false);
            }

            var quoteAssetId = (byte[])args[0];
            var baseAssetId  = (byte[])args[1];

            if (quoteAssetId.Length != 32 && quoteAssetId.Length != 20 ||
                baseAssetId.Length != 32 && baseAssetId.Length != 20)
            {
                failReason = "bad asset len";
                goto SetMarketFail;
            }

            byte[] marketId = PREFIX_MARKETS.Concat(quoteAssetId).Concat(baseAssetId);

            Market market;

            if (VerifyManager() == false && VerifyOwner() == false)
            {
                byte[] existingMarket = Storage.Get(Storage.CurrentContext, marketId);
                if (existingMarket.Length == 0 || VerifyWhitelister() == false)
                {
                    failReason = "No permission";
                    goto SetMarketFail;
                }
                // Only make it here if the whitelister is calling with an already existing market.
                // We allow programatically changing the minimumSize, buyFee, and sellFee by the whitelister.
                market                = (Market)existingMarket.Deserialize();
                market.MinimumSize    = (BigInteger)args[2];
                market.BuyFeePercent  = (BigInteger)args[4];
                market.SellFeePercent = (BigInteger)args[5];
            }
            else
            {
                market = new Market();
                market.QuoteAssetId    = quoteAssetId;
                market.BaseAssetId     = baseAssetId;
                market.MinimumSize     = (BigInteger)args[2];
                market.MinimumTickSize = (BigInteger)args[3];
                market.BuyFeePercent   = (BigInteger)args[4];
                market.SellFeePercent  = (BigInteger)args[5];
            }

            if (market.BuyFeePercent < 0 || market.SellFeePercent < 0)
            {
                failReason = "Fee cannot be negative";
                goto SetMarketFail;
            }

            // Verify base asset and quote asset have asset settings set
            byte[] quoteAssetSettings = Storage.Get(Storage.CurrentContext, PREFIX_ASSET_SETTINGS.Concat(market.QuoteAssetId));
            byte[] baseAssetSettings  = Storage.Get(Storage.CurrentContext, PREFIX_ASSET_SETTINGS.Concat(market.BaseAssetId));
            if (quoteAssetSettings.Length == 0 || baseAssetSettings.Length == 0)
            {
                failReason = "Invalid Asset";
                goto SetMarketFail;
            }

            byte[] marketData = market.Serialize();
            Storage.Put(Storage.CurrentContext, marketId, marketData);
            Runtime.Notify("putMarket", marketId, marketData);
            Runtime.Notify("marketSet", marketId, market.QuoteAssetId, market.BaseAssetId, market.MinimumSize,
                           market.MinimumTickSize, market.BuyFeePercent, market.SellFeePercent);
            return(true);

SetMarketFail:
            Runtime.Notify("setMarketFail", failReason);
            return(false);
        }