Çağrı, transaction, event, filtre ve topic

Yazının orijinali için tıklayın.

Önceki yazıda bir kontratın nasıl deploy edilip çağrıldığını görmüştük. Bu yazıda ise kontrat, transaction, event ve topic çağırma hususunda biraz daha derine dalacağız.

Video

Bu yazıda anlatılanların işlendiği ingilizce demoyu bu videoda bulabilirsiniz.

Test Kontratı

Aşağıdaki akıllı kontrat, önceki yazıdaki “multiply” kontratının güncel bir versiyonu.

contract test {

    int _multiplier;
    event Multiplied(int indexed a, address indexed sender, int result );

    function test(int multiplier) {
        _multiplier = multiplier;
    }

    function multiply(int a) returns (int r) {
       r = a * _multiplier;
       Multiplied(a, msg.sender, r);
       return r;
    }
 }

Bu kontratta öncekinden farklı olarak bir “Multiplied” event’i var. Bu event, çarpma parametresi “a” yı, sender adresini ve çarpmanın sonucunu log’a kaydeder. “a” parametresi ve sender adresi indekslenir ki topic’leri kullanarak bu iki değer için özel filtreler üretebilelim.

Kontratı Deploy Etme

Önceki yazıda bahsedildiği gibi bir kontratı şöylece deploy edebiliriz.

    var senderAddress = "0x12890d2cce102216644c59daE5baed380d84830c";
    var password = "password";

    var abi = @"[{'constant':false,'inputs':[{'name':'a','type':'int256'}],'name':'multiply','outputs':[{'name':'r','type':'int256'}],'type':'function'},{'inputs':[{'name':'multiplier','type':'int256'}],'type':'constructor'},{'anonymous':false,'inputs':[{'indexed':true,'name':'a','type':'int256'},{'indexed':true,'name':'sender','type':'address'},{'indexed':false,'name':'result','type':'int256'}],'name':'Multiplied','type':'event'}]";

    var byteCode = "0x6060604052604051602080610104833981016040528080519060200190919050505b806000600050819055505b5060ca8061003a6000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480631df4f144146037576035565b005b604b60048080359060200190919050506061565b6040518082815260200191505060405180910390f35b60006000600050548202905080503373ffffffffffffffffffffffffffffffffffffffff16827f841774c8b4d8511a3974d7040b5bc3c603d304c926ad25d168dacd04e25c4bed836040518082815260200191505060405180910390a380905060c5565b91905056";

    var multiplier = 7;

    var web3 = new Web3.Web3();

    var unlockResult = await web3.Personal.UnlockAccount.SendRequestAsync(senderAddress, password, new HexBigInteger(120));
    Assert.True(unlockResult);

    var transactionHash = await web3.Eth.DeployContract.SendRequestAsync(abi, byteCode, senderAddress, new HexBigInteger(900000), multiplier);
    var receipt = await MineAndGetReceiptAsync(web3, transactionHash);

Multiply Transaction’ı

Bir çağrı yaptığımızda ya akıllı kontrat state’inde tutulan bir veriyi getiriyoruzdur veya bir action icra ediyoruzdur(çarpma gibi mesela), dolayısıyla çağrılar transaction değillerdir ve blokzincir konsensusu ile doğrulanmazlar.

Akıllı kontratta bir fonksiyon operasyonu icra etmek için transaction yollamak, operasyonun sonucunu döndürmeyecektir, bunun yerine bilgiyi çekmek için event’ler kullanabilir veya fonksiyon çağrılarıyla akıllı kontratın state’ini inceleyebiliriz.

    var contractAddress = receipt.ContractAddress;

    var contract = web3.Eth.GetContract(abi, contractAddress);

    var multiplyFunction = contract.GetFunction("multiply");

    transactionHash = await multiplyFunction.SendTransactionAsync(senderAddress, 7);
    transactionHash = await multiplyFunction.SendTransactionAsync(senderAddress, 8);

    receipt = await MineAndGetReceiptAsync(web3, transactionHash);

Transaction’ı deploy ettikten sonra kontrat adresini kullanarak bir kontrat objesi instance’ı ve “multiply” fonksiyonu instance’ı oluşturabiliriz.

Fonksiyon objesi, transaction göndermeyi çağrı çağırmak gibi kolay kılar. Yukarıda örnekte görüldüğü gibi yalnızca operasyonun mal olduğu gaz miktarının kesileceği “senderAddress” i ve fonksiyon operasyonu için gerekli parametreleri yollamamız yeterlidir.

Ayrıca transaction’ın bir parçası olarak gaz değerinin belirtilmesi veya Ether değerinin eklenmesi de seçenekler arasındadır.

Örnekte sırasıyla 7 ve 8 ile çarpma yapmak için iki transaction yolladık ve özel test ağımızda kazılmalarını bekledik.

Event’ler, filtreler ve topic’ler

Event ve filtre oluşturma

Eventler abi’nin bir kısmı olarak tanımlanır ve fonksiyonlara benzer şekilde kontrat instance’ımızı kullanarak event’leri elde edebiliriz.

 var multiplyEvent = contract.GetEvent("Multiplied");

Event objesi, log’da tutulan bilgiyi elde etmek için filtre oluşturmaya yarar.

Bütün event log’larını getirecek filtreler de yaratabiliriz.

var filterAll = await multiplyEvent.CreateFilterAsync();

Ya da spesifik bir topic için,

var filter7 = await multiplyEvent.CreateFilterAsync(7);

Yukarıdaki örnekte multiply parametresi 7 olan log’ları getiriyoruz, çarpma için verdiğimiz input parametresi indexed olarak işaretli olduğu için bu topic’e göre filtreleme yapabiliyoruz.

Benzer şekilde indexed olarak işaretlendiği için sender address’ine göre bir filtre de oluşturabilirdik ama bu spesifik topic’e göre filtrelemek istersek, filtre oluştururken ikinci parametreyi kullanmamız gerekir.

var filterSender = await multiplyEvent.CreateFilterAsync(null, senderAddress);

Event DTO

Event veri transfer objeleri, aynen bir JSON objesini deserialize etmemiz gibi tüm event parametrelerinin basitçe bir transfer objesine decode edilmesini sağlar.

 public class MultipliedEvent
 {
    [Parameter("int", "a", 1, true)]
    public int MultiplicationInput {get; set;}

    [Parameter("address", "sender", 2, true)]
    public string Sender {get; set;}

    [Parameter("int", "result", 3, false)]
    public int Result {get; set;}

 }

Yukarıdaki örnekte “MultipliedEvent” özellikleri özel parametre nitelikleriyle birlikte event parametrelerine eşlenmiştir. Her parametre, orijinal tür, isim, sıra ve index durumunu belirtir. Gördüğümüz gibi adres gibi türler stringlere decode edilmiştir ve bizim senaryomuzda int256’yı int32’ye decode etmekte beis yoktur. Ama genel durum için BigInteger türü daha iyi bir seçenek olurdu.

Event ve Logları Getirme

Oluşturduğumuz filtrelerle logları ve eventleri getirebiliriz.

 var log = await multiplyEvent.GetFilterChanges<MultipliedEvent>(filterAll);
 var log7 = await multiplyEvent.GetFilterChanges<MultipliedEvent>(filter7);

Yukarıda “GetFilterChanges” kullandık, ki bu, filtre oluşturulduğundan beri veya değişiklikleri almaya çalıştığımız son kezden beri kriterlerimize uyan herhangi bir logu getirmekte kullanılabilir. Başka bir seçenek “FilterInput” ile beraber “GetAllChanges” kullanmak olurdu.

Nihai Kod

Kaynak kodunu Tutorial çözümlerinde “CallTransactionEvents” in altında bulabilirsiniz.

    public async Task ShouldBeAbleCallAndReadEventLogs()
    {
        var senderAddress = "0x12890d2cce102216644c59daE5baed380d84830c";
        var password = "password";

        var abi = @"[{'constant':false,'inputs':[{'name':'a','type':'int256'}],'name':'multiply','outputs':[{'name':'r','type':'int256'}],'type':'function'},{'inputs':[{'name':'multiplier','type':'int256'}],'type':'constructor'},{'anonymous':false,'inputs':[{'indexed':true,'name':'a','type':'int256'},{'indexed':true,'name':'sender','type':'address'},{'indexed':false,'name':'result','type':'int256'}],'name':'Multiplied','type':'event'}]";

        var byteCode = "0x6060604052604051602080610104833981016040528080519060200190919050505b806000600050819055505b5060ca8061003a6000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480631df4f144146037576035565b005b604b60048080359060200190919050506061565b6040518082815260200191505060405180910390f35b60006000600050548202905080503373ffffffffffffffffffffffffffffffffffffffff16827f841774c8b4d8511a3974d7040b5bc3c603d304c926ad25d168dacd04e25c4bed836040518082815260200191505060405180910390a380905060c5565b91905056";

        var multiplier = 7;

        var web3 = new Web3.Web3();

        var unlockResult = await web3.Personal.UnlockAccount.SendRequestAsync(senderAddress, password, new HexBigInteger(120));
        Assert.True(unlockResult);

        var transactionHash = await web3.Eth.DeployContract.SendRequestAsync(abi, byteCode, senderAddress, new HexBigInteger(900000), multiplier);
        var receipt = await MineAndGetReceiptAsync(web3, transactionHash);

        var contractAddress = receipt.ContractAddress;

        var contract = web3.Eth.GetContract(abi, contractAddress);

        var multiplyFunction = contract.GetFunction("multiply");

        var multiplyEvent = contract.GetEvent("Multiplied");

        var filterAll = await multiplyEvent.CreateFilterAsync();
        var filter7 = await multiplyEvent.CreateFilterAsync(7);

        transactionHash = await multiplyFunction.SendTransactionAsync(senderAddress, 7);
        transactionHash = await multiplyFunction.SendTransactionAsync(senderAddress, 8);

        receipt = await MineAndGetReceiptAsync(web3, transactionHash);


        var log = await multiplyEvent.GetFilterChanges<MultipliedEvent>(filterAll);
        var log7 = await multiplyEvent.GetFilterChanges<MultipliedEvent>(filter7);

        Assert.Equal(2, log.Count);
        Assert.Equal(1, log7.Count);
        Assert.Equal(7, log7[0].Event.MultiplicationInput);
        Assert.Equal(49, log7[0].Event.Result);
    }

    //event Multiplied(int indexed a, address indexed sender, int result );

    public class MultipliedEvent
    {
        [Parameter("int", "a", 1, true)]
        public int MultiplicationInput {get; set;}

        [Parameter("address", "sender", 2, true)]
        public string Sender {get; set;}

        [Parameter("int", "result", 3, false)]
        public int Result {get; set;}

    }


    public async Task<TransactionReceipt> MineAndGetReceiptAsync(Web3.Web3 web3, string transactionHash){

        var miningResult = await web3.Miner.Start.SendRequestAsync(6);
        Assert.True(miningResult);

        var receipt = await web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(transactionHash);

        while(receipt == null){
            Thread.Sleep(1000);
            receipt = await web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(transactionHash);
        }

        miningResult = await web3.Miner.Stop.SendRequestAsync();
        Assert.True(miningResult);
        return receipt;
    }
  • Ahmet Bilal Uçan

Leave a Comment

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir