2 место Работаем с блокчейном напрямую

Zedx

Client
Регистрация
12.06.2018
Сообщения
1 177
Благодарностей
816
Баллы
113
В общем с подтверждением непонятно, в каких случаях оно нужно, по крайней мере твоя ошибка ведёт на эту тему. Сейчас сделал перевод кастомного токена используя свой код, который скинул выше - транзакция успешно прошла, кастомные токены перевелись.
Ну вот заодно и дописал переводы токенов, которые уже больше полугода не было времени доделать.
 
Последнее редактирование:

Porosenok

Client
Регистрация
26.09.2010
Сообщения
1 278
Благодарностей
96
Баллы
48
Да, BigInterer.

Ещё кстати необходимо добавить вот этот класс

C#:
[Function("transfer", "bool")]
public class TransferFunction : FunctionMessage
{
    [Parameter("address", "_to", 1)]
    public string To { get; set; }

    [Parameter("uint256", "_value", 2)]
    public BigInteger TokenAmount { get; set; }
}
решил пойти этим путем, но тоже не получается, добавляю этот класс в код и все подчеркивается. Как я понимаю нужен родительский класс FunctionMessage, а его нигде нет, в доках тож не получается найти
 

Zedx

Client
Регистрация
12.06.2018
Сообщения
1 177
Благодарностей
816
Баллы
113
решил пойти этим путем, но тоже не получается, добавляю этот класс в код и все подчеркивается. Как я понимаю нужен родительский класс FunctionMessage, а его нигде нет, в доках тож не получается найти
Добавь using Nethereum.Contracts;
 
  • Спасибо
Реакции: Porosenok

S10n4eg

Client
Регистрация
25.06.2014
Сообщения
186
Благодарностей
24
Баллы
18
А может кто-то знает как по api получать стоимость комисси в разных сетях? Подскажите пожалуйста)
 

Zedx

Client
Регистрация
12.06.2018
Сообщения
1 177
Благодарностей
816
Баллы
113
А может кто-то знает как по api получать стоимость комисси в разных сетях? Подскажите пожалуйста)
Через рассматриваемую тут библиотеку Nethereum точно можно - формируем транзакцию и на основе этой транзакции запрашиваем из сети количество необходимого газа
 
  • Спасибо
Реакции: S10n4eg

SlipDez

Client
Регистрация
18.07.2018
Сообщения
374
Благодарностей
70
Баллы
28
Zedx


Не подскажешь как получить сигнатуру данных такого вида?

112007


есть Http запрос на который уходят данные и сигнатура, не могу понять как она формируется


"data": {
"domain": {
"name": "snapshot",
"version": "0.1.4"
},
"types": {
"Vote": [{
"name": "from",
"type": "address"
}, {
"name": "space",
"type": "string"
}, {
"name": "timestamp",
"type": "uint64"
}, {
"name": "proposal",
"type": "bytes32"
}, {
"name": "choice",
"type": "uint32"
}, {
"name": "reason",
"type": "string"
}, {
"name": "app",
"type": "string"
}, {
"name": "metadata",
"type": "string"
}
]
},
"message": {
"space": "servise.eth",
"proposal": "0xcd749b3b1caea7f5f2ecc3d1270eb2f938819f015d4410e5e07f77cab47e9ec9",
"choice": 2,
"app": "appVote",
"reason": "",
"metadata": "{}",
"from": "0x8A652fd68C4702AB7Eeba24e21611c2B81dc4596",
"timestamp": 1695386652
}
}

Как я понимаю первая часть как бы ABI контракта но не могу додумать как сделать сигнатуру из этого. Единственое что приходит в голову это сгенерировать HEX из ABI и получить HEX по входным данным и ее уже подписать. Только как это кодом сделать не знаю

PS обычные голосования для SnapShot вот пример Тык
 
Последнее редактирование:

Zedx

Client
Регистрация
12.06.2018
Сообщения
1 177
Благодарностей
816
Баллы
113
Zedx


Не подскажешь как получить сигнатуру данных такого вида?

Посмотреть вложение 112007

есть Http запрос на который уходят данные и сигнатура, не могу понять как она формируется


"data": {
"domain": {
"name": "snapshot",
"version": "0.1.4"
},
"types": {
"Vote": [{
"name": "from",
"type": "address"
}, {
"name": "space",
"type": "string"
}, {
"name": "timestamp",
"type": "uint64"
}, {
"name": "proposal",
"type": "bytes32"
}, {
"name": "choice",
"type": "uint32"
}, {
"name": "reason",
"type": "string"
}, {
"name": "app",
"type": "string"
}, {
"name": "metadata",
"type": "string"
}
]
},
"message": {
"space": "servise.eth",
"proposal": "0xcd749b3b1caea7f5f2ecc3d1270eb2f938819f015d4410e5e07f77cab47e9ec9",
"choice": 2,
"app": "appVote",
"reason": "",
"metadata": "{}",
"from": "0x8A652fd68C4702AB7Eeba24e21611c2B81dc4596",
"timestamp": 1695386652
}
}

Как я понимаю первая часть как бы ABI контракта но не могу додумать как сделать сигнатуру из этого. Единственое что приходит в голову это сгенерировать HEX из ABI и получить HEX по входным данным и ее уже подписать. Только как это кодом сделать не знаю

PS обычные голосования для SnapShot вот пример Тык
Глянул быстренько. Получается в данном случае proposal = 0xcd749b3b1caea7f5f2ecc3d1270eb2f938819f015d4410e5e07f77cab47e9ec9 является адресом конкретного голосования, а choice = 2 это вариант ответа на него. Т.е. получается всего навсего, что в запросе передаём адрес страницы/голосования и вариант нужного ответа. Ничего кодировать/декодировать не надо
 
  • Спасибо
Реакции: edger

SlipDez

Client
Регистрация
18.07.2018
Сообщения
374
Благодарностей
70
Баллы
28
Глянул быстренько. Получается в данном случае proposal = 0xcd749b3b1caea7f5f2ecc3d1270eb2f938819f015d4410e5e07f77cab47e9ec9 является адресом конкретного голосования, а choice = 2 это вариант ответа на него. Т.е. получается всего навсего, что в запросе передаём адрес страницы/голосования и вариант нужного ответа. Ничего кодировать/декодировать не надо
Это да, но вся фишка в том что надо передавать Signature а она кодируется не как простое сообщение сериализованное а с спец образом. Спустя пол дня страданий и мучений поиска нашел что есть стандарт EIP 712 благо на c# меньше всего документации вот кстати этот using Nethereum.Signer.EIP712 сейчас пытаюсь вставить нужный тип значений в поля. Даже для python есть кусок кода который выполняет эту задачу вот он кстати
Python:
from web3.auto import w3
from eth_account.messages import encode_structured_data
import time
from loguru import logger
import aiohttp
import asyncio
from config import TIME, TIMEMAX, TIME_ERROR
import random
import ast
import json

logger.add(f'log.log')


def validation_type(type, choise):
    if type == 'uint32':
        return int(choise)
    if type == 'string':
        return json.dumps(dict(choise))
    choise = [int(ch) for ch in choise]
    return choise


# region form
def forma(address, signature, space, proposal, choice, timestamp, type_choice="uint32"):
    forma = {
        "address": address,
        "sig": signature,
        "data": {
            "domain": {
                "name": "snapshot",
                "version": "0.1.4"
            },
            "types": {
                "Vote": [{
                    "name": "from",
                    "type": "address"
                },
                    {
                        "name": "space",
                        "type": "string"
                    },
                    {
                        "name": "timestamp",
                        "type": "uint64"
                    },
                    {
                        "name": "proposal",
                        "type": "bytes32"
                    },
                    {
                        "name": "choice",
                        "type": type_choice
                    },
                    {
                        "name": "reason",
                        "type": "string"
                    },
                    {
                        "name": "app",
                        "type": "string"
                    },
                    {
                        "name": "metadata",
                        "type": "string"
                    }
                ]
            },
            "message": {
                "space": space,
                "proposal": proposal,
                "choice": validation_type(type_choice,choice),
                "app": "snapshot",
                "reason": "",
                "from": address,
                "timestamp": timestamp,
                'metadata': "{}"
            }
        }
    }
    return forma


# endregion

# region signature
def signature(address, space, proposal, choice, timestamp, key, type_choice="uint32"):
    sig_signature = {
        "domain": {
            "name": "snapshot",
            "version": "0.1.4"
        },
        "types": {
            "Vote": [
                {
                    "name": "from",
                    "type": "address"
                },
                {
                    "name": "space",
                    "type": "string"
                },
                {
                    "name": "timestamp",
                    "type": "uint64"
                },
                {
                    "name": "proposal",
                    "type": "bytes32"
                },
                {
                    "name": "choice",
                    "type": type_choice
                },
                {
                    "name": "reason",
                    "type": "string"
                },
                {
                    "name": "app",
                    "type": "string"
                },
                {
                    "name": "metadata",
                    "type": "string"
                }
            ],
            'EIP712Domain': [{'name': 'name', 'type': 'string'}, {'name': 'version', 'type': 'string'}]
        },
        'primaryType': "Vote",
        "message": {
            "space": space,
            "proposal": w3.toBytes(hexstr=proposal),
            "choice": validation_type(type_choice,choice),
            "app": "snapshot",
            "reason": "",
            "from": address,
            "timestamp": timestamp,
            'metadata': "{}"
        }
    }

    signature = (w3.eth.account.sign_message(encode_structured_data(primitive=sig_signature), key))['signature'].hex()
    return signature
 

Zedx

Client
Регистрация
12.06.2018
Сообщения
1 177
Благодарностей
816
Баллы
113
Это да, но вся фишка в том что надо передавать Signature а она кодируется не как простое сообщение сериализованное а с спец образом. Спустя пол дня страданий и мучений поиска нашел что есть стандарт EIP 712 благо на c# меньше всего документации вот кстати этот using Nethereum.Signer.EIP712 сейчас пытаюсь вставить нужный тип значений в поля. Даже для python есть кусок кода который выполняет эту задачу вот он кстати
Python:
from web3.auto import w3
from eth_account.messages import encode_structured_data
import time
from loguru import logger
import aiohttp
import asyncio
from config import TIME, TIMEMAX, TIME_ERROR
import random
import ast
import json

logger.add(f'log.log')


def validation_type(type, choise):
    if type == 'uint32':
        return int(choise)
    if type == 'string':
        return json.dumps(dict(choise))
    choise = [int(ch) for ch in choise]
    return choise


# region form
def forma(address, signature, space, proposal, choice, timestamp, type_choice="uint32"):
    forma = {
        "address": address,
        "sig": signature,
        "data": {
            "domain": {
                "name": "snapshot",
                "version": "0.1.4"
            },
            "types": {
                "Vote": [{
                    "name": "from",
                    "type": "address"
                },
                    {
                        "name": "space",
                        "type": "string"
                    },
                    {
                        "name": "timestamp",
                        "type": "uint64"
                    },
                    {
                        "name": "proposal",
                        "type": "bytes32"
                    },
                    {
                        "name": "choice",
                        "type": type_choice
                    },
                    {
                        "name": "reason",
                        "type": "string"
                    },
                    {
                        "name": "app",
                        "type": "string"
                    },
                    {
                        "name": "metadata",
                        "type": "string"
                    }
                ]
            },
            "message": {
                "space": space,
                "proposal": proposal,
                "choice": validation_type(type_choice,choice),
                "app": "snapshot",
                "reason": "",
                "from": address,
                "timestamp": timestamp,
                'metadata': "{}"
            }
        }
    }
    return forma


# endregion

# region signature
def signature(address, space, proposal, choice, timestamp, key, type_choice="uint32"):
    sig_signature = {
        "domain": {
            "name": "snapshot",
            "version": "0.1.4"
        },
        "types": {
            "Vote": [
                {
                    "name": "from",
                    "type": "address"
                },
                {
                    "name": "space",
                    "type": "string"
                },
                {
                    "name": "timestamp",
                    "type": "uint64"
                },
                {
                    "name": "proposal",
                    "type": "bytes32"
                },
                {
                    "name": "choice",
                    "type": type_choice
                },
                {
                    "name": "reason",
                    "type": "string"
                },
                {
                    "name": "app",
                    "type": "string"
                },
                {
                    "name": "metadata",
                    "type": "string"
                }
            ],
            'EIP712Domain': [{'name': 'name', 'type': 'string'}, {'name': 'version', 'type': 'string'}]
        },
        'primaryType': "Vote",
        "message": {
            "space": space,
            "proposal": w3.toBytes(hexstr=proposal),
            "choice": validation_type(type_choice,choice),
            "app": "snapshot",
            "reason": "",
            "from": address,
            "timestamp": timestamp,
            'metadata': "{}"
        }
    }

    signature = (w3.eth.account.sign_message(encode_structured_data(primitive=sig_signature), key))['signature'].hex()
    return signature
Да, вчера не увидел, что там ещё нужна и подпись. Сообщение для подписи можно вытащить из запроса и подставить свои значения, потом используя свой приватный ключ подписать это сообщение
burp.png
 

SlipDez

Client
Регистрация
18.07.2018
Сообщения
374
Благодарностей
70
Баллы
28
Да, вчера не увидел, что там ещё нужна и подпись. Сообщение для подписи можно вытащить из запроса и подставить свои значения, потом используя свой приватный ключ подписать это сообщение
Посмотреть вложение 112048
Я сначала тоже так подумал оказывается нет)
1)Сначала формируются данные тип значение
2) Этот объект (иногда сериализуется)
3) Приводится к нужному типу
3) Вычисляется хеш всего этого
4) Подписывается это все

Причем есть несколько способов как это все подписывать

То что +- написал(скопировал) но это не работает:
using Nethereum.ABI;
using Nethereum.Hex.HexConvertors.Extensions;
using Nethereum.Signer;
using Nethereum.Signer.EIP712;
using Nethereum.Util;
using Nethereum.Web3.Accounts;
using Org.BouncyCastle.Asn1.Ocsp;
using System;
using System.Collections.Generic;
using System.Text;

namespace EIP712Signature2
{
    class Program
    {
        static void Main(string[] args)
        {
            string address = "0x34d427B88fD947b92c1555051EDd686cDb840b97";
            string app = "snapshot";
            ulong timestamp = 1695435358;
            int choice = 1;
            string reason = "";
            string metadata = "{}";
            string space = "magicappstore.eth";

            var privKey = "privkey";
            string proposal = "0xcd749b3c1caea7f5f2ecc3d1270eb2f938819f015d4410e5e07f77cab47e9eb9";
            byte[] bytesProposal = proposal.HexToByteArray();
           


            var account = new Account(privKey);
            var typedData = new TypedData
            {
                Domain = new Domain
                {
                    Name = "snapshot",
                    Version = "0.1.4",
                },
                Types = new Dictionary<string, MemberDescription[]>
                {
                    ["EIP712Domain"] = new[]
                    {
                        new MemberDescription {Name = "snapshot", Type = "string"},
                        new MemberDescription {Name = "0.1.4", Type = "string"},
                    },
                    ["Vote"] = new[]
                    {
                        new MemberDescription {Name = "from", Type = "address"},
                        new MemberDescription {Name = "space", Type = "string"},
                        new MemberDescription {Name = "timestamp", Type = "uint64"},
                        new MemberDescription {Name = "proposal", Type = "bytes32"},
                        new MemberDescription {Name = "choice", Type = "uint32"},
                        new MemberDescription {Name = "reason", Type = "string"},
                        new MemberDescription {Name = "app", Type = "string"},
                        new MemberDescription {Name = "metadata", Type = "string"},

                    }
                },
                PrimaryType = "Vote",
                Message = new[]
                {
                    new MemberValue {TypeName = "string", Value = address},
                    new MemberValue {TypeName = "string", Value = app},
                    new MemberValue {TypeName = "uint64", Value = 1695405069},
                    new MemberValue {TypeName = "bytes32", Value = bytesProposal},
                    new MemberValue {TypeName = "uint32", Value = choice},
                    new MemberValue {TypeName = "string", Value = reason},
                    new MemberValue {TypeName = "string", Value = space},
                    new MemberValue {TypeName = "string", Value = metadata},
                }
            };
           
            var key = new EthECKey(privKey);
            var hashedData = Sha3Keccack.Current.CalculateHash(Eip712TypedDataSigner.Current.EncodeTypedData(typedData));
            var correctSignature = key.SignAndCalculateV(hashedData);
            var result = EthECDSASignature.CreateStringSignature(correctSignature);
            Console.WriteLine("Правильная сигнатура         " + result);


            var sig = Eip712TypedDataSigner.Current.SignTypedData(typedData, new EthECKey(account.PrivateKey));
            Console.WriteLine("siccccc  " + sig);
            //var parsedSignature = ParseSignature(sig);
            //Console.WriteLine(
            //    $"signature:\n{sig}\n" +
            //    $"SigR:\n{parsedSignature.R}\n" +
            //    $"SigS:\n{parsedSignature.S}\n" +
            //    $"SigV:\n{parsedSignature.V}\n"
            //    );
        }


        //private static Signature ParseSignature(string fullSig)
        //{
        //    if (fullSig.StartsWith("0x"))
        //        fullSig = fullSig[2..];
        //    return new Signature
        //    {
        //        R = "0x" + fullSig.Substring(0, 64),
        //        S = "0x" + fullSig.Substring(64, 64),
        //        V = Convert.ToByte(fullSig.Substring(128, 2), 16)
        //    };
        //}
    }
}
Есть документация вот тут как это все работает и как это все применить Тык Тык Тестовая форма на сайте для проверки
еще по js можно вытащить куски кода Тык
По c#
Тык Проект который генерирует данные но неправильные
Рабочий проект, но для меня сложноватый

К сожалению толкого ничего не получилось поэтому я остановился на том что выдернуть кусок кода из рабочего проекта(код который скидывал выше)
Прикрутил к зенке
1)создание bat в начале проекта который запускает main.py
2) Переписывание файла main.py когда необходимо сгенерировать данные и запустка bat
 
  • Спасибо
Реакции: evgenii2000

Zedx

Client
Регистрация
12.06.2018
Сообщения
1 177
Благодарностей
816
Баллы
113
Тут всё равно каждый раз нужно делать десериализацию данных вручную под разные случаи, какого-то универсального способа не нашёл, чтобы можно было просто скормить json
 

SlipDez

Client
Регистрация
18.07.2018
Сообщения
374
Благодарностей
70
Баллы
28
Немного не понял про какую десериализацию имеется ввиду
 

Zedx

Client
Регистрация
12.06.2018
Сообщения
1 177
Благодарностей
816
Баллы
113
Для каждого конкретного случая (для разных голосований) будет разный набор данных. Создаём нужные классы, присваиваем значения и уже это подписываем как в примере из песочницы nethereum. Ну возможно как-то можно это всё автоматизировать на C#, чтобы из запроса сразу брать json и его на лету преобразовывать, но в nethereum я не нашёл таких способов, хотя gpt-4 мне выдавало якобы рабочий код, где всё просто, но по факту он не работает.
 

SlipDez

Client
Регистрация
18.07.2018
Сообщения
374
Благодарностей
70
Баллы
28
Теперь понял, в основном провайдер голосований один это SnapShot можно сделать основу и передавать туда лишь разные значения. А если делать код под универсальную структуру я думаю это тоже можно, но это не мой уровень)

А в Netherum не знаю на сколько правильно работают методы потому что они не у меня не хочет работать код.Чтобы себя не мучить можно взять код js компилировать в Node js а запускать через bat)
 

kul0n

Client
Регистрация
10.03.2016
Сообщения
85
Благодарностей
14
Баллы
8
Теперь понял, в основном провайдер голосований один это SnapShot можно сделать основу и передавать туда лишь разные значения. А если делать код под универсальную структуру я думаю это тоже можно, но это не мой уровень)

А в Netherum не знаю на сколько правильно работают методы потому что они не у меня не хочет работать код.Чтобы себя не мучить можно взять код js компилировать в Node js а запускать через bat)
тогда уж проще взять уже готовое решение на питоне и запускать скрипт уже как удобно, в том числе и через зенку)
 

SlipDez

Client
Регистрация
18.07.2018
Сообщения
374
Благодарностей
70
Баллы
28
тогда уж проще взять уже готовое решение на питоне и запускать скрипт уже как удобно, в том числе и через зенку)
Ну фактически я так и сделал, просто взял скрипт переделал под себя и запускаю из ZP)
 

Vasyl1

Client
Регистрация
11.12.2016
Сообщения
194
Благодарностей
20
Баллы
18
Попробуй вот так:
C#:
var account = new Account(WalletKey, ChainId);
var web3 = new Web3(account, JsonRpc);
web3.TransactionManager.UseLegacyAsDefault = true;

var transactionMessage = new TransferFunction
{
    FromAddress = account.Address,
    To ="адрес получателя",
    TokenAmount = amount      // количество
};

var transferHandler = web3.Eth.GetContractTransactionHandler<TransferFunction>();
var transferReceipt = transferHandler.SendRequestAndWaitForReceiptAsync("адрес контракта токена", transactionMessage).Result;

var transaction = web3.Eth.Transactions.GetTransactionByHash.SendRequestAsync(transferReceipt.TransactionHash).Result;
Ещё есть костыльный способ - у каждого токена в контракте есть функция transfer, в которую передаётся адрес получателя и количество. Кодируешь эти данные, как я описывал в этой статье и отправляешь через эту функцию.
Посмотреть вложение 107559
Можете скинуть шаблон пожалуйста, а то я пытаюсь подставить у меня не получается.
 

Zedx

Client
Регистрация
12.06.2018
Сообщения
1 177
Благодарностей
816
Баллы
113
  • Спасибо
Реакции: Vasyl1

davidjame

Новичок
Регистрация
18.08.2021
Сообщения
6
Благодарностей
2
Баллы
3
I have been searching for this topic for a long time. While Python uses web3py very easily, C#'s Netherum is really difficult for beginners
 

SlipDez

Client
Регистрация
18.07.2018
Сообщения
374
Благодарностей
70
Баллы
28
I have been searching for this topic for a long time. While Python uses web3py very easily, C#'s Netherum is really difficult for beginners
Многие говорят что питон для новичков на порядок легче чем c#. На питоне за месяц уже можно проекты делать а на c# не получится так
 

davidjame

Новичок
Регистрация
18.08.2021
Сообщения
6
Благодарностей
2
Баллы
3
Многие говорят что питон для новичков на порядок легче чем c#. На питоне за месяц уже можно проекты делать а на c# не получится так
That's true. I can learn python from zero in 1 week and code web3py, but I can't learn c# even in 1-2 months. Without this nice guy, it seems I would still have to use a web app like Flask Python to connect zenno and Python and interact with Blockchain.
 
  • Спасибо
Реакции: Zedx и SlipDez

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)