-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update to ethers v6 #137
Update to ethers v6 #137
Conversation
ab90f87
to
4eae71b
Compare
packages/cli/package.json
Outdated
"@tableland/node-helpers": "^0.0.2", | ||
"@tableland/sdk": "^6.0.0", | ||
"@tableland/node-helpers": "^1.0.0", | ||
"@tableland/sdk": "^7.0.0", | ||
"@tableland/sqlparser": "^1.3.0", | ||
"@tableland/studio-cli": "^0.1.0-pre.3", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: optionally, we need the studio CLI to get bumped before we publish this CLI (will comment on why down below).
@@ -44,8 +44,6 @@ export interface GlobalOptions { | |||
providerUrl: string; | |||
baseUrl: string; | |||
verbose: boolean; | |||
ensProviderUrl?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ENS has been dropped from the CLI
@@ -82,9 +82,10 @@ export const builder: CommandBuilder<Record<string, unknown>, Options> = ( | |||
}); | |||
|
|||
const reg = new Registry({ signer }); | |||
const res = await reg.setController({ tableName: name, controller }); | |||
const tx = await reg.setController({ tableName: name, controller }); | |||
const res = await helpers.getContractReceipt(tx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ethers v6 will return a transaction response with a bigint
type. thus, the JSON.stringify(...)
down below errors out (with do not know how to serialized BigInt
). we can choose to process the bigint -> string or number...but i chose to just use the method we already have (less data & includes the essentials).
? err?.cause?.message | ||
: err.message | ||
); | ||
if (err.message.match(/table name has wrong format/)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed the logic to use the sqlparser and validated table names. to keep the errors consistent, I'm catching and using the same one we defined before.
packages/cli/src/commands/studio.ts
Outdated
@@ -6,6 +6,7 @@ import type { GlobalOptions } from "../cli.js"; | |||
export const command = "studio <sub>"; | |||
export const desc = "Tableland Studio CLI commands"; | |||
|
|||
// @ts-expect-error polygon amoy vs mumbai is `temporarily` causing a type error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here's why the studio CLI should be published before this CLI, and then this CLI needs to update its deps before it gets published.
the studio CLI still has mumbai as a chain, so once it drops mumbai for amoy, then this TS directive is unneded
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes sense.
To be explicit, the plan is:
- publish the sdk, node-helpers, and local
- update sdk dep and publish studio-cli
- update studio-cli dep and publish this cli
Does that sound right? If so, let's publish a pre-
version of the sdk, then open a pr for the studio-cli update, then update this PR with the new studio-cli version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joewagner cool. so do i need to change the SDK version in this PR to ...-pre.0
? or, can we just publish the SDK "offline" as pre.0
and then proceed with what's outlined above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Publishing pre versions "offline" is what I've done in the past.
I wrote a playbook in Notion, but it's basically cd
into the packages/sdk
directory, publish with npm (not lerna) and make sure to include --tag next
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotcha. but, my username doesn't have the ability to publish to npm, tho. maybe you could run that step for me?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, forgot joe's a bit OOO this week. @asutula or @carsonfarmer—if either of you have npm access, could you help publish a pre version of the SDK only? it'd be7.0.0-pre.0
, and the process is described above.
(i need this to get the Studio changes going that use this PR, and i also want to get DIMO the new SDK w/ amoy so they can keep building.)
const wallet = accounts[1]; | ||
const provider = getDefaultProvider(TEST_PROVIDER_URL, { chainId: 31337 }); | ||
const signer = wallet.connect(provider); | ||
const accounts = getAccounts(TEST_PROVIDER_URL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when you review the LT getAccounts
method, you'll see I've changed it to accept either and LT instance, or a URL. this is required because we must use a provider with custom settings—otherwise, it leads to a flakey "nonce to low" issue for all tests.
plus, this makes it a bit easier as compared to the previous logic, which had to be that way because of our custom port setups during tests. now, the custom ports are passed to the method as part of the URL.
@@ -17,7 +17,7 @@ | |||
"ethereum" | |||
], | |||
"engines": { | |||
"node": ">=14.0.0" | |||
"node": ">=18.0.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bumped to node 18 b/c the new hardhat requires LTS / works with >=18
* @returns An instance of a Tableland `Validator` with the correct `baseUrl`. | ||
*/ | ||
export const getAccounts = function (instance?: LocalTableland): Wallet[] { | ||
export const getAccounts = function ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here's the getAccounts
logic change
@@ -10,10 +10,13 @@ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'll update the README separately...i don't think much, if anything, has changed, but I'll take care of that when i update the docs site
@@ -2,7 +2,7 @@ import { type WaitableTransactionReceipt } from "../registry/utils.js"; | |||
import { type FetchConfig } from "../validator/client/index.js"; | |||
import { type PollingController } from "./await.js"; | |||
import { type ChainName, getBaseUrl } from "./chains.js"; | |||
import { type Signer, type ExternalProvider, getSigner } from "./ethers.js"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
much of the following is where the core of the ethers v6 changes start. this old branch has a lot of the changes already mapped out, and i followed the majority of them: https://github.com/tablelandnetwork/js-tableland/tree/joe/bump-ethers
* @param signer A signer instance. | ||
* @returns Current gas fee information for the network. | ||
*/ | ||
export async function getFeeData(signer: Signer): Promise<FeeData> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we had issues with doing anything in @tableland/evm
without fetching live fee data. i've included the logic here to fetch data for amoy, and i figured doing it for polygon mainnet as well wouldn't hurt. if neither of those chains are provided, then it hits the generic ethers getFeeData
method.
@@ -82,28 +139,49 @@ export interface MultiEventTransactionReceipt { | |||
* | |||
*/ | |||
export async function getContractReceipt( | |||
tx: ContractTransaction | |||
tx: ContractTransactionResponse |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is where i had to refactor a bit. a couple of notes:
- event/log parsing is more difficult in v6. perhaps there's an easier way.
- here, i created a new type (will explain below) that builds upon the types in the subscription logic where the index of a
tableId
is mapped to the event type. - in
getContractReceipt
, i use those types when i parse the args to get the index of atableId
- i also added support for setting a controller and transferring a table
// `_detectNetwork` here ensures this doesn't happen, but follow here for | ||
// this issue getting resolved in the future: | ||
// https://github.com/ethers-io/ethers.js/issues/4377#issuecomment-2023855491 | ||
await browserProvider._detectNetwork(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in case it ever comes up—this is a super annoying issue. see the link for details, but TL;DR it logs an error message every second until a connection is made
* @param options Optional settings for the provider. See ethersjs | ||
* {@link JsonRpcApiProviderOptions} for more information. | ||
*/ | ||
export function createSigner({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a new method, not really used anywhere. i think it's useful because pretty much every docs snippet starts with creating a pk signer. this just abstracts it away a little bit.
* Possible events that can be emitted by the Registry as defined in the | ||
* ITablelandTables interface. | ||
*/ | ||
export type ContractEventNames = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here's where new types are defined for the event/log parsing i mentioned above. basically, I'm strongly typing the the registry's event names to silence some TS compiler complaints.
/** | ||
* Mapping of all ITablelandTables contract events and `tableId` event index. | ||
*/ | ||
export const contractEventsTableIdx: ContractEventTableIdMap = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before, there was a single object contractEvents
object (further below) that tracked events to their tableId
and emit
name.
with contractEventsTableIdx
, it basically does the same but:
- includes create table events
- doesn't use the
emit
keyword
i suppose i could maybe combine them. anyways, thecontractEventsTableIdx
is used in thegetContractReceipt
above.
@@ -260,7 +313,8 @@ export class TableEventBus { | |||
}); | |||
}; | |||
|
|||
contract.on(key, listener); | |||
const topicFilter = contract.filters[key as SubscribableEventNames]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some additional logic i had to change for filtering events/logs in ethers v6
@@ -262,7 +262,7 @@ function fetcher<Paths>() { | |||
init: mergeRequestInit(defaultInit, init), | |||
fetch, | |||
}) | |||
)) as CreateFetch<M, Paths[P][M]>, | |||
)) as unknown as CreateFetch<M, Paths[P][M]>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nre TS was complaining if i didnt cast and then recast. i couldn't figure out why. but, if i changed it to CreateFetch
to have a string value for either of the generics, the error went away. not a big deal tho.
const [, wallet] = getAccounts(); | ||
const provider = getDefaultProvider(TEST_PROVIDER_URL); | ||
const signer = wallet.connect(provider); | ||
const [, signer] = getAccounts(TEST_PROVIDER_URL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just another callout to the updated getAccounts
that returns a signer that we need use everywhere, avoiding nonce issues
@@ -74,7 +70,7 @@ describe("registry", function () { | |||
err.message, | |||
// Actual hidden error: | |||
// reverted with custom error 'OwnerQueryForNonexistentToken()' | |||
/cannot estimate gas; transaction may fail or may require manual gas limit.*/ | |||
/execution reverted (unknown custom error)*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: in case it's used elsewhere—the ethers v6 error message for onchain reverts has changed
match(from, /^0x[0-9a-fA-F]+$/); | ||
match(to, /^0x[0-9a-fA-F]+$/); | ||
strictEqual(tableId, eventTableId); | ||
strictEqual(txn.event, "TransferTable"); | ||
match(txn.transactionHash, /^0x[0-9a-fA-F]+$/); | ||
strictEqual(log.fragment.name, "TransferTable"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nre another random ethers v6 change that could be useful to know, if you're ever parsing events
@@ -243,8 +238,16 @@ describe("subscribe", function () { | |||
|
|||
await eventBus.addListener(`${tableName}`); | |||
const listeners = eventBus.listeners[tableIdentifier]; | |||
|
|||
strictEqual(eventBus.contracts["31337"] instanceof Contract, true); | |||
// TODO: understand why checking `instanceof Contract` doesn't work |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: i had this issue in the @tableland/evm
package as well. although a contract is typed as a BaseContract
in typechain, the compiler doesn't recognize it as matching the ethers Contract
interface
@joewagner this is a hefty one! it includes both the ethers v6 and amoy changes. it took forever to figure out that flakey nonce issue, but things finally work. note that we're dropping node 16 support, so that test doesn't matter. I'm in the process of updating the studio with these changes. ideally, we:
|
022cf20
to
e7b4060
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: i updated the docs file locally and then ran the SDK build:api
command off of that, so this should match in the future once the docs site gets updated.
however, if you run the build process, it'll overwrite this file at packages/sdk/src/validator/client/validator.ts
. if that's problematic, i can revert this change or push a quick docs change to main
0958bd7
to
d30895d
Compare
(@joewagner rebased this on the latest changes from main, looks like that fixed the review workflow issue!) |
OP Sepolia, FIL Calibration, and Hardhat all log an RPC error if gas estimation is attempted. Other testnets seems to work just fine.
packages/sdk/src/helpers/ethers.ts
Outdated
const chainIdNumber = typeof chainId === "bigint" ? Number(chainId) : chainId; | ||
// Hardhat, Optimism Sepolia, & Filecoin Calibration will return an RPC error | ||
return !( | ||
chainIdNumber === 31337 || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dtb I'm wondering if we know for sure that chains with id 31337
always don't have these methods?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For background, there was a user in discord using that chainId for a private chain and they were using that chain id. Also there are some other local dev chains that use this id, e.g. https://chainlist.org/chain/31337
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm, yeah that's a good point; we can remove 31337. the main thing this method does is silence an error for The method "eth_maxPriorityFeePerGas" does not exist / is not available.', data: {…}}
, which will get logged in the console of the Studio/web apps on those 3 chains. but, who cares if it gets logged with LT tbh.
some additional context: anvil
and hardhat
don't support the eth_maxPriorityFeePerGas
method but do support eth_gasPrice
. e.g., when you try to call the native ethers getFeeData
on hardhat, the eth_maxPriorityFeePerGas
method logs an error, but the tx still gets executed. aka we don't need to include 31337 because it'll still work fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
edit: it's probs easier to just stick to removing 31337 from the filter and not do what's mentioned below. OP sepolia and FIL calibration networks work fine w/o custom gas prices.
also, we could adjust this method to destructure the gasPrice
too. we just have to handle logic to avoid an error for Cannot send both gasPrice and maxFeePerGas params
. e.g.:
// also get the gas price and conditionally pass it
const { gasPrice, maxFeePerGas, maxPriorityFeePerGas } = await getFeeData(
signer
);
if (maxFeePerGas != null && maxPriorityFeePerGas != null) {
opts.maxFeePerGas = maxFeePerGas;
opts.maxPriorityFeePerGas = maxPriorityFeePerGas;
} else if (gasPrice != null) {
opts.gasPrice = gasPrice;
}
to get the gas price, we'd have to call eth_gasPrice
directly to avoid the logging error from above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sticking to the removal of 31337 sounds good to me. If it's annoying in LT we can stop logging those messages in LT? (If that makes sense, we do it in another PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
works for me!
@dtbuchholz I just pushed a commit with some updated versions of the studio. I think we are gtg for Monday. The only notable thing is that since we have two monorepos with a circular dependency, I had to do something a little hacky to enable us to publish everything simultaneously next week. tl;dr; we can either just leave them incase we do something like this again, or remove them once all the |
gotcha. i suppose if leaving those files doesn't impact anything negatively, then why not do so...just in case we have a major bump in the future, which I'm guessing will happen |
Summary
Closes #77.
Details
getAccounts
method in the LT package for details—this must be used in all tests for ethers v6 to work with harhat when there are many txs in a row.polygon
vs.matic
.Note: node 16 has been dropped in hardhat &
@tableland/evm
, so expect that test to fail.How it was tested
Updated all tests across all clients.
Checklist: