예제 #1
0
        public void DepositTokens(Address from, string symbol, BigInteger amount)
        {
            Runtime.Expect(IsWitness(from), "invalid witness");

            var info = Runtime.Nexus.GetTokenInfo(symbol);

            Runtime.Expect(info.IsFungible, "must be fungible");

            var unitAmount = UnitConversion.GetUnitValue(info.Decimals);

            Runtime.Expect(amount >= unitAmount, "invalid amount");

            _total += amount;

            var balance = GetAvailable(symbol);

            balance += amount;
            _balances.Set <string, BigInteger>(symbol, balance);

            Runtime.Expect(Runtime.Nexus.TransferTokens(Runtime, symbol, from, Runtime.Chain.Address, amount), "tokens transfer failed");
            Runtime.Notify(EventKind.TokenSend, from, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = symbol, value = amount
            });
        }
예제 #2
0
        public void RegisterScript(Address target, byte[] script)
        {
            Runtime.Expect(target.IsUser, "must be user address");
            Runtime.Expect(target != Runtime.GenesisAddress, "address must not be genesis");
            Runtime.Expect(Runtime.IsWitness(target), "invalid witness");

            var stake = Runtime.GetStake(target);

            Runtime.Expect(stake >= UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals), "must have something staked");

            Runtime.Expect(script.Length < 1024, "invalid script length");

            Runtime.Expect(!_scriptMap.ContainsKey(target), "address already has a script");

            var witnessCheck = Runtime.InvokeTrigger(script, AccountTrigger.OnWitness.ToString(), target);

            Runtime.Expect(witnessCheck, "script does not handle OnWitness correctly, case #1");

            witnessCheck = Runtime.InvokeTrigger(script, AccountTrigger.OnWitness.ToString(), Address.Null);
            Runtime.Expect(!witnessCheck, "script does not handle OnWitness correctly, case #2");

            _scriptMap.Set(target, script);

            // TODO? Runtime.Notify(EventKind.AddressRegister, target, script);
        }
예제 #3
0
        public void SingleUploadSuccessMaxFileSize()
        {
            var owner = KeyPair.Generate();

            var simulator = new ChainSimulator(owner, 1234);
            var nexus     = simulator.Nexus;

            var testUser = KeyPair.Generate();

            BigInteger accountBalance = (Archive.MaxSize / 1024) / KilobytesPerStake;  //provide enough account balance for max file size available space

            accountBalance *= UnitConversion.GetUnitValue(Nexus.StakingTokenDecimals);

            Transaction tx = null;

            simulator.BeginBlock();
            simulator.GenerateTransfer(owner, testUser.Address, nexus.RootChain, Nexus.FuelTokenSymbol, 100000000);
            simulator.GenerateTransfer(owner, testUser.Address, nexus.RootChain, Nexus.StakingTokenSymbol, accountBalance);
            simulator.EndBlock();

            //-----------
            //Perform a valid Stake call
            var stakeAmount         = accountBalance;
            var startingSoulBalance = simulator.Nexus.RootChain.GetTokenBalance(Nexus.StakingTokenSymbol, testUser.Address);

            simulator.BeginBlock();
            tx = simulator.GenerateCustomTransaction(testUser, () =>
                                                     ScriptUtils.BeginScript().AllowGas(testUser.Address, Address.Null, 1, 9999)
                                                     .CallContract("energy", "Stake", testUser.Address, stakeAmount).
                                                     SpendGas(testUser.Address).EndScript());
            simulator.EndBlock();

            BigInteger stakedAmount = (BigInteger)simulator.Nexus.RootChain.InvokeContract("energy", "GetStake", testUser.Address);

            Assert.IsTrue(stakedAmount == stakeAmount);

            var finalSoulBalance = simulator.Nexus.RootChain.GetTokenBalance(Nexus.StakingTokenSymbol, testUser.Address);

            Assert.IsTrue(stakeAmount == startingSoulBalance - finalSoulBalance);

            //-----------
            //Upload a file: should succeed
            var filename      = "notAVirus.exe";
            var headerSize    = CalculateRequiredSize(filename, 0);
            var contentSize   = (long)(Archive.MaxSize) - (long)headerSize;
            var content       = new byte[contentSize];
            var contentMerkle = new MerkleTree(content);

            simulator.BeginBlock();
            tx = simulator.GenerateCustomTransaction(testUser, () =>
                                                     ScriptUtils.BeginScript().AllowGas(testUser.Address, Address.Null, 1, 9999)
                                                     .CallContract("storage", "UploadFile", testUser.Address, filename, contentSize, contentMerkle, ArchiveFlags.None, new byte[0]).
                                                     SpendGas(testUser.Address).EndScript());
            System.IO.File.WriteAllText(@"D:\Repos\bug_vm.txt", string.Join('\n', new VM.Disassembler(tx.Script).Instructions));
            simulator.EndBlock();

            var usedSpace = (BigInteger)simulator.Nexus.RootChain.InvokeContract("storage", "GetUsedSpace", testUser.Address);

            Assert.IsTrue(usedSpace == 0);
        }
예제 #4
0
        public static BigInteger CalculateStorageSizeForStake(BigInteger stakeAmount)
        {
            var availableSize = stakeAmount * KilobytesPerStake * 1024;

            availableSize /= UnitConversion.GetUnitValue(Nexus.StakingTokenDecimals);

            return(availableSize);
        }
예제 #5
0
        public BigInteger CalculateStorageSizeForStake(BigInteger stakeAmount)
        {
            var kilobytesPerStake = (int)Runtime.GetGovernanceValue(StorageContract.KilobytesPerStakeTag);
            var availableSize     = stakeAmount * kilobytesPerStake * 1024;

            availableSize /= UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals);

            return(availableSize);
        }
예제 #6
0
        public void RegisterName(Address target, string name)
        {
            Runtime.Expect(target.IsUser, "must be user address");
            Runtime.Expect(target != Runtime.GenesisAddress, "address must not be genesis");
            Runtime.Expect(Runtime.IsWitness(target), "invalid witness");
            Runtime.Expect(ValidationUtils.IsValidIdentifier(name), "invalid name");

            var stake = Runtime.GetStake(target);

            Runtime.Expect(stake >= UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals), "must have something staked");

            Runtime.Expect(name != Runtime.NexusName, "name already used for nexus");
            Runtime.Expect(!Runtime.ChainExists(name), "name already used for a chain");
            Runtime.Expect(!Runtime.PlatformExists(name), "name already used for a platform");
            Runtime.Expect(!Runtime.ContractExists(name), "name already used for a contract");
            Runtime.Expect(!Runtime.FeedExists(name), "name already used for a feed");
            Runtime.Expect(!Runtime.OrganizationExists(name), "name already used for a organization");
            Runtime.Expect(!Runtime.TokenExists(name.ToUpper()), "name already used for a token");

            Runtime.Expect(!_addressMap.ContainsKey(target), "address already has a name");
            Runtime.Expect(!_nameMap.ContainsKey(name), "name already used for other account");

            //System.Console.WriteLine("Trying to register: " + name);
            bool isReserved = false;

            for (int i = 0; i < prefixNames.Length; i++)
            {
                if (name.StartsWith(prefixNames[i]))
                {
                    //System.Console.WriteLine("Starts with : " + prefixNames[i]+ " at index " +i);
                    isReserved = true;
                    break;
                }
            }

            for (int i = 0; i < reservedNames.Length; i++)
            {
                if (name == reservedNames[i])
                {
                    //System.Console.WriteLine("Reserved with : " + reservedNames[i]);
                    isReserved = true;
                    break;
                }
            }

            if (isReserved && Runtime.IsWitness(Runtime.GenesisAddress))
            {
                isReserved = false;
            }

            Runtime.Expect(!isReserved, $"name '{name}' reserved by system");

            _addressMap.Set(target, name);
            _nameMap.Set(name, target);

            Runtime.Notify(EventKind.AddressRegister, target, name);
        }
예제 #7
0
        public void TransferScriptMethodExtraction()
        {
            var source = PhantasmaKeys.Generate();
            var dest   = PhantasmaKeys.Generate();
            var amount = UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals);
            var script = ScriptUtils.BeginScript().AllowGas(source.Address, Address.Null, 1, 999).TransferTokens(DomainSettings.StakingTokenSymbol, source.Address, dest.Address, amount).SpendGas(source.Address).EndScript();

            var table   = DisasmUtils.GetDefaultDisasmTable();
            var methods = DisasmUtils.ExtractMethodCalls(script, table);

            Assert.IsTrue(methods != null && methods.Count() == 3);
        }
예제 #8
0
        public void DepositTokens(Address from, string symbol, BigInteger amount)
        {
            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");
            Runtime.Expect(from.IsUser, "address must be user address");

            Runtime.Expect(IsSupportedToken(symbol), "token is unsupported");

            var info       = Runtime.GetToken(symbol);
            var unitAmount = UnitConversion.GetUnitValue(info.Decimals);

            Runtime.Expect(amount >= unitAmount, "invalid amount");

            Runtime.TransferTokens(symbol, from, this.Address, amount);
        }
예제 #9
0
        // returns value in FIAT token
        public BigInteger GetTokenPrice(string symbol)
        {
            if (symbol == DomainSettings.FiatTokenSymbol)
            {
                return(UnitConversion.GetUnitValue(DomainSettings.FiatTokenDecimals));
            }

            Core.Throw.If(!Nexus.TokenExists(RootStorage, symbol), "cannot read price for invalid token");
            var token = GetToken(symbol);

            Core.Throw.If(Oracle == null, "cannot read price from null oracle");
            var bytes = Oracle.Read(this.Time, "price://" + symbol);
            var value = BigInteger.FromUnsignedArray(bytes, true);

            Expect(value > 0, "token price not available for " + symbol);

            return(value);
        }
예제 #10
0
        public void DepositTokens(Address from, string symbol, BigInteger amount)
        {
            Runtime.Expect(IsWitness(from), "invalid witness");
            Runtime.Expect(from.IsUser, "address must be user address");

            Runtime.Expect(IsSupportedToken(symbol), "token is unsupported");

            var info       = Runtime.Nexus.GetTokenInfo(symbol);
            var unitAmount = UnitConversion.GetUnitValue(info.Decimals);

            Runtime.Expect(amount >= unitAmount, "invalid amount");

            Runtime.Expect(Runtime.Nexus.TransferTokens(Runtime, symbol, from, this.Address, amount), "tokens transfer failed");
            Runtime.Notify(EventKind.TokenSend, from, new TokenEventData()
            {
                chainAddress = this.Address, symbol = symbol, value = amount
            });
        }
예제 #11
0
        public void RegisterScript(Address target, byte[] script, byte[] abiBytes)
        {
            Runtime.Expect(target.IsUser, "must be user address");
            Runtime.Expect(target != Runtime.GenesisAddress, "address must not be genesis");
            Runtime.Expect(Runtime.IsWitness(target), "invalid witness");

            var stake = Runtime.GetStake(target);

            Runtime.Expect(stake >= UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals), "must have something staked");

            Runtime.Expect(script.Length < 1024, "invalid script length");

            Runtime.Expect(!_scriptMap.ContainsKey(target), "address already has a script");

            var abi = ContractInterface.FromBytes(abiBytes);

            Runtime.Expect(abi.MethodCount > 0, "unexpected empty contract abi");

            var witnessTriggerName = AccountTrigger.OnWitness.ToString();

            if (abi.HasMethod(witnessTriggerName))
            {
                var witnessCheck = Runtime.InvokeTrigger(false, script, NativeContractKind.Account, abi, witnessTriggerName, Address.Null) != TriggerResult.Failure;
                Runtime.Expect(!witnessCheck, "script does not handle OnWitness correctly, case #1");

                witnessCheck = Runtime.InvokeTrigger(false, script, NativeContractKind.Account, abi, witnessTriggerName, target) != TriggerResult.Failure;
                Runtime.Expect(witnessCheck, "script does not handle OnWitness correctly, case #2");
            }

            _scriptMap.Set(target, script);
            _abiMap.Set(target, abiBytes);

            var constructor = abi.FindMethod(SmartContract.ConstructorName);

            if (constructor != null)
            {
                Runtime.CallContext(target.Text, constructor, target);
            }

            // TODO? Runtime.Notify(EventKind.AddressRegister, target, script);
        }
예제 #12
0
        // returns value in FIAT token
        public BigInteger GetTokenPrice(string symbol)
        {
            if (symbol == Nexus.FiatTokenSymbol)
            {
                return(UnitConversion.GetUnitValue(Nexus.FiatTokenDecimals));
            }

            if (symbol == Nexus.FuelTokenSymbol)
            {
                var result = GetTokenPrice(Nexus.StakingTokenSymbol);
                result /= 5;
                return(result);
            }

            Core.Throw.If(Oracle == null, "cannot read price from null oracle");

            Core.Throw.If(!Nexus.TokenExists(symbol), "cannot read price for invalid token");

            var bytes = Oracle.Read("price://" + symbol);
            var value = BigInteger.FromUnsignedArray(bytes, true);

            return(value);
        }
예제 #13
0
        public void RegisterName(Address target, string name)
        {
            Runtime.Expect(target.IsUser, "must be user address");
            Runtime.Expect(target != Runtime.GenesisAddress, "address must not be genesis");
            Runtime.Expect(Runtime.IsWitness(target), "invalid witness");
            Runtime.Expect(ValidationUtils.IsValidIdentifier(name), "invalid name");

            var stake = Runtime.GetStake(target);

            Runtime.Expect(stake >= UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals), "must have something staked");

            Runtime.Expect(name != Runtime.NexusName, "name already used for nexus");
            Runtime.Expect(!Runtime.ChainExists(name), "name already used for a chain");
            Runtime.Expect(!Runtime.PlatformExists(name), "name already used for a platform");
            Runtime.Expect(!Runtime.ContractExists(name), "name already used for a contract");
            Runtime.Expect(!Runtime.FeedExists(name), "name already used for a feed");
            Runtime.Expect(!Runtime.OrganizationExists(name), "name already used for a organization");
            Runtime.Expect(!Runtime.TokenExists(name.ToUpper()), "name already used for a token");

            Runtime.Expect(!_addressMap.ContainsKey(target), "address already has a name");
            Runtime.Expect(!_nameMap.ContainsKey(name), "name already used for other account");

            var isReserved = ValidationUtils.IsReservedIdentifier(name);

            if (isReserved && Runtime.IsWitness(Runtime.GenesisAddress))
            {
                isReserved = false;
            }

            Runtime.Expect(!isReserved, $"name '{name}' reserved by system");

            _addressMap.Set(target, name);
            _nameMap.Set(name, target);

            Runtime.Notify(EventKind.AddressRegister, target, name);
        }
예제 #14
0
        // anyone can call this, not only manager, in order to be able to trigger refunds
        public void CloseSale(Address from, Hash saleHash)
        {
            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");

            Runtime.Expect(_saleMap.ContainsKey(saleHash), "sale does not exist or already closed");

            var sale = _saleMap.Get <Hash, SaleInfo>(saleHash);

            Runtime.Expect(Runtime.Time > sale.EndDate, "sale still not reached end date");

            var soldSupply     = _saleSupply.Get <Hash, BigInteger>(saleHash);
            var buyerAddresses = GetSaleParticipants(saleHash);

            var amountMap = _buyerAmounts.Get <Hash, StorageMap>(saleHash);

            var saleToken    = Runtime.GetToken(sale.SellSymbol);
            var receiveToken = Runtime.GetToken(sale.ReceiveSymbol);

            if (soldSupply >= sale.GlobalSoftCap) // if at least soft cap reached, send tokens to buyers and funds to sellers
            {
                foreach (var addr in buyerAddresses)
                {
                    var buyer  = addr;
                    var amount = amountMap.Get <Address, BigInteger>(buyer);

                    Runtime.Notify(EventKind.Crowdsale, buyer, new SaleEventData()
                    {
                        kind = SaleEventKind.Distribution, saleHash = saleHash
                    });

                    if (Runtime.ProtocolVersion <= 5)
                    {
                        buyer = sale.Creator;
                    }

                    Runtime.TransferTokens(sale.SellSymbol, this.Address, buyer, amount);
                }

                var fundsAmount = Runtime.ConvertBaseToQuote(soldSupply, UnitConversion.GetUnitValue(receiveToken.Decimals), saleToken, receiveToken);
                fundsAmount /= sale.Price;

                Runtime.Notify(EventKind.Crowdsale, sale.Creator, new SaleEventData()
                {
                    kind = SaleEventKind.Distribution, saleHash = saleHash
                });
                Runtime.TransferTokens(sale.ReceiveSymbol, this.Address, sale.Creator, fundsAmount);

                var leftovers = sale.GlobalHardCap - soldSupply;
                Runtime.TransferTokens(sale.SellSymbol, this.Address, sale.Creator, leftovers);
            }
            else // otherwise return funds to buyers and return tokens to sellers
            {
                foreach (var buyer in buyerAddresses)
                {
                    var amount = amountMap.Get <Address, BigInteger>(buyer);

                    amount = Runtime.ConvertBaseToQuote(amount, sale.Price, saleToken, receiveToken);
                    Runtime.Notify(EventKind.Crowdsale, buyer, new SaleEventData()
                    {
                        kind = SaleEventKind.Refund, saleHash = saleHash
                    });
                    Runtime.TransferTokens(sale.ReceiveSymbol, this.Address, buyer, amount);
                }

                Runtime.Notify(EventKind.Crowdsale, sale.Creator, new SaleEventData()
                {
                    kind = SaleEventKind.Refund, saleHash = saleHash
                });
                Runtime.TransferTokens(sale.SellSymbol, this.Address, sale.Creator, sale.GlobalHardCap);
            }
        }
예제 #15
0
        public void TestAutoSendAddress()
        {
            var test = CreateAPI();

            var simulator  = test.simulator;
            var owner      = test.owner;
            var testUser   = PhantasmaKeys.Generate();
            var autoSender = PhantasmaKeys.Generate();
            var receiver   = PhantasmaKeys.Generate();
            var node       = PhantasmaKeys.FromWIF(nodeWIF);
            var nexus      = simulator.Nexus;
            var api        = test.api;

            var symbol = DomainSettings.StakingTokenSymbol;

            var senderAddressStr    = Base16.Encode(autoSender.Address.ToByteArray());
            var receivingAddressStr = Base16.Encode(receiver.Address.ToByteArray());
            var isTrue = true;

            string[] scriptString = new string[]
            {
                $"alias r1, $triggerReceive",
                $"alias r2, $currentTrigger",
                $"alias r3, $comparisonResult",
                $"alias r13, $triggerWitness",
                $"alias r14, $currentAddress",
                $"alias r15, $sourceAddress",

                $@"load $triggerReceive, ""{AccountTrigger.OnReceive}""",
                $@"load $triggerWitness, ""{AccountTrigger.OnWitness}""",
                $"pop $currentTrigger",
                $"pop $currentAddress",

                $"equal $triggerWitness, $currentTrigger, $comparisonResult",
                $"jmpif $comparisonResult, @witnessHandler",

                $"equal $triggerReceive, $currentTrigger, $comparisonResult",
                $"jmpif $comparisonResult, @receiveHandler",

                $"jmp @end",

                $"@receiveHandler: nop",

                $"alias r4, $tokenContract",
                $"alias r5, $sourceAddress",
                $"alias r6, $targetAddress",
                $"alias r7, $receivedAmount",
                $"alias r8, $symbol",
                $"alias r9, $methodName",

                $"pop $sourceAddress",
                $"pop $symbol",
                $"pop $receivedAmount",

                $"load r11 0x{receivingAddressStr}",
                $"push r11",
                $@"extcall ""Address()""",
                $"pop $targetAddress",

                $"push $receivedAmount",
                $"push $symbol",
                $"push $targetAddress",
                $"push $sourceAddress",
                "extcall \"Runtime.TransferTokens\"",

                $"@witnessHandler: ",
                $"load r11 0x{senderAddressStr}",
                $"push r11",
                "extcall \"Address()\"",
                $"pop $sourceAddress",
                $"equal $sourceAddress, $currentAddress, $comparisonResult",
                "jmpif $comparisonResult, @endWitness",
                "throw",

                $"@endWitness: ret",
                $"load r11 {isTrue}",
                $"push r11",

                $"@end: ret"
            };

            var script = AssemblerUtils.BuildScript(scriptString);

            simulator.BeginBlock();
            simulator.GenerateTransfer(owner, autoSender.Address, simulator.Nexus.RootChain, DomainSettings.FuelTokenSymbol, 100000);
            simulator.GenerateTransfer(owner, autoSender.Address, simulator.Nexus.RootChain, DomainSettings.StakingTokenSymbol, UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals));
            simulator.GenerateCustomTransaction(autoSender, ProofOfWork.None,
                                                () => ScriptUtils.BeginScript().AllowGas(autoSender.Address, Address.Null, 1, 9999)
                                                .CallContract("stake", "Stake", autoSender.Address, UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals))
                                                .CallContract("account", "RegisterScript", autoSender.Address, script).SpendGas(autoSender.Address)
                                                .EndScript());
            simulator.EndBlock();


            simulator.BeginBlock();
            simulator.GenerateTransfer(owner, autoSender.Address, simulator.Nexus.RootChain, symbol, 30000);
            simulator.EndBlock();

            var token = simulator.Nexus.GetTokenInfo(simulator.Nexus.RootStorage, symbol);

            var balance = simulator.Nexus.RootChain.GetTokenBalance(simulator.Nexus.RootStorage, token, receiver.Address);

            Assert.IsTrue(balance == 30000);
        }
예제 #16
0
        public void Purchase(Address from, Hash saleHash, string quoteSymbol, BigInteger quoteAmount)
        {
            //For now, prevent purchases with other tokens
            Runtime.Expect(quoteSymbol == DomainSettings.StakingTokenSymbol, "invalid receive token symbol: " + quoteSymbol + ". SOUL token must be used for purchase");

            Runtime.Expect(Runtime.TokenExists(quoteSymbol), "token must exist: " + quoteSymbol);
            var quoteToken = Runtime.GetToken(quoteSymbol);

            Runtime.Expect(_saleMap.ContainsKey <Hash>(saleHash), "sale does not exist");
            var sale = _saleMap.Get <Hash, SaleInfo>(saleHash);

            Runtime.Expect(Runtime.Time >= sale.StartDate, "sale has not started");
            Runtime.Expect(Runtime.Time < sale.EndDate, "sale has reached end date");

            Runtime.Expect(quoteSymbol != sale.SellSymbol, "cannot participate in the sale using " + quoteSymbol);
            Runtime.Expect(from != sale.Creator, "sale creator can't participate");

            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");
            if (sale.Flags.HasFlag(SaleFlags.Whitelist))
            {
                Runtime.Expect(IsWhitelisted(saleHash, from), "address is not whitelisted");
            }

            var saleToken       = Runtime.GetToken(sale.SellSymbol);
            var convertedAmount = Runtime.ConvertQuoteToBase(quoteAmount, UnitConversion.GetUnitValue(quoteToken.Decimals), saleToken, quoteToken) * sale.Price;

            var temp = UnitConversion.ToDecimal(convertedAmount, saleToken.Decimals);

            Runtime.Expect(temp >= 1, "cannot purchase very tiny amount");

            var previousSupply = _saleSupply.Get <Hash, BigInteger>(saleHash);
            var nextSupply     = previousSupply + convertedAmount;

            //Runtime.Expect(nextSupply <= sale.HardCap, "hard cap reached");
            if (nextSupply > sale.GlobalHardCap)
            {
                convertedAmount = sale.GlobalHardCap - previousSupply;
                Runtime.Expect(convertedAmount > 0, "hard cap reached");
                quoteAmount = Runtime.ConvertBaseToQuote(convertedAmount, sale.Price, saleToken, quoteToken);
                nextSupply  = 0;
            }

            Runtime.TransferTokens(quoteSymbol, from, this.Address, quoteAmount);
            Runtime.Notify(EventKind.Crowdsale, from, new SaleEventData()
            {
                kind = SaleEventKind.Participation, saleHash = saleHash
            });

            _saleSupply.Set <Hash, BigInteger>(saleHash, nextSupply);

            if (nextSupply == 0)
            {
                Runtime.Notify(EventKind.Crowdsale, from, new SaleEventData()
                {
                    kind = SaleEventKind.HardCap, saleHash = saleHash
                });
            }
            else
            if (previousSupply < sale.GlobalSoftCap && nextSupply >= sale.GlobalSoftCap)
            {
                Runtime.Notify(EventKind.Crowdsale, from, new SaleEventData()
                {
                    kind = SaleEventKind.SoftCap, saleHash = saleHash
                });
            }

            if (quoteSymbol != sale.ReceiveSymbol)
            {
                Runtime.CallNativeContext(NativeContractKind.Swap, nameof(SwapContract.SwapTokens), this.Address, quoteSymbol, sale.ReceiveSymbol, quoteAmount);
            }

            var amountMap   = _buyerAmounts.Get <Hash, StorageMap>(saleHash);
            var totalAmount = amountMap.Get <Address, BigInteger>(from);

            var newAmount = totalAmount + convertedAmount;

            if (sale.UserSoftCap > 0)
            {
                Runtime.Expect(newAmount >= sale.UserSoftCap, "user purchase minimum limit not reached");
            }

            if (sale.UserHardCap > 0)
            {
                Runtime.Expect(newAmount <= sale.UserHardCap, "user purchase maximum limit exceeded");
            }

            var addressMap = _buyerAddresses.Get <Hash, StorageList>(saleHash);

            if (!addressMap.Contains <Address>(from))
            {
                addressMap.Add <Address>(from);
            }

            amountMap.Set <Address, BigInteger>(from, newAmount);
        }
예제 #17
0
        // send to external chain
        public void WithdrawTokens(Address from, Address to, string symbol, BigInteger amount)
        {
            Runtime.Expect(amount > 0, "amount must be positive and greater than zero");
            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");

            Runtime.Expect(from.IsUser, "source must be user address");
            Runtime.Expect(to.IsInterop, "destination must be interop address");

            Runtime.Expect(Runtime.TokenExists(symbol), "invalid token");

            var transferTokenInfo = this.Runtime.GetToken(symbol);

            Runtime.Expect(transferTokenInfo.Flags.HasFlag(TokenFlags.Transferable), "transfer token must be transferable");
            Runtime.Expect(transferTokenInfo.Flags.HasFlag(TokenFlags.External), "transfer token must be external");
            Runtime.Expect(transferTokenInfo.Flags.HasFlag(TokenFlags.Fungible), "transfer token must be fungible");

            string platform;

            byte[] dummy;
            to.DecodeInterop(out platform, out dummy, 0);
            Runtime.Expect(platform != DomainSettings.PlatformName, "must be external platform");
            Runtime.Expect(Runtime.PlatformExists(platform), "invalid platform");
            var platformInfo = Runtime.GetPlatform(platform);

            Runtime.Expect(to != platformInfo.Address, "invalid target address");

            var feeSymbol = platformInfo.Symbol;

            Runtime.Expect(Runtime.TokenExists(feeSymbol), "invalid fee token");

            var feeTokenInfo = this.Runtime.GetToken(feeSymbol);

            Runtime.Expect(feeTokenInfo.Flags.HasFlag(TokenFlags.Fungible), "fee token must be fungible");
            Runtime.Expect(feeTokenInfo.Flags.HasFlag(TokenFlags.Transferable), "fee token must be transferable");

            var basePrice = UnitConversion.GetUnitValue(DomainSettings.FiatTokenDecimals) / InteropFeeRacio; // 50cents
            var feeAmount = Runtime.GetTokenQuote(DomainSettings.FiatTokenSymbol, feeSymbol, basePrice);

            Runtime.Expect(feeAmount > 0, "fee is too small");

            Runtime.Expect(Runtime.TransferTokens(feeSymbol, from, this.Address, feeAmount), "fee transfer failed");

            Runtime.Expect(Runtime.TransferTokens(symbol, from, platformInfo.Address, amount), "burn failed");

            var collateralAmount = Runtime.GetTokenQuote(DomainSettings.FiatTokenSymbol, DomainSettings.FuelTokenSymbol, basePrice);

            var withdraw = new InteropWithdraw()
            {
                destination      = to,
                transferAmount   = amount,
                transferSymbol   = symbol,
                feeAmount        = feeAmount,
                feeSymbol        = feeSymbol,
                hash             = Runtime.Transaction.Hash,
                broker           = Address.Null,
                collateralAmount = collateralAmount,
                timestamp        = Runtime.Time
            };

            _withdraws.Add <InteropWithdraw>(withdraw);

            Runtime.Notify(EventKind.TokenSend, from, new TokenEventData()
            {
                chainAddress = this.Address, value = amount, symbol = symbol
            });
            Runtime.Notify(EventKind.TokenEscrow, from, new TokenEventData()
            {
                chainAddress = this.Address, value = feeAmount, symbol = symbol
            });
            Runtime.Notify(EventKind.BrokerRequest, from, to);
        }