Skip to content

Commit

Permalink
feat(common): remove need for tx queue in createContract (#1271)
Browse files Browse the repository at this point in the history
  • Loading branch information
holic committed Aug 9, 2023
1 parent 9a7c900 commit 35c9f33
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 66 deletions.
5 changes: 5 additions & 0 deletions .changeset/fluffy-moles-march.md
@@ -0,0 +1,5 @@
---
"@latticexyz/common": patch
---

- Remove need for tx queue in `createContract`
1 change: 0 additions & 1 deletion packages/common/package.json
Expand Up @@ -62,7 +62,6 @@
"chalk": "^5.2.0",
"debug": "^4.3.4",
"execa": "^7.0.0",
"p-queue": "^7.3.4",
"p-retry": "^5.1.2",
"prettier": "^2.8.4",
"prettier-plugin-solidity": "^1.1.2",
Expand Down
96 changes: 47 additions & 49 deletions packages/common/src/createContract.ts
Expand Up @@ -5,15 +5,13 @@ import {
Chain,
GetContractParameters,
GetContractReturnType,
Hex,
PublicClient,
SimulateContractParameters,
Transport,
WalletClient,
WriteContractParameters,
getContract,
} from "viem";
import pQueue from "p-queue";
import pRetry from "p-retry";
import { createNonceManager } from "./createNonceManager";
import { debug as parentDebug } from "./debug";
Expand Down Expand Up @@ -62,11 +60,6 @@ export function createContract<
address: walletClient.account.address,
});

// Concurrency of one means transactions will be queued and inserted into the mem pool synchronously and in order.
// Although increasing this will allow for more parallel requests/transactions and nonce errors will get automatically retried,
// we can't guarantee local nonce accurancy due to needing async operations (simulate) before incrementing the nonce.
const queue = new pQueue({ concurrency: 1 });

// Replace write calls with our own proxy. Implemented ~the same as viem, but adds better handling of nonces (via queue + retries).
contract.write = new Proxy(
{},
Expand All @@ -80,34 +73,31 @@ export function createContract<
}
>getFunctionParameters(parameters as any);

async function write(): Promise<Hex> {
if (!nonceManager.hasNonce()) {
await nonceManager.resetNonce();
}

// Temporarily override base fee for our default anvil config
// TODO: remove once https://github.com/wagmi-dev/viem/pull/963 is fixed
// TODO: more specific mud foundry check? or can we safely assume anvil+mud will be block fee zero for now?
if (
walletClient.chain.id === 31337 &&
options.maxFeePerGas == null &&
options.maxPriorityFeePerGas == null
) {
options.maxFeePerGas = 0n;
options.maxPriorityFeePerGas = 0n;
}
// Temporarily override base fee for our default anvil config
// TODO: replace with https://github.com/wagmi-dev/viem/pull/1006 once merged
// TODO: more specific mud foundry check? or can we safely assume anvil+mud will be block fee zero for now?
if (
walletClient.chain.id === 31337 &&
options.maxFeePerGas == null &&
options.maxPriorityFeePerGas == null
) {
debug("assuming zero base fee for anvil chain");
options.maxFeePerGas = 0n;
options.maxPriorityFeePerGas = 0n;
}

async function prepareWrite(): Promise<
WriteContractParameters<TAbi, typeof functionName, TChain, TAccount>
> {
if (options.gas) {
const nonce = nonceManager.nextNonce();
debug("gas provided, skipping simulate and calling write function with nonce", nonce, options);
return await walletClient.writeContract({
debug("gas provided, skipping simulate", functionName, args, options);
return {
address,
abi,
functionName,
args,
nonce,
...options,
} as unknown as WriteContractParameters<TAbi, typeof functionName, TChain, TAccount>);
} as unknown as WriteContractParameters<TAbi, typeof functionName, TChain, TAccount>;
}

debug("simulating write", functionName, args, options);
Expand All @@ -120,29 +110,37 @@ export function createContract<
account: options.account ?? walletClient.account,
} as unknown as SimulateContractParameters<TAbi, typeof functionName, TChain>);

const nonce = nonceManager.nextNonce();
debug("calling write function with nonce", nonce, request);
return await walletClient.writeContract({
nonce,
...request,
} as unknown as WriteContractParameters<TAbi, typeof functionName, TChain, TAccount>);
return request as unknown as WriteContractParameters<TAbi, typeof functionName, TChain, TAccount>;
}

return await queue.add(
() =>
pRetry(write, {
retries: 3,
onFailedAttempt: async (error) => {
// On nonce errors, reset the nonce and retry
if (nonceManager.shouldResetNonce(error)) {
debug("got nonce error, retrying", error);
await nonceManager.resetNonce();
return;
}
throw error;
},
}),
{ throwOnTimeout: true }
const preparedWrite = await prepareWrite();

return await pRetry(
async () => {
if (!nonceManager.hasNonce()) {
await nonceManager.resetNonce();
}

const nonce = nonceManager.nextNonce();
debug("calling write function with nonce", nonce, preparedWrite);
return await walletClient.writeContract({
nonce,
...preparedWrite,
});
},
{
retries: 3,
onFailedAttempt: async (error) => {
// On nonce errors, reset the nonce and retry
if (nonceManager.shouldResetNonce(error)) {
debug("got nonce error, retrying", error);
await nonceManager.resetNonce();
return;
}
// TODO: prepareWrite again if there are gas errors?
throw error;
},
}
);
};
},
Expand Down
16 changes: 0 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 35c9f33

Please sign in to comment.