Мы пытаемся интегрировать web3js с Trezor в сеть разработки truffle или с помощью тестовой сети ropsten .
Идея состоит в том, чтобы подписывать транзакции с помощью аппаратного кошелька, а затем отправлять необработанные транзакции с помощью web3js.
Мы получаем, что у нас нет баланса для совершения транзакции, вероятно, потому, что web3js не использует одну из 10 учетных записей трюфелей и использует адрес трезора, которого нет в моей локальной сети .
На ropsten у меня есть несколько эфиров, и я получаю «неверный адрес»
Есть ли способ отправить подписанные транзакции (с помощью trezor) с помощью web3js в сеть разработки трюфелей? я имею в виду, есть ли способ включить адрес trezor в сеть truffle?
Ситуация с трюфелем более подробно описана здесь, но вопрос можно обобщить так: « Есть ли способ включить аппаратные кошельки в сеть разработки трюфелей? »: https://github.com/trufflesuite/truffle/issues/973 .
Используя ropsten, нам удалось отправить транзакцию и получить хэш транзакции в обратном вызове, но если мы запросим эту транзакцию, мы получим, что транзакция не существует... так что... как это возможно?
Мы также пытались развернуть контракт в Ropsten, и теперь мы получаем «Неверный адрес» при вызове функции смарт-контракта. Может быть, функция подписи неверна? кто-нибудь может интегрировать подписание транзакций Trezor с web3js?
Ребята, вы видите что-то неправильное в процессе подписания и отправки, которому мы следовали? Возможно, что-то не так с обработкой параметров R, V и S.
Еще одна важная вещь заключается в том, что мы используем https://github.com/ethereumjs/ethereumjs-tx для создания необработанных транзакций.
Проблемы, опубликованные в web3js, truffle и trezzor, связаны с дополнительной информацией:
с уважением
trezorLogin = async()=> {
let trezor= await this.getTrezor();
// site icon, optional. at least 48x48px
var hosticon = 'https://doc.satoshilabs.com/trezor-apps/_images/copay_logo.png';
// server-side generated and randomized challenges
var challenge_hidden = '';
var challenge_visual = '';
//use anonimous functions on callback otherwise returns cross origin errors
trezor.requestLogin(hosticon, challenge_hidden, challenge_visual, function (result){
if (result.success) {
console.log('Public key:', result.public_key); // pubkey in hex
console.log('Signature:', result.signature); // signature in hex
console.log('Version 2:', result.version === 2); // version field
console.log(result);
}else {
console.error('Error:', result.error);
}
});}
trezorSignTx= async(transaction)=> {
let trezor= await this.getTrezor();
// spend one change output
var address_n = "m/44'/60'/0'/0/0"
// var address_n = [44 | 0x80000000,
// 60 | 0x80000000,
// 0 | 0x80000000 ,
// 0 ]; // same, in raw form
var nonce = transaction.nonce.substring(2); // note - it is hex, not number!!!
var gas_price = transaction.gasPrice.substring(2);
var gas_limit = transaction.gasLimit.substring(2);
var to = transaction.to.substring(2);
// var value = '01'; // in hexadecimal, in wei - this is 1 wei
var value = transaction.value.substring(2); // in hexadecimal, in wei - this is about 18 ETC
var data = transaction.data.substring(2); // some contract data
// var data = null // for no data
var chain_id = 5777; // 1 for ETH, 61 for ETC
return new Promise (function (resolve,reject) {
trezor.ethereumSignTx(
address_n,
nonce,
gas_price,
gas_limit,
to,
value,
data,
chain_id,
function (response) {
if (response.success) {
console.log('Signature V (recovery parameter):', response.v); // number
console.log('Signature R component:', response.r); // bytes
console.log('Signature S component:', response.s); // bytes
resolve(response);
} else {
console.error('Error:', response.error); // error message
resolve(null);
}
});
})
}
getTrezorAddress = async() => {
let trezor= await this.getTrezor();
// spend one change output
var address_n = "m/44'/60'/0'/0/0";
trezor.ethereumGetAddress(address_n, function (result) {
if (result.success) { // success
console.log('Address: ', result.address);
} else {
console.error('Error:', result.error); // error message
}
});
}
getTrezor = async() => {
let trezorC;
await getTrezorConnect
.then(trezorConnect => {
trezorC= trezorConnect;
})
.catch((error) => {
console.log(error)
})
return trezorC;
}
sendTransaction= async(address, amount, id)=>{
let tokenInstance = this.props.smartContractInstance;
var getData = tokenInstance.mint.getData(address, amount);
var tx = {
nonce: '0x00',
gasPrice: '0x09184e72a000',
gasLimit: '0x2710',
to: CONTRACT_ADDRESS,
value: '0x00',
from:CONTRACT_OWNER_ADDRESS,
data: getData
};
let response = await this.trezorSignTx(tx);
let web3;
let _this = this;
if (response!=null){
getWeb3
.then(results => {
web3= results.web3;
let v = response.v.toString();
if (v.length % 2 != 0){
v="0"+v;
}
tx.r=Buffer.from(response.r,'hex');
tx.v=Buffer.from(v,'hex');
tx.s=Buffer.from(response.s,'hex');
let ethtx = new ethereumjs(tx);
console.dir(ethtx.getSenderAddress().toString('hex'), );
const serializedTx = ethtx.serialize();
const rawTx = '0x' + serializedTx.toString('hex');
console.log(rawTx);
//finally pass this data parameter to send Transaction
web3.eth.sendRawTransaction(rawTx, function (error, result) {
if(!error){
_this.props.addTokens(id)
.then(()=>{
_this.setState({modalOpen: true});
_this.props.getAllTransactions();
}
);
}else{
alert(error)
}
});
})
.catch((error) => {
console.log(error)
})
}else{
alert("There was an error signing with trezor hardware wallet")
}
}
Функция getTrezorConnect просто получает window.trezorConnect асинхронно, потому что объект вводится как скрипт
<script src="https://connect.trezor.io/4/connect.js"></script>
У вас много вопросов в списке. Лучше публиковать по одному вопросу за раз, чтобы увеличить ваши шансы получить на них ответ.
Позвольте мне коснуться самых важных из них .
Q1. Есть ли способ включить аппаратные кошельки в сеть разработки трюфелей?
Да, используя truffle console
и настроив его для подключения к testrpc
. С testrpc
, вы можете иметь любую учетную запись, которую вы хотите финансировать (отредактируйте: это неправда - учетные записи на самом деле являются закрытыми ключами, которые недоступны при использовании кошелька HW), запустив ее следующим образом:
testrpc --account="0x8414315fe005b8f294020dfc61cfd13749fbc045b0c6abc31fbd1ee3f4ff3b41, 10000000000000000000" --account="0x566a9022cd3f0dfcc3dff657a6c578897d4b0300e335fa569a082b637e6bb273, 70000000000000000000" --account="0x90b4e47ca43b66fab5dbebfee464087b51923f73f649701ca485da313574fd5b, 80000000000000000000" --account="0x5d47b245c405d706fecbc5eb213819d20a2168ad696b352644ad0ffc87aef18e, 90000000000000000000"
Где адреса — это ваши трезор-адреса.
Или вы можете запустить его с помощью сида вашего трезора (я не рекомендую этого, если вы точно не знаете, что трезор используется в живой сети):
testrpc -m 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'
Q2: Ребята, вы видите что-то неправильное в процессе подписания и отправки, которому мы следовали?
Мне удалось программно подписать транзакции eth с помощью аппаратного кошелька Ledger Nano S. Библиотека ledgerco js, которую я использовал для подписи транзакций, также возвращает параметры V, R и S. Я предполагаю, что они в том же формате, что и файлы, возвращаемые библиотекой Трезора, но я не могу быть в этом уверен. Во всяком случае, вот как я использую V, R, S для создания действительных транзакций:
console.log('Please sign transaction on device...');
//the signature is an object with keys v,r and s
const signature = await eth_utils.ledger.signTransaction_async(argv['derivation_path'], tx.serialize().toString('hex'));
//"hexify" the keys
Object.keys(signature).map( (key, index) => {
signature[key] = '0x'+signature[key];
});
//tx_raw is a js object that contains all the tx params, the one that was signed on the hw device
//(equivalent of your tx from your sendTransaction() function)
const tx_obj = { ...tx_raw, ...signature};
//re-create the Transaction using ethereumjs-tx
const signed_tx = new Transaction( tx_obj );
//signed_tx_hex needs to be broadcasted
const signed_tx_hex = '0x'+signed_tx.serialize().toString('hex');
Вот и все.
Что ж, после долгих попыток нам удалось отправить необработанную транзакцию, подписанную с помощью Trezor, в Ropsten, основываясь на помощи Tudor Constantin:
https://ropsten.etherscan.io/address/0x89e2c46b22881f747797cf67310aad1a831d50b7
Это то, что я изменил, чтобы сделать возможной отправку подписанных транзакций в тестовую сеть Ropsten.
Это предполагает, что ваш контракт развернут в Ropsten и у вас есть адрес контракта.
1) Получите адрес вашей учетной записи Trezor
getTrezorAddress = async() => {
let trezor= await this.getTrezor();
// spend one change output
var address_n = "m/44'/1'/0'/0/0";
trezor.ethereumGetAddress(address_n, function (result) {
if (result.success) { // success
console.log('Address: ', result.address);
} else {
console.error('Error:', result.error); // error message
}
});
}
2) Поместите адрес trezor в from
поле вашей необработанной транзакции, получите nonce
транзакцию, получив количество транзакций для этого адреса. Важно: используйте необязательный параметр «ожидание» в getTransactionCount, чтобы получить все транзакции учетной записи, иначе вы будете перезаписывать ожидающие транзакции.
getNonce = async(address) => {
let web3 = await this.getWeb3();
return new Promise (function (resolve,reject) {
web3.eth.getTransactionCount(address, "pending", function (error,result){
console.log("Nonce "+result);
resolve(result);
});
});
}
let count = null;
await this.getNonce("0xedff546ac229317df81ef9e6cb3b67c0e6425fa7").then(result => {
if(result.length % 2 !==0){
result = "0"+result;
}
count = "0x"+result;
});
var tx = {
nonce: count ,
gasPrice: web3.toHex(gasPriceGwei*1e9),
gasLimit: web3.toHex(gasLimit),
to: CONTRACT_ADDRESS,
value: '0x00',
data: getData,
chainId:chainId,
from:"yourTrezzorAddress"
};
3) Параметры r, s, v были неверными, правильный способ справиться с ними — взять эти значения для ответа трезора и просто преобразовать их в шестнадцатеричный формат:
// response is the Trezor sign response
tx.v= response.v;
tx.r="0x"+response.r;
tx.s="0x"+response.s;
let ethtx = new ethereumjs(tx);.
const serializedTx = ethtx.serialize();
const rawTx = '0x' + serializedTx.toString('hex');
//finally pass this data parameter to send Transaction
web3.eth.sendRawTransaction(rawTx, someCallbackFunction);
Важно: время майнинга в ropsten будет от 15 до 30 секунд, поэтому, если в someCallbackFunction вы проверите получение транзакции, используя хэш, вы получите результат null, потому что транзакция находится в состоянии ожидания.
4) Для проверки на ropsten используем Infura, поэтому меняем провайдера web3:
import Web3 from 'web3'
import HDWalletProvider from "truffle-hdwallet-provider";
let getWeb3 = new Promise(function(resolve, reject) {
// Wait for loading completion to avoid race conditions with web3 injection timing.
window.addEventListener('load', function() {
var results
var web3 = window.web3
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider.
web3 = new Web3(web3.currentProvider)
results = {
web3: web3
}
console.log('Injected web3 detected.');
return resolve(results)
} else {
// Fallback to localhost if no web3 injection. We've configured this to
// use the development console's port by default.
// var provider = new Web3.providers.HttpProvider("https://ropsten.infura.io/your_infura_api_key")
var mnemonic = "infura mnemonic"
var provider = new HDWalletProvider(mnemonic, "https://ropsten.infura.io/your_infura_api_key")
web3 = new Web3(provider)
results = {
web3: web3
}
console.log('No web3 instance injected, using Local web3.');
return resolve(results)
}
})
})
export default getWeb3
РЕДАКТИРОВАТЬ :
Это также работает на Truffle ! проверьте последние комментарии к этой проблеме https://github.com/trufflesuite/truffle/issues/973
Маркос Мартинес
Маркос Мартинес
Тюдор Константин
Маркос Мартинес
Тюдор Константин
Маркос Мартинес