示例#1
0
        /// <summary>
        /// Creates an invocation contract with a contract script to be deployed on the network.
        /// </summary>
        /// <param name="contractScript"></param>
        /// <param name="parameterList"></param>
        /// <param name="returnType"></param>
        /// <param name="properties"></param>
        /// <param name="name"></param>
        /// <param name="version"></param>
        /// <param name="author"></param>
        /// <param name="email"></param>
        /// <param name="description"></param>
        /// <returns></returns>
        public override async Task <InvocationTransaction> DeployContract(byte[] contractScript, byte[] parameterList,
                                                                          ContractParameterType returnType, ContractPropertyState properties,
                                                                          string name, string version, string author, string email, string description)
        {
            var generatedScript = TransactionBuilderHelper.PrepareDeployContract(contractScript, parameterList,
                                                                                 returnType, properties,
                                                                                 name, version, author, email, description);

            decimal fee = 100;

            if (properties.HasFlag(ContractPropertyState.HasStorage))
            {
                fee += 400;
            }

            if (properties.HasFlag(ContractPropertyState.HasDynamicInvoke))
            {
                fee += 500;
            }

            fee -= 10; // first 10 GAS is free

            var tx = new InvocationTransaction
            {
                Script  = generatedScript,
                Version = 0,
            };

            tx = MakeTransaction(tx, fee: Fixed8.FromDecimal(fee));
            var success = await SignAndSendTransaction(tx);

            return(success ? tx : null);
        }
示例#2
0
        /// <summary>
        /// Creates a 'ClaimTransaction', signs it and send a 'sendrawtransaction' RPC call to the connected node.
        /// This method does not put gas into claimable state. Can only claim 'unclaimable' amount.
        /// </summary>
        /// <returns></returns>
        public override async Task <ClaimTransaction> ClaimGas(UInt160 changeAddress = null)
        {
            var(claimable, amount) =
                await TransactionBuilderHelper.GetClaimable(AddressScriptHash.ToAddress(), _restService);

            if (amount <= 0)
            {
                throw new WalletException("No GAS available to claim at this address");
            }

            var tx = new ClaimTransaction();

            var references = new List <CoinReference>();

            foreach (var entry in claimable)
            {
                references.Add(new CoinReference
                {
                    PrevHash  = UInt256.Parse(entry.Txid),
                    PrevIndex = (ushort)entry.N,
                });
            }

            if (changeAddress == null)
            {
                changeAddress = AddressScriptHash;
            }
            var outputs = new List <TransactionOutput>
            {
                new TransactionOutput
                {
                    ScriptHash = changeAddress,
                    AssetId    = Utils.GasToken,
                    Value      = Fixed8.FromDecimal(amount),
                }
            };

            tx.Version    = 0;
            tx.Claims     = references.ToArray();
            tx.Inputs     = new CoinReference[0];
            tx.Outputs    = outputs.ToArray();
            tx.Attributes = new TransactionAttribute[0];

            var result = await SignAndSendTransaction(tx);

            return(result ? tx : null);
        }
示例#3
0
        /// <summary>
        /// Method that SendNativeAsset, TransferNep5 and CallContract uses to create the inputs and outputs of the transaction.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tx"></param>
        /// <param name="from"></param>
        /// <param name="changeAddress"></param>
        /// <param name="fee"></param>
        /// <returns></returns>
        private T MakeTransaction <T>(T tx, UInt160 from = null, UInt160 changeAddress = null, Fixed8 fee = default(Fixed8)) where T : Transaction
        {
            if (tx.Outputs == null)
            {
                tx.Outputs = new TransactionOutput[0];
            }
            if (tx.Attributes == null)
            {
                tx.Attributes = new TransactionAttribute[0];
            }
            if (from == null)
            {
                from = AddressScriptHash;
            }
            fee += tx.SystemFee;
            var payTotal = tx.Outputs.GroupBy(p => p.AssetId, (k, g) => new
            {
                AssetId = k,
                Value   = g.Sum(p => p.Value)
            }).ToDictionary(p => p.AssetId);

            if (fee > Fixed8.Zero)
            {
                if (payTotal.ContainsKey(Utils.GasToken))
                {
                    payTotal[Utils.GasToken] = new
                    {
                        AssetId = Utils.GasToken,
                        Value   = payTotal[Utils.GasToken].Value + fee
                    };
                }
                else
                {
                    payTotal.Add(Utils.GasToken, new
                    {
                        AssetId = Utils.GasToken,
                        Value   = fee
                    });
                }
            }

            var payCoins = payTotal.Select(async p => new
            {
                AssetId  = p.Key,
                Unspents = await TransactionBuilderHelper.FindUnspentCoins(p.Key, p.Value.Value, from, _restService)
            }).Select(x => x.Result).ToDictionary(p => p.AssetId);

            if (payCoins.Any(p => p.Value.Unspents == null))
            {
                return(null);
            }

            var inputSum = payCoins.Values.ToDictionary(p => p.AssetId, p => new
            {
                p.AssetId,
                Value = p.Unspents.Sum(q => q.Output.Value)
            });

            if (changeAddress == null)
            {
                changeAddress = from;
            }
            List <TransactionOutput> outputsNew = new List <TransactionOutput>(tx.Outputs);

            foreach (UInt256 assetId in inputSum.Keys)
            {
                if (inputSum[assetId].Value > payTotal[assetId].Value)
                {
                    outputsNew.Add(new TransactionOutput
                    {
                        AssetId    = assetId,
                        Value      = inputSum[assetId].Value - payTotal[assetId].Value,
                        ScriptHash = changeAddress
                    });
                }
            }
            tx.Inputs  = payCoins.Values.SelectMany(p => p.Unspents).Select(p => p.Reference).ToArray();
            tx.Outputs = outputsNew.ToArray();
            return(tx);
        }
示例#4
0
        /// <summary>
        /// Transfer NEP5 tokens.
        /// </summary>
        /// <param name="attributes"></param>
        /// <param name="outputs"></param>
        /// <param name="changeAddress"></param>
        /// <param name="fee"></param>
        /// <returns></returns>
        public override async Task <Transaction> TransferNep5(List <TransactionAttribute> attributes,
                                                              IEnumerable <TransferOutput> outputs,
                                                              UInt160 changeAddress = null, decimal fee = 0)
        {
            InvocationTransaction tx;
            var cOutputs = outputs.Where(p => !p.IsGlobalAsset).GroupBy(p => new
            {
                AssetId = (UInt160)p.AssetId,
                Account = p.ScriptHash
            }, (k, g) => new
            {
                k.AssetId,
                Value = g.Aggregate(BigInteger.Zero, (x, y) => x + y.Value.Value),
                k.Account
            }).ToArray();

            if (cOutputs.Length == 0)
            {
                return(null);
            }
            var nep5Balances = await TransactionBuilderHelper.GetNep5Balances(AddressScriptHash.ToAddress(), _restService);

            using (ScriptBuilder sb = new ScriptBuilder())
            {
                foreach (var output in cOutputs)
                {
                    var nep5Balance = nep5Balances.SingleOrDefault(x => x.AssetHash == output.AssetId.ToString().Remove(0, 2));
                    if (nep5Balance == null)
                    {
                        throw new WalletException($"Not enough balance of: {output.AssetId} ");
                    }
                    sb.EmitAppCall(output.AssetId, Nep5Methods.transfer.ToString(), AddressScriptHash, output.Account, output.Value);
                    sb.Emit(OpCode.THROWIFNOT);
                }

                byte[] nonce = GenerateNonce(8);
                sb.Emit(OpCode.RET, nonce);
                tx = new InvocationTransaction
                {
                    Version = 1,
                    Script  = sb.ToArray()
                };
            }

            if (attributes == null)
            {
                attributes = new List <TransactionAttribute>();
            }
            attributes.Add(new TransactionAttribute
            {
                Usage = TransactionAttributeUsage.Script,
                Data  = AddressScriptHash.ToArray()
            });

            tx.Attributes = attributes.ToArray();
            tx.Inputs     = new CoinReference[0];
            tx.Outputs    = outputs.Where(p => p.IsGlobalAsset).Select(p => p.ToTxOutput()).ToArray();
            tx.Witnesses  = new Witness[0];

            var gasConsumed = await EstimateGasAsync(tx.Script.ToHexString()); //todo add gas limit

            tx.Gas = InvocationTransaction.GetGas(Fixed8.FromDecimal(gasConsumed));

            tx = MakeTransaction(tx, AddressScriptHash, changeAddress, Fixed8.FromDecimal(fee));
            var success = await SignAndSendTransaction(tx);

            return(success ? tx : null);
        }