Skip to content

Commit c9f3c19

Browse files
committed
Back to basics, based on frontend requirements
1 parent b1b244a commit c9f3c19

File tree

4 files changed

+85
-140
lines changed

4 files changed

+85
-140
lines changed

hardhat.config.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,8 @@ task("test", "Run tests").setAction(async () => {
5353
try {
5454
const abiCoder = new ethers.utils.AbiCoder();
5555
const decoded = abiCoder.decode(["string"], `0x${error.data.slice(10)}`);
56-
res.send({
57-
jsonrpc: "2.0",
58-
error: { message: `Error: VM Exception while processing transaction: reverted with reason string '${decoded[0]}'` },
59-
id: req.body.id,
60-
});
56+
const message = `Error: VM Exception while processing transaction: reverted with reason string '${decoded[0]}'`;
57+
res.send({ jsonrpc: "2.0", error: { message }, id: req.body.id });
6158
} catch (e) {
6259
res.send({ jsonrpc: "2.0", error: { message: error.message }, id: req.body.id });
6360
}

packages/package-utils/ExtendedNonceManager.js

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,19 @@
11
const { NonceManager } = require("@ethersproject/experimental");
2-
const { parseTransaction } = require("ethers/lib/utils");
32

43
class ExtendedNonceManager extends NonceManager {
5-
constructor(signer) {
6-
super(signer);
7-
this.signedTransactions = {};
8-
this.nonce = 0;
9-
this.signer.provider.on("block", async () => {
10-
Object.keys(this.signedTransactions).map(async (txHash) => {
11-
const nodeTx = await this.signer.provider.getTransaction(txHash);
12-
if (!nodeTx) {
13-
const txCount = await this.signer.getTransactionCount("pending");
14-
const parsedTransaction = parseTransaction(this.signedTransactions[txHash]);
15-
if (parsedTransaction.nonce < txCount) {
16-
// It's not been mined, but it's been replaced by another tx.
17-
// Resend, with a new nonce
18-
delete this.signedTransactions[txHash];
19-
this.sendTransaction({
20-
from: parsedTransaction.from,
21-
to: parsedTransaction.to,
22-
value: parsedTransaction.value,
23-
data: parsedTransaction.data,
24-
});
25-
} else {
26-
// No reason to think it's been replaced, so rebroadcast.
27-
this.signer.provider.sendTransaction(this.signedTransactions[txHash]);
28-
return;
29-
}
30-
}
31-
if (nodeTx.blockNumber) {
32-
// It's been mined, so forget it.
33-
delete this.signedTransactions[txHash];
34-
}
35-
// Otherwise it's known, but not mined yet. No action required.
36-
});
37-
});
38-
}
39-
404
async sendTransaction(transactionRequest) {
415
try {
42-
const populatedTransaction = await this.populateTransaction(transactionRequest);
43-
const signedTransaction = await this.signTransaction(populatedTransaction);
6+
const txCount = await this.signer.getTransactionCount("latest");
7+
this.setTransactionCount(txCount);
448
const response = super.sendTransaction(transactionRequest);
459
const tx = await response;
46-
this.signedTransactions[tx.hash] = signedTransaction;
10+
await tx.wait();
4711
return response;
4812
} catch (e) {
49-
if (e.code === "NONCE_EXPIRED") {
50-
// The nonce has expired, so we need to update it.
51-
const txCount = await this.signer.getTransactionCount("pending");
52-
this.setTransactionCount(txCount);
13+
if (e.code === "NONCE_EXPIRED" || e.code === "TRANSACTION_REPLACED") {
14+
// The nonce has expired, so try again
5315
return this.sendTransaction(transactionRequest);
5416
}
55-
console.log(e);
5617
throw e;
5718
}
5819
}

test/packages/extendedNonceManager.js

Lines changed: 0 additions & 38 deletions
This file was deleted.

test/packages/metaTransactionBroadcaster.js

Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ const axios = require("axios");
99
const { TruffleLoader, RetryProvider } = require("../../packages/package-utils");
1010
const { setupEtherRouter } = require("../../helpers/upgradable-contracts");
1111
const { UINT256_MAX, CURR_VERSION } = require("../../helpers/constants");
12-
const { web3GetTransaction, currentBlockTime, stopMining, startMining, sleep, hardhatDropTransaction } = require("../../helpers/test-helper");
12+
const {
13+
web3GetTransaction,
14+
currentBlockTime,
15+
stopMining,
16+
startMining,
17+
sleep,
18+
hardhatDropTransaction,
19+
mineBlock,
20+
} = require("../../helpers/test-helper");
1321

1422
const MetatransactionBroadcaster = require("../../packages/metatransaction-broadcaster/MetatransactionBroadcaster");
1523
const { getMetaTransactionParameters, getPermitParameters, setupColony } = require("../../helpers/test-data-generator");
@@ -379,6 +387,12 @@ contract("Metatransaction broadcaster", (accounts) => {
379387
expect(balanceAccount2).to.eq.BN(1200000);
380388
const balanceColony = await metaTxToken.balanceOf(colony.address);
381389
expect(balanceColony).to.eq.BN(600000);
390+
391+
// Check the transactions happened with the hashes returned
392+
const tx1 = await web3GetTransaction(txHash);
393+
expect(tx1.gas).to.be.lt.BN(500000);
394+
const tx2 = await web3GetTransaction(txHash2);
395+
expect(tx2.gas).to.be.lt.BN(500000);
382396
});
383397

384398
it("a nonce collision is resolved correctly", async function () {
@@ -403,18 +417,28 @@ contract("Metatransaction broadcaster", (accounts) => {
403417
await stopMining();
404418
const p = new web3.eth.providers.HttpProvider("http://localhost:8545");
405419

406-
const res = await axios.post("http://127.0.0.1:3000/broadcast", jsonData, {
420+
const postRequest = axios.post("http://127.0.0.1:3000/broadcast", jsonData, {
407421
headers: {
408422
"Content-Type": "application/json",
409423
},
410424
});
411425

412-
await hardhatDropTransaction(p, res.data.data.txHash);
426+
let firstTxHash = null;
427+
while (!firstTxHash) {
428+
const block = await provider.send("eth_getBlockByNumber", ["pending", true]);
429+
if (block.transactions.length > 0) {
430+
const [firstTx] = block.transactions;
431+
firstTxHash = firstTx.hash;
432+
}
433+
await sleep(1000);
434+
}
413435

414-
await startMining();
436+
await hardhatDropTransaction(p, firstTxHash);
415437

416438
const currentNonce = await provider.getTransactionCount(USER0);
417439

440+
await startMining();
441+
418442
// This will have the nonce of the dropped transaction, which the broadcaster will try to
419443
// rebroadcast when it sees a block, but it will fail because the nonce is too low
420444
// it should then rebroadcast with the correct nonce
@@ -423,11 +447,12 @@ contract("Metatransaction broadcaster", (accounts) => {
423447
let newNonce = 0;
424448
while (newNonce < currentNonce + 2) {
425449
newNonce = await provider.getTransactionCount(USER0);
450+
await mineBlock();
426451
await sleep(1000);
427452
}
453+
const res = await postRequest;
428454

429455
const { txHash } = res.data.data;
430-
431456
expect(txHash.length).to.be.equal(66);
432457

433458
expect(res.data).to.be.deep.equal({
@@ -442,6 +467,16 @@ contract("Metatransaction broadcaster", (accounts) => {
442467
expect(balanceAccount1).to.eq.BN(1200000);
443468
const balanceColony = await metaTxToken.balanceOf(colony.address);
444469
expect(balanceColony).to.eq.BN(300000);
470+
471+
// Check the transactions happened with the hashes returned
472+
const tx1 = await web3GetTransaction(txHash);
473+
if (!tx1) {
474+
throw new Error("Transaction not found");
475+
}
476+
expect(tx1.gas).to.be.lt.BN(500000);
477+
478+
const originalTx = await web3GetTransaction(firstTxHash);
479+
expect(originalTx).to.be.null;
445480
});
446481

447482
it("a valid transaction is broadcast and mined, even if the broadcaster's nonce manager fell behind", async function () {
@@ -452,7 +487,12 @@ contract("Metatransaction broadcaster", (accounts) => {
452487
const txData = await metaTxToken.contract.methods.transfer(colony.address, 300000).encodeABI();
453488

454489
const { r, s, v } = await getMetaTransactionParameters(txData, USER0, metaTxToken.address);
455-
const { r: r2, s: s2, v: v2 } = await getMetaTransactionParameters(txData, USER1, metaTxToken.address);
490+
491+
const txCount = await provider.getTransactionCount(accounts[0]);
492+
await broadcaster.nonceManager.setTransactionCount(txCount);
493+
494+
// Make an unexpected transaction
495+
await metaTxToken.mint(USER2, 1500000, { from: USER0 });
456496

457497
// Send to endpoint
458498

@@ -471,21 +511,6 @@ contract("Metatransaction broadcaster", (accounts) => {
471511
},
472512
});
473513

474-
// Make an unexpected transaction
475-
await metaTxToken.mint(USER2, 1500000, { from: USER0 });
476-
477-
jsonData.r = r2;
478-
jsonData.s = s2;
479-
jsonData.v = v2;
480-
jsonData.userAddress = USER1;
481-
console.log("before");
482-
await axios.post("http://127.0.0.1:3000/broadcast", jsonData, {
483-
headers: {
484-
"Content-Type": "application/json",
485-
},
486-
});
487-
console.log("after");
488-
489514
const { txHash } = res.data.data;
490515

491516
expect(txHash.length).to.be.equal(66);
@@ -501,7 +526,11 @@ contract("Metatransaction broadcaster", (accounts) => {
501526
const balanceAccount1 = await metaTxToken.balanceOf(USER0);
502527
expect(balanceAccount1).to.eq.BN(1200000);
503528
const balanceAccount2 = await metaTxToken.balanceOf(colony.address);
504-
expect(balanceAccount2).to.eq.BN(600000);
529+
expect(balanceAccount2).to.eq.BN(300000);
530+
531+
// Check the transactions happened with the hashes returned
532+
const tx1 = await web3GetTransaction(txHash);
533+
expect(tx1.gas).to.be.lt.BN(500000);
505534
});
506535

507536
it("a valid transaction is broadcast and mined, even if the broadcaster's nonce manager got ahead", async function () {
@@ -512,7 +541,6 @@ contract("Metatransaction broadcaster", (accounts) => {
512541
const txData = await metaTxToken.contract.methods.transfer(colony.address, 300000).encodeABI();
513542

514543
const { r, s, v } = await getMetaTransactionParameters(txData, USER0, metaTxToken.address);
515-
const { r: r2, s: s2, v: v2 } = await getMetaTransactionParameters(txData, USER1, metaTxToken.address);
516544

517545
// Send to endpoint
518546

@@ -525,22 +553,11 @@ contract("Metatransaction broadcaster", (accounts) => {
525553
v,
526554
};
527555

528-
let res = await axios.post("http://127.0.0.1:3000/broadcast", jsonData, {
529-
headers: {
530-
"Content-Type": "application/json",
531-
},
532-
});
533-
534556
// Set the nonce
535557
const txCount = await provider.getTransactionCount(accounts[0]);
536558
await broadcaster.nonceManager.setTransactionCount(txCount + 1);
537559

538-
jsonData.r = r2;
539-
jsonData.s = s2;
540-
jsonData.v = v2;
541-
jsonData.userAddress = USER1;
542-
543-
res = await axios.post("http://127.0.0.1:3000/broadcast", jsonData, {
560+
const res = await axios.post("http://127.0.0.1:3000/broadcast", jsonData, {
544561
headers: {
545562
"Content-Type": "application/json",
546563
},
@@ -561,7 +578,11 @@ contract("Metatransaction broadcaster", (accounts) => {
561578
const balanceAccount1 = await metaTxToken.balanceOf(USER0);
562579
expect(balanceAccount1).to.eq.BN(1200000);
563580
const balanceAccount2 = await metaTxToken.balanceOf(colony.address);
564-
expect(balanceAccount2).to.eq.BN(600000);
581+
expect(balanceAccount2).to.eq.BN(300000);
582+
583+
// Check the transaction happened with the hash returned
584+
const tx = await web3GetTransaction(txHash);
585+
expect(tx.gas).to.be.lt.BN(500000);
565586
});
566587

567588
it("a valid EIP712 transaction is broadcast and mined", async function () {
@@ -603,6 +624,10 @@ contract("Metatransaction broadcaster", (accounts) => {
603624
// Check the transaction happened
604625
const allowed = await metaTxToken.allowance(USER0, colony.address);
605626
expect(allowed).to.eq.BN(1);
627+
628+
// With the expected hash
629+
const tx = await web3GetTransaction(txHash);
630+
expect(tx.gas).to.be.lt.BN(500000);
606631
});
607632

608633
it("an EIP712 transaction with an invalid spender is not broadcast and mined", async function () {
@@ -664,26 +689,22 @@ contract("Metatransaction broadcaster", (accounts) => {
664689
s,
665690
v,
666691
};
667-
try {
668-
const res = await axios.post("http://127.0.0.1:3000/broadcast", jsonData, {
669-
headers: {
670-
"Content-Type": "application/json",
671-
},
672-
});
692+
const res = await axios.post("http://127.0.0.1:3000/broadcast", jsonData, {
693+
headers: {
694+
"Content-Type": "application/json",
695+
},
696+
});
673697

674-
const { txHash } = res.data.data;
698+
const { txHash } = res.data.data;
675699

676-
expect(txHash.length).to.be.equal(66);
700+
expect(txHash.length).to.be.equal(66);
677701

678-
expect(res.data).to.be.deep.equal({
679-
status: "success",
680-
data: {
681-
txHash,
682-
},
683-
});
684-
} catch (err) {
685-
console.log(err.response.data);
686-
}
702+
expect(res.data).to.be.deep.equal({
703+
status: "success",
704+
data: {
705+
txHash,
706+
},
707+
});
687708

688709
// Check the transaction happened
689710
const roles = await colony.getUserRoles(USER1, 1);
@@ -692,6 +713,10 @@ contract("Metatransaction broadcaster", (accounts) => {
692713

693714
const expectedRoles = roleArchitecture | roleFunding; // eslint-disable-line no-bitwise
694715
expect(roles).to.equal(ethers.utils.hexZeroPad(ethers.BigNumber.from(expectedRoles).toHexString(), 32));
716+
717+
// With the expected hash
718+
const tx = await web3GetTransaction(txHash);
719+
expect(tx.gas).to.be.lt.BN(500000);
695720
});
696721

697722
it("a multicall transaction that calls something invalid is rejected", async function () {

0 commit comments

Comments
 (0)