public async Task Test_Fees() { var(abi, tvc) = TestClient.Package("GiverV2", 2); var keys = await _client.Crypto.GenerateRandomSignKeysAsync(); var address = await _client.DeployWithGiverAsync(new ParamsOfEncodeMessage { Abi = abi, DeploySet = new DeploySet { Tvc = tvc }, CallSet = new CallSet { FunctionName = "constructor" }, Signer = new Signer.Keys { KeysProperty = keys } }); var @params = new ParamsOfEncodeMessage { Abi = abi, Address = address, CallSet = new CallSet { FunctionName = "sendTransaction", Input = new { dest = address, value = 100000000u, bounce = false }.ToJson() }, Signer = new Signer.Keys { KeysProperty = keys } }; var account = (await _client.FetchAccountAsync(address))["boc"]?.ToString(); var message = await _client.Abi.EncodeMessageAsync(@params); var localResult = await _client.Tvm.RunExecutorAsync(new ParamsOfRunExecutor { Account = new AccountForExecutor.Account { Boc = account }, Message = message.Message }); var runResult = await _client.Processing.ProcessMessageAsync(new ParamsOfProcessMessage { MessageEncodeParams = @params, SendEvents = false }); Assert.Equal(localResult.Fees.GasFee, runResult.Fees.GasFee); Assert.Equal(localResult.Fees.OutMsgsFwdFee, runResult.Fees.OutMsgsFwdFee); Assert.Equal(localResult.Fees.InMsgFwdFee, runResult.Fees.InMsgFwdFee); Assert.Equal(localResult.Fees.TotalOutput, runResult.Fees.TotalOutput); Assert.Equal(localResult.Fees.TotalOutput, new BigInteger(100000000u)); Assert.Equal(localResult.Fees.TotalAccountFees - localResult.Fees.StorageFee, localResult.Fees.TotalAccountFees - localResult.Fees.StorageFee); Assert.True(runResult.Fees.StorageFee >= localResult.Fees.StorageFee); Assert.True(runResult.Fees.OutMsgsFwdFee > 0); Assert.True(runResult.Fees.InMsgFwdFee > 0); Assert.True(runResult.Fees.TotalAccountFees > 0); }
public async Task Should_Suspend_Resume() { var keys = await _client.Crypto.GenerateRandomSignKeysAsync(); var(abi, tvc) = TestClient.Package("Hello"); var deployParams = new ParamsOfEncodeMessage { Abi = abi, DeploySet = new DeploySet { Tvc = tvc }, Signer = new Signer.Keys { KeysProperty = keys }, CallSet = new CallSet { FunctionName = "constructor" } }; var msg = await _client.Abi.EncodeMessageAsync(deployParams); var address = msg.Address; var transactionIds = new List <string>(); var notifications = new List <ClientError>(); var subscriptionClient = TestClient.Create(_logger); var handle = await subscriptionClient.Net.SubscribeCollectionAsync(new ParamsOfSubscribeCollection { Collection = "transactions", Filter = new { account_addr = new { eq = address }, status = new { eq = 3 } // Finalized }.ToJson(), Result = "id account_addr" }, (json, result) => { switch (result) { case 100: // OK transactionIds.Add((string)json.SelectToken("result.id")); break; case 101: // Error var clientError = new TonSerializer(_logger).Deserialize <ClientError>(json); notifications.Add(clientError); break; default: throw new NotSupportedException($"Response code ${result} not supported"); } return(Task.CompletedTask); }); // send grams to create first transaction await _client.GetGramsFromGiverAsync(msg.Address); await Task.Delay(TimeSpan.FromSeconds(1)); // check that transaction is received Assert.Single(transactionIds); // and no error notifications Assert.Empty(notifications); // suspend subscription await subscriptionClient.Net.SuspendAsync(); // deploy to create second transaction await _client.Processing.ProcessMessageAsync(new ParamsOfProcessMessage { MessageEncodeParams = deployParams, SendEvents = false }); // create second subscription while network is suspended var handle2 = await subscriptionClient.Net.SubscribeCollectionAsync(new ParamsOfSubscribeCollection { Collection = "transactions", Filter = new { account_addr = new { eq = msg.Address }, status = new { eq = 3 } // Finalized }.ToJson(), Result = "id account_addr" }, (json, result) => { switch (result) { case 100: // OK transactionIds.Add((string)json.SelectToken("result.id")); break; case 101: // Error var clientError = new TonSerializer(_logger).Deserialize <ClientError>(json); notifications.Add(clientError); break; default: throw new NotSupportedException($"Response code ${result} not supported"); } return(Task.CompletedTask); }); await Task.Delay(TimeSpan.FromMilliseconds(500)); // check that second transaction is not received when subscription suspended Assert.Single(transactionIds); Assert.Equal(2, notifications.Count); Assert.All(notifications, n => Assert.Equal((uint)NetErrorCode.NetworkModuleSuspended, n.Code)); // resume subscription await subscriptionClient.Net.ResumeAsync(); // run contract function to create new transaction await subscriptionClient.Processing.ProcessMessageAsync(new ParamsOfProcessMessage { MessageEncodeParams = new ParamsOfEncodeMessage { Abi = abi, Signer = new Signer.Keys { KeysProperty = keys }, Address = msg.Address, CallSet = new CallSet { FunctionName = "touch" } }, SendEvents = false }); // give some time for subscription to receive all data await Task.Delay(TimeSpan.FromSeconds(10)); // check that third transaction is now received after resume Assert.Equal(3, transactionIds.Count); Assert.Equal(2, transactionIds.Distinct().Count()); // and both subscriptions received notification about resume Assert.Equal(4, notifications.Count); Assert.Equal(2, notifications.Count(n => n.Code == (uint)NetErrorCode.NetworkModuleSuspended)); Assert.Equal(2, notifications.Count(n => n.Code == (uint)NetErrorCode.NetworkModuleResumed)); await subscriptionClient.Net.UnsubscribeAsync(handle); await subscriptionClient.Net.UnsubscribeAsync(handle2); }
public async Task SubscribeForTransactionsWithAddresses() { KeyPair keys = await _tonClient.Crypto.GenerateRandomSignKeys(); ITonClient subscriptionClient = _fixture.CreateClient(_outputHelper, true); var deployParams = new ParamsOfEncodeMessage { Abi = TestsEnv.Packages.Hello.Abi, DeploySet = new DeploySet { Tvc = TestsEnv.Packages.Hello.Tvc }, Signer = new Signer.Keys { KeysAccessor = keys }, CallSet = new CallSet { FunctionName = "constructor" } }; ResultOfEncodeMessage msg = await _tonClient.Abi.EncodeMessage(deployParams); var transactions = new List <string>(); var errorCodes = new List <uint>(); var @lock = new object(); var address = msg.Address; var callback = new Action <JsonElement, uint>((serdeJson, responseType) => { switch ((SubscriptionResponseType)responseType) { case SubscriptionResponseType.Ok: JsonElement resultOk = serdeJson.GetProperty("result"); resultOk.Get <string>("account_addr").Should().Be(address); lock (@lock) { transactions.Add(resultOk.Get <string>("id")); } break; case SubscriptionResponseType.Error: var error = serdeJson.ToObject <ClientError>(); _outputHelper.WriteLine($">> {error}"); lock (@lock) { errorCodes.Add(error.Code); } break; default: throw new TonClientException($"Unknown SubscriptionResponseType: {responseType}"); } }); //act ResultOfSubscribeCollection handle1 = await subscriptionClient.Net.SubscribeCollection(new ParamsOfSubscribeCollection { Collection = "transactions", Filter = new { account_addr = new { eq = address }, status = new { eq = (int)TransactionProcessingStatus.Finalized } }.ToJsonElement(), Result = "id account_addr" }, callback); // send grams to create first transaction await _tonClient.SendGramsFromLocalGiver(address); // give some time for subscription to receive all data await Task.Delay(TimeSpan.FromSeconds(1)); var transactionCount1 = transactions.Count; // suspend subscription await subscriptionClient.Net.Suspend(); await Task.Delay(TimeSpan.FromSeconds(1)); // deploy to create second transaction await _tonClient.Processing.ProcessMessage(new ParamsOfProcessMessage { MessageEncodeParams = deployParams, SendEvents = false }); //act ResultOfSubscribeCollection handle2 = await subscriptionClient.Net.SubscribeCollection(new ParamsOfSubscribeCollection { Collection = "transactions", Filter = new { account_addr = new { eq = address }, status = new { eq = (int)TransactionProcessingStatus.Finalized } }.ToJsonElement(), Result = "id account_addr" }, callback); await Task.Delay(TimeSpan.FromSeconds(1)); // check that second transaction is not received when subscription suspended var transactionCount2 = transactions.Count; // resume subscription await subscriptionClient.Net.Resume(); await Task.Delay(TimeSpan.FromSeconds(1)); // run contract function to create third transaction await _tonClient.Processing.ProcessMessage(new ParamsOfProcessMessage { MessageEncodeParams = new ParamsOfEncodeMessage { Abi = TestsEnv.Packages.Hello.Abi, Signer = new Signer.Keys { KeysAccessor = keys }, Address = address, CallSet = new CallSet { FunctionName = "touch" } }, SendEvents = false }); // give some time for subscription to receive all data await Task.Delay(TimeSpan.FromSeconds(1)); await subscriptionClient.Net.Unsubscribe(new ResultOfSubscribeCollection { Handle = handle1.Handle }); await subscriptionClient.Net.Unsubscribe(new ResultOfSubscribeCollection { Handle = handle2.Handle }); //check count before suspending transactionCount1.Should().Be(1); //check count before resume transactionCount2.Should().Be(1); // check that third transaction is now received after resume transactions.Count.Should().Be(3); transactions[0].Should().NotBe(transactions[2]); // check errors errorCodes.Count.Should().Be(4); errorCodes.Take(2).Should().AllBeEquivalentTo((uint)NetErrorCode.NetworkModuleSuspended); errorCodes.TakeLast(2).Should().AllBeEquivalentTo((uint)NetErrorCode.NetworkModuleResumed); }
/// <summary> /// <para>Encodes an ABI-compatible message</para> /// <para>Allows to encode deploy and function call messages,</para> /// <para>both signed and unsigned.</para> /// <para>Use cases include messages of any possible type:</para> /// <para>- deploy with initial function call (i.e. `constructor` or any other function that is used for some kind</para> /// <para>of initialization);</para> /// <para>- deploy without initial function call;</para> /// <para>- signed/unsigned + data for signing.</para> /// <para>`Signer` defines how the message should or shouldn't be signed:</para> /// <para>`Signer::None` creates an unsigned message. This may be needed in case of some public methods,</para> /// <para>that do not require authorization by pubkey.</para> /// <para>`Signer::External` takes public key and returns `data_to_sign` for later signing.</para> /// <para>Use `attach_signature` method with the result signature to get the signed message.</para> /// <para>`Signer::Keys` creates a signed message with provided key pair.</para> /// <para>[SOON] `Signer::SigningBox` Allows using a special interface to implement signing</para> /// <para>without private key disclosure to SDK. For instance, in case of using a cold wallet or HSM,</para> /// <para>when application calls some API to sign data.</para> /// <para>There is an optional public key can be provided in deploy set in order to substitute one</para> /// <para>in TVM file.</para> /// <para>Public key resolving priority:</para> /// <para>1. Public key from deploy set.</para> /// <para>2. Public key, specified in TVM file.</para> /// <para>3. Public key, provided by signer.</para> /// </summary> public async Task <ResultOfEncodeMessage> EncodeMessage(ParamsOfEncodeMessage @params, CancellationToken cancellationToken = default) { return(await _tonClientAdapter.Request <ParamsOfEncodeMessage, ResultOfEncodeMessage>("abi.encode_message", @params, cancellationToken)); }
public async Task Consume(ConsumeContext <FreeTonDeploy> context) { var cancellationToken = context.CancellationToken; var phrase = context.Message.Phrase; var keyPair = await _tonClient.Crypto.MnemonicDeriveSignKeys(new ParamsOfMnemonicDeriveSignKeys { Phrase = phrase }, cancellationToken); var contract = await _tonPackageManager.LoadPackage(SafeMultisigWallet); var paramsOfEncodedMessage = new ParamsOfEncodeMessage { Abi = contract.Abi, DeploySet = new DeploySet { Tvc = contract.Tvc, InitialData = new { }.ToJsonElement() }, CallSet = new CallSet { FunctionName = "constructor", Input = new { owners = new[] { $"0x{keyPair.Public}" }, reqConfirms = 0 }.ToJsonElement() }, Signer = new Signer.Keys { KeysAccessor = keyPair }, ProcessingTryIndex = 1 }; var encodeMessage = await _tonClient.Abi.EncodeMessage(paramsOfEncodedMessage, cancellationToken); var address = encodeMessage.Address; var result = await _tonClient.Net.QueryCollection(new ParamsOfQueryCollection { Collection = "accounts", Filter = new { id = new { eq = address } }.ToJsonElement(), Result = "acc_type balance" }, cancellationToken); if (result.Result.Length == 0) { await context.RespondAsync(new FreeTonDeployResult { Success = false, Balance = 0, Error = $"You need to transfer at least 0.5 tokens for deploy to {address}", Address = address, KeyPair = keyPair }); return; } var account = result.Result[0]; var balance = new BigInteger(Convert.ToUInt64(account.Get <string>("balance"), 16)).ToDecimalBalance(); var accType = account.Get <int>("acc_type"); switch (accType) { case 0 when balance < (decimal)0.5: await context.RespondAsync(new FreeTonDeployResult { Success = false, Error = $"You need to transfer at least 0.5 tokens for deploy to {address}", Balance = balance, Address = address, KeyPair = keyPair }); return; case 1: await context.RespondAsync(new FreeTonDeployResult { Success = true, Balance = balance, Address = address, KeyPair = keyPair }); return; } await _tonClient.Processing.ProcessMessage(new ParamsOfProcessMessage { SendEvents = false, MessageEncodeParams = paramsOfEncodedMessage }, cancellationToken : cancellationToken); var accBalance = await _tonClient.Net.QueryCollection(new ParamsOfQueryCollection { Collection = "accounts", Filter = new { id = new { eq = address } }.ToJsonElement(), Result = "balance" }, cancellationToken); balance = new BigInteger(Convert.ToUInt64(accBalance.Result[0].Get <string>("balance"), 16)).ToDecimalBalance(); await context.RespondAsync(new FreeTonDeployResult { Success = true, Balance = balance, Address = address, KeyPair = keyPair }); }