Example #1
0
        public static void ExecuteScript(string[] args, SpookSettings settings, NexusAPI api, BigInteger minFee)
        {
            if (args.Length != 1)
            {
                throw new CommandException("Invalid number of arguments, expected file name");
            }

            DoChecks(api);

            var fileName = args[0];

            if (!File.Exists(fileName))
            {
                throw new CommandException("Provided file does not exist");
            }

            if (!fileName.EndsWith(".tx"))
            {
                throw new CommandException($"Provided file is not a compiled transaction script");
            }

            var txScript = File.ReadAllBytes(fileName);

            var sb = new ScriptBuilder();

            sb.AllowGas(Keys.Address, Address.Null, minFee, 9999);
            sb.EmitRaw(txScript);
            sb.SpendGas(Keys.Address);

            var script = sb.EndScript();

            var hash = ExecuteTransaction(settings, api, script, ProofOfWork.Minimal, Keys);
        }
Example #2
0
        public static void Migrate(string[] args, SpookSettings settings, NexusAPI api, BigInteger minFee)
        {
            if (args.Length != 1)
            {
                throw new CommandException("Invalid number of arguments, expected new wif");
            }

            DoChecks(api);

            var newWIF  = args[0];
            var newKeys = PhantasmaKeys.FromWIF(newWIF);

            var expectedLimit = 800;

            var sb = new ScriptBuilder();

            sb.AllowGas(Keys.Address, Address.Null, minFee, expectedLimit);
            sb.CallContract("validator", "Migrate", Keys.Address, newKeys.Address);
            sb.SpendGas(Keys.Address);
            var script = sb.EndScript();

            var hash = ExecuteTransaction(settings, api, script, ProofOfWork.None, Keys /*, newKeys*/);

            if (hash != Hash.Null)
            {
                logger.Message($"Migrated to " + newKeys.Address);
                Keys = newKeys;
            }
        }
Example #3
0
        public static void Airdrop(string[] args, NexusAPI api, BigInteger minFee)
        {
            if (args.Length != 1)
            {
                throw new CommandException("Invalid number of arguments, expected airdrop filename");
            }

            DoChecks(api);

            var fileName = args[0];

            if (!File.Exists(fileName))
            {
                throw new CommandException("Airdrop file does not exist");
            }

            var lines = File.ReadAllLines(fileName);
            var sb    = new ScriptBuilder();

            var expectedLimit = 100 + 600 * lines.Length;

            var expectedGas = UnitConversion.ToDecimal(expectedLimit * minFee, DomainSettings.FuelTokenDecimals);

            logger.Message($"This airdrop will require at least {expectedGas} {DomainSettings.FuelTokenSymbol}");

            sb.AllowGas(Keys.Address, Address.Null, minFee, expectedLimit);

            int addressCount = 0;

            foreach (var line in lines)
            {
                var temp = line.Split(',');

                if (!Address.IsValidAddress(temp[0]))
                {
                    continue;
                }

                addressCount++;

                var target = Address.FromText(temp[0]);
                var symbol = temp[1];
                var amount = BigInteger.Parse(temp[2]);

                sb.TransferTokens(symbol, Keys.Address, target, amount);
            }

            sb.SpendGas(Keys.Address);
            var script = sb.EndScript();

            logger.Message($"Sending airdrop to {addressCount} addresses...");
            ExecuteTransaction(api, script, ProofOfWork.None, Keys);
        }
Example #4
0
        private static void DeployOrUpgrade(string[] args, SpookSettings settings, NexusAPI api, BigInteger minFee, bool isUpgrade)
        {
            if (args.Length != 1)
            {
                throw new CommandException("Invalid number of arguments, expected file name");
            }

            DoChecks(api);

            var fileName = args[0];

            if (!File.Exists(fileName))
            {
                throw new CommandException("Provided file does not exist");
            }

            if (!FileExistsCaseSensitive(fileName))
            {
                throw new CommandException("Provided file case does not match real file name case");
            }

            var extension = ScriptModule.ScriptExtension;

            if (!fileName.EndsWith(extension))
            {
                throw new CommandException($"Provided file is not a compiled {extension} script");
            }

            var abiFile = fileName.Replace(extension, ".abi");

            if (!File.Exists(abiFile))
            {
                throw new CommandException($"No ABI file {abiFile} that matches provided script file");
            }

            var contractName = Path.GetFileNameWithoutExtension(fileName);

            var contractScript = File.ReadAllBytes(fileName);
            var abiBytes       = File.ReadAllBytes(abiFile);

            var abi = ContractInterface.FromBytes(abiBytes);

            var sb = new ScriptBuilder();

            int nexusVersion = 0;

            try
            {
                var nexusDetails = api.Execute("getNexus", new object[] { false });
                var root         = LunarLabs.Parser.JSON.JSONReader.ReadFromString(nexusDetails);

                var governance = root["governance"];

                var entry = governance.Children.Where(x => x.GetNode("name").Value == "nexus.protocol.version").FirstOrDefault();
                entry = entry.GetNodeByIndex(1);

                nexusVersion = Int32.Parse(entry.Value);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                nexusVersion = -1;
            }

            if (nexusVersion <= 1)
            {
                throw new CommandException("Failed to obtain nexus version via API");
            }


            bool isToken        = ValidationUtils.IsValidTicker(contractName);
            var  availableFlags = Enum.GetValues(typeof(TokenFlags)).Cast <TokenFlags>().ToArray();

            sb.AllowGas(Keys.Address, Address.Null, minFee, 9999);

            if (isUpgrade)
            {
                // check for modification in flags
                if (isToken)
                {
                    var symbol    = contractName;
                    var resultStr = api.Execute("getToken", new[] { symbol, "false" });

                    logger.Debug($"{resultStr}");

                    //2021.08.27 sfichera: Fixed api obj deserialization.
                    //dynamic apiResult = System.Text.Json.JsonSerializer.Deserialize<TokenResult>(resultStr);
                    dynamic apiResult = JsonConvert.DeserializeObject <TokenResult>(resultStr);

                    if (apiResult is TokenResult)
                    {
                        var oldToken   = (TokenResult)apiResult;
                        var oldFlags   = TokenFlags.None;
                        var splitFlags = oldToken.flags.Split(',');
                        foreach (var entry in splitFlags)
                        {
                            TokenFlags flag;

                            if (Enum.TryParse <TokenFlags>(entry, true, out flag))
                            {
                                oldFlags |= flag;
                            }
                        }

                        foreach (var flag in availableFlags)
                        {
                            var propName = "is" + flag;
                            if (abi.HasMethod(propName))
                            {
                                var isSet  = ExecuteScript(contractScript, abi, propName).AsBool();
                                var wasSet = oldFlags.HasFlag(flag);
                                if (isSet != wasSet)
                                {
                                    throw new CommandException($"Detected '{flag}' flag change: {wasSet} => {isSet}");
                                }
                            }
                        }
                    }
                    else
                    {
                        throw new CommandException("could not find any deployed token contract for " + symbol);
                    }
                }

                sb.CallInterop("Runtime.UpgradeContract", Keys.Address, contractName, contractScript, abiBytes);
            }
            else
            if (isToken)
            {
                if (!abi.HasMethod("getName"))
                {
                    throw new CommandException("token contract is missing required 'name' property");
                }

                string symbol = null;


                if (nexusVersion < 6)
                {
                    symbol = contractName;
                }
                else
                {
                    if (abi.HasMethod("getSymbol"))
                    {
                        symbol = ExecuteScript(contractScript, abi, "getSymbol").AsString();
                    }

                    if (string.IsNullOrEmpty(symbol))
                    {
                        throw new CommandException("token contract 'symbol' property is either missing or returning an empty value");
                    }
                }

                var name = ExecuteScript(contractScript, abi, "getName").AsString();

                if (string.IsNullOrEmpty(name))
                {
                    throw new CommandException("token contract 'name' property is either missing or returning an empty value");
                }

                BigInteger maxSupply = abi.HasMethod("getMaxSupply") ? ExecuteScript(contractScript, abi, "getMaxSupply").AsNumber() : 0;
                BigInteger decimals  = abi.HasMethod("getDecimals") ? ExecuteScript(contractScript, abi, "getDecimals").AsNumber() : 0;

                TokenFlags flags = TokenFlags.None;

                foreach (var flag in availableFlags)
                {
                    var propName = "is" + flag;
                    if (abi.HasMethod(propName) && ExecuteScript(contractScript, abi, propName).AsBool())
                    {
                        flags |= flag;
                    }
                }

                if (nexusVersion < 6)
                {
                    sb.CallInterop("Nexus.CreateToken", Keys.Address, symbol, name, maxSupply, decimals, flags, contractScript, abiBytes);
                }
                else
                {
                    sb.CallInterop("Nexus.CreateToken", Keys.Address, contractScript, abiBytes);
                }

                contractName = symbol;
            }
            else
            {
                sb.CallInterop("Runtime.DeployContract", Keys.Address, contractName, contractScript, abiBytes);
            }

            sb.SpendGas(Keys.Address);
            var script = sb.EndScript();

            if (!isUpgrade)
            {
                var upgradeTrigger = AccountContract.GetTriggerForABI(AccountTrigger.OnUpgrade);
                if (abi.Implements(upgradeTrigger))
                {
                    logger.Message($"{contractName} implements proper triggers, and can be upgraded later.");
                }
                else
                {
                    logger.Warning($"{contractName} does not implements proper triggers, can't be upgraded later.");
                }
            }

            var hash = ExecuteTransaction(settings, api, script, ProofOfWork.Minimal, Keys);

            if (hash != Hash.Null)
            {
                var expectedEvent    = isUpgrade ? EventKind.ContractUpgrade : (isToken ? EventKind.TokenCreate : EventKind.ContractDeploy);
                var expectedEventStr = expectedEvent.ToString();

                var events = GetTransactionEvents(hash);
                if (events.Any(x => x.kind == expectedEventStr))
                {
                    var contractAddress = SmartContract.GetAddressForName(contractName);

                    string action = isUpgrade ? "Upgraded" : "Deployed";

                    logger.Message($"{action} {contractName} at {contractAddress}");
                }
                else
                {
                    throw new CommandException("Transaction was confirmed but deployment event is missing!");
                }
            }
        }
Example #5
0
        public static void Airdrop(string[] args, SpookSettings settings, NexusAPI api, BigInteger minFee)
        {
            if (args.Length != 1)
            {
                throw new CommandException("Invalid number of arguments, expected airdrop filename");
            }

            DoChecks(api);

            var fileName = args[0];

            if (!File.Exists(fileName))
            {
                throw new CommandException("Airdrop file does not exist");
            }

            var lines = File.ReadAllLines(fileName);
            var sb    = new ScriptBuilder();

            var expectedLimit = 100 + 600 * lines.Length;

            var expectedGas = UnitConversion.ToDecimal(expectedLimit * minFee, DomainSettings.FuelTokenDecimals);

            logger.Message($"This airdrop will require at least {expectedGas} {DomainSettings.FuelTokenSymbol}");

            sb.AllowGas(Keys.Address, Address.Null, minFee, expectedLimit);


            Dictionary <string, TokenResult> tokens = new Dictionary <string, TokenResult>();

            int addressCount = 0;

            foreach (var line in lines)
            {
                var temp = line.Split(',');

                if (!Address.IsValidAddress(temp[0]))
                {
                    continue;
                }

                addressCount++;

                var     target = Address.FromText(temp[0]);
                var     symbol = temp[1];
                decimal val;

                if (!Decimal.TryParse(temp[2], NumberStyles.Any, new CultureInfo("en-US"), out val) || val <= 0)
                {
                    throw new CommandException($"Invalid amount {val} for {target}");
                }

                var token = tokens.ContainsKey(symbol) ? tokens[symbol] : FetchTokenInfo(api, symbol);
                tokens[symbol] = token;

                var amount = UnitConversion.ToBigInteger(val, token.decimals);

                sb.TransferTokens(symbol, Keys.Address, target, amount);
            }

            sb.SpendGas(Keys.Address);
            var script = sb.EndScript();

            logger.Message($"Sending airdrop to {addressCount} addresses...");
            ExecuteTransaction(settings, api, script, ProofOfWork.None, Keys);
        }
Example #6
0
        private static void DeployOrUpgrade(string[] args, NexusAPI api, BigInteger minFee, bool isUpgrade)
        {
            if (args.Length != 1)
            {
                throw new CommandException("Invalid number of arguments, expected file name");
            }

            DoChecks(api);

            var fileName = args[0];

            if (!File.Exists(fileName))
            {
                throw new CommandException("Provided file does not exist");
            }

            var extension = ScriptModule.ScriptExtension;

            if (!fileName.EndsWith(extension))
            {
                throw new CommandException($"Provided file is not a compiled {extension} script");
            }

            var abiFile = fileName.Replace(extension, ".abi");

            if (!File.Exists(abiFile))
            {
                throw new CommandException($"No ABI file {abiFile} that matches provided script file");
            }

            var contractName = Path.GetFileNameWithoutExtension(fileName);

            var contractScript = File.ReadAllBytes(fileName);
            var abiBytes       = File.ReadAllBytes(abiFile);

            var abi = ContractInterface.FromBytes(abiBytes);

            var sb = new ScriptBuilder();

            bool isToken        = ValidationUtils.IsValidTicker(contractName);
            var  availableFlags = Enum.GetValues(typeof(TokenFlags)).Cast <TokenFlags>().ToArray();

            sb.AllowGas(Keys.Address, Address.Null, minFee, 9999);

            if (isUpgrade)
            {
                // check for modification in flags
                if (isToken)
                {
                    var     symbol    = contractName;
                    var     resultStr = api.Execute("getToken", new[] { symbol, "false" });
                    dynamic apiResult = JsonConvert.DeserializeObject <TokenResult>(resultStr);

                    if (apiResult is TokenResult)
                    {
                        var oldToken = (TokenResult)apiResult;

                        var oldFlags   = TokenFlags.None;
                        var splitFlags = oldToken.flags.Split(',');
                        foreach (var entry in splitFlags)
                        {
                            TokenFlags flag;

                            if (Enum.TryParse <TokenFlags>(entry, true, out flag))
                            {
                                oldFlags |= flag;
                            }
                        }

                        foreach (var flag in availableFlags)
                        {
                            var propName = "is" + flag;
                            if (abi.HasMethod(propName))
                            {
                                var isSet  = ExecuteScript(contractScript, abi, propName).AsBool();
                                var wasSet = oldFlags.HasFlag(flag);
                                if (isSet != wasSet)
                                {
                                    throw new CommandException($"Detected '{flag}' flag change: {wasSet} => {isSet}");
                                }
                            }
                        }
                    }
                    else
                    {
                        throw new CommandException("could not find any deployed token contract for " + symbol);
                    }
                }

                sb.CallInterop("Runtime.UpgradeContract", Keys.Address, contractName, contractScript, abiBytes);
            }
            else
            if (isToken)
            {
                if (!abi.HasMethod("getName"))
                {
                    throw new CommandException("token contract is missing required 'name' property");
                }

                var symbol = contractName;
                var name   = ExecuteScript(contractScript, abi, "getName").AsString();

                if (string.IsNullOrEmpty(name))
                {
                    throw new CommandException("token contract 'name' property is returning an empty value");
                }

                BigInteger maxSupply = abi.HasMethod("getMaxSupply") ? ExecuteScript(contractScript, abi, "getMaxSupply").AsNumber() : 0;
                BigInteger decimals  = abi.HasMethod("getDecimals") ? ExecuteScript(contractScript, abi, "getDecimals").AsNumber() : 0;

                TokenFlags flags = TokenFlags.None;

                foreach (var flag in availableFlags)
                {
                    var propName = "is" + flag;
                    if (abi.HasMethod(propName) && ExecuteScript(contractScript, abi, propName).AsBool())
                    {
                        flags |= flag;
                    }
                }

                sb.CallInterop("Nexus.CreateToken", Keys.Address, symbol, name, maxSupply, decimals, flags, contractScript, abiBytes);

                contractName = symbol;
            }
            else
            {
                sb.CallInterop("Runtime.DeployContract", Keys.Address, contractName, contractScript, abiBytes);
            }

            sb.SpendGas(Keys.Address);
            var script = sb.EndScript();

            if (!isUpgrade)
            {
                var upgradeTrigger = AccountContract.GetTriggerForABI(AccountTrigger.OnUpgrade);
                if (abi.Implements(upgradeTrigger))
                {
                    logger.Message($"{contractName} implements proper triggers, and can be upgraded later.");
                }
                else
                {
                    logger.Warning($"{contractName} does not implements proper triggers, can't be upgraded later.");
                }
            }

            var hash = ExecuteTransaction(api, script, ProofOfWork.Minimal, Keys);

            if (hash != Hash.Null)
            {
                var expectedEvent    = isUpgrade ? EventKind.ContractUpgrade : (isToken ? EventKind.TokenCreate : EventKind.ContractDeploy);
                var expectedEventStr = expectedEvent.ToString();

                var events = GetTransactionEvents(hash);
                if (events.Any(x => x.kind == expectedEventStr))
                {
                    var contractAddress = SmartContract.GetAddressForName(contractName);

                    string action = isUpgrade ? "Upgraded" : "Deployed";

                    logger.Message($"{action} {contractName} at {contractAddress}");
                }
                else
                {
                    throw new CommandException("Transaction was confirmed but deployment event is missing!");
                }
            }
        }