-
Notifications
You must be signed in to change notification settings - Fork 465
Reorganize and modularize generated contract wrappers and artifacts #1105
Conversation
What is the generated artifacts folder? Why can't we just use the artifacts from migrations? |
packages/contracts/package.json
Outdated
"name": "contracts", | ||
"version": "2.1.48", | ||
"name": "@0xproject/contracts", | ||
"version": "2.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.
Hm, what's the rationale here? Lerna is going to publish patch
updates of this package whenever it's deps update. Don't think it's possible to have this version track the "contract" version. Rather we should maybe add a second key called "0xVersion: "2.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.
I was thinking it would be cleaner to start at 2.0.0
and it shouldn't matter since this package has never been published before.
That said, you're right that in the long-term this version will probably not track the "contract" version exactly. I changed my mind and I think it's probably better to keep the same version number we had before. I'll change it back to 2.1.48
.
packages/contracts/src/index.ts
Outdated
@@ -0,0 +1,3 @@ | |||
export * from './artifacts'; | |||
import * as wrappers from './wrappers'; | |||
export { wrappers }; |
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.
Also, in contracts
let's add a .npmignore
file and make sure the bundle published to npmjs.com
repository only includes these files and not the tests
and Solidity
source dirs.
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 was planning to sort what to include in the published package out at the end. Want to make sure everything works inside the monorepo first.
@LogvinovLeon the goal of this PR is to have all artifacts and generated contract wrappers in a single canonical location. Other packages that need those things will import the canonical package instead of copying files or calling I'm planning to have the |
Reminder: make sure to add |
I added a summary of major changes to this PR description. In terms of progress:
The biggest things I still need to do are:
|
4df5bb4
to
1c09da9
Compare
2f66ae8
to
66f407f
Compare
Updated high-level summary of changes:
|
Okay I think I have an approach that works now. Most of what's left to do is bookkeeping:
|
608418f
to
4326757
Compare
import * as ZRXToken from '../artifacts/ZRXToken.json'; | ||
|
||
export { | ||
AssetProxyOwner, |
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 that we don't have to do the (as any) as ContractArtifact
hack anymore! With "resolveJsonModule": true
in tsconfig.json TypeScript supports typed JSON imports. It also works with Project References, so when you watch, it will recompile when the JSON file(s) change.
artifacts.ERC20Token, | ||
normalizedTokenAddress, | ||
); | ||
// TODO(albrow): Do we really still need this check? The default 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.
@LogvinovLeon or @fabioberger thoughts on this TODO? (Also appears a few other places).
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.
What is the default error? doesContractExistAtAddressAsync
returns a boolean.
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.
What I'm asking is whether we really need the ContractWrappersError.ERC20TokenContractDoesNotExist
error. If we remove this check, Web3Wrapper
throws an error along the lines of "Tried to send a transaction to 0xabcde... but it is not a contract address."
. Together with a stack trace, I think this makes it pretty clear what went wrong. What do you think?
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.
Yep, let's get rid of the mapping.
import { provider, txDefaults, web3Wrapper } from './web3_wrapper'; | ||
|
||
// Those addresses come from migrations. They're deterministic so it's relatively safe to hard-code them here. | ||
// Before we were fetching them from the TokenRegistry but now we can't as it's deprecated and removed. | ||
// TODO(albrow): Import these from the migrations package instead of hard-coding them. |
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'm going to leave this for now. We're no worse off than we were before this PR and can always fix it later. This PR is already arguably too big as it is.
@@ -112,7 +107,7 @@ describe('Revert Validation ExchangeWrapper', () => { | |||
makerTokenBalance, | |||
); | |||
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); | |||
expect( | |||
return expect( |
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 subtle, but there was a race condition here before adding the return
.
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.
Can we add a TSLint rule for this? If not now, let's add a task to the backlog.
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.
➕ Good idea. I believe we already have a check for unhandled promise rejections. The problem is that expect().to.be.rejectedWith
doesn't return a real Promise
. Instead it returns a Chai.PromisedAssertion
. There must be some way to check for it.
@@ -454,13 +458,6 @@ describe('ExchangeWrapper', () => { | |||
})().catch(done); | |||
}); | |||
}); | |||
describe('#getZRXTokenAddressAsync', () => { |
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.
Since we removed the token registry, this test isn't really doing anything.
@@ -124,39 +112,8 @@ export abstract class ContractWrapper { | |||
const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); | |||
return logWithDecodedArgs; | |||
} | |||
protected async _getContractAbiAndAddressFromArtifactsAsync( |
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.
Since we aren't reading addresses from a file anymore, I was able to remove this function and others like it. For ContractWrappers
addresses are now known at instantiation time and are synchronous, which simplifies a lot of our code.
.gitignore
Outdated
packages/0x.js/src/generated_contract_wrappers/ | ||
packages/contracts/generated_contract_wrappers/ | ||
packages/contract-wrappers/src/contract_wrappers/generated/ | ||
packages/contracts/generated-wrappers/ | ||
packages/metacoin/src/contract_wrappers | ||
packages/fill-scenarios/src/generated_contract_wrappers/ | ||
packages/order-watcher/src/generated_contract_wrappers/ |
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.
Do these still exist? This is where the abi-gen-wrappers
used to get copied to.
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 have a TODO item on this PR to fix .gitignore
"engines": { | ||
"node": ">=6.12" | ||
}, | ||
"description": "Smart contract components of 0x protocol", |
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.
"0x smart contract wrappers generated using @0xproject/abi-gen"
@@ -0,0 +1,14 @@ | |||
export * from '../wrappers/asset_proxy_owner'; |
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.
Why are the wrappers not under the src
dir?
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.
IMO src
should be for user-editable source code. These wrappers are generated code. Does that sound right to you?
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... I think of src
as where all the TS code lived that needs to be compiled into lib
. Those generated contracts have disclaimers in their headers, and we could also name the directory to generated_wrappers
.
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.
👍 Okay I changed it.
@@ -0,0 +1,64 @@ | |||
import * as _ from 'lodash'; | |||
|
|||
export interface ContractAddresses { |
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.
Can we rename this to contractToAddress
? We are trying to turn this mapped naming which mimicks the left (key) and right (value) of the interface into a convention.
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.
Hmm.. I'm not sure I agree with renaming it. In this case, I think the kind of information we are passing around (the addresses of deployed contracts) is more important to call out than the underlying data structure (a mapping of contract name to contract address). For similar reasons, it seems awkward to rename getContractAddressesForNetwork
to getContractToAddressForNetwork
or rename the contractAddresses
parameter in the constructor for ContractWrappers
to contractToAddress
.
To look at it from another perspective, here are a few examples of how we use the XToY
naming convention in @0xproject/types
:
export interface ExportNameToTypedocNames {
[exportName: string]: string[];
}
export interface ExternalTypeToLink {
[externalTypeName: string]: string;
}
export interface ExternalExportToLink {
[externalExport: string]: string;
}
All of these cases represent a mapping of arbitrary X to Y, and that's where the naming convention makes the most sense to me. Since we don't know ahead of time exactly what keys the type will hold (and whether other uses of the type will have the same keys), the underlying data structure is more important to call out. In other words, the data structure itself is what defines the type, not the data it holds.
All that said, if you feel strongly about it, I can be convinced.
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.
Yeah, I see your point in this case. Thanks for the thoughtful explanation, let's leave it the way it is.
@@ -109,6 +110,7 @@ export class AssetBuyer { | |||
this.expiryBufferSeconds = expiryBufferSeconds; | |||
this._contractWrappers = new ContractWrappers(this.provider, { | |||
networkId, | |||
contractAddresses: getContractAddressesForNetwork(networkId), |
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.
Since this is optional, and the fallback is to call this same method under-the-hood, we can omit this line and remove the contract-addresses
dep from this package.
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.
@fragosti @BMillman19 do we want people to be able to use AssetBuyer in their own private testnets? If so, we might want to expose this config in the contructor.
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.
Omitted for now. We can add it back in if we think users want to configure it.
public getZRXTokenAddress(): string { | ||
const contractAddress = this._getContractAddress(artifacts.ZRXToken, this._zrxContractAddressIfExists); | ||
return contractAddress; | ||
} |
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... any reason to remove these? We could just return it no? It's a big breaking change to remove these... I know relayers rely on that. Do we want to force them to move to using contract-addresses
package?
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.
With the changes in this PR, zrxTokenAddress
is simply a property of ExchangeWrapper
and it will always be set after instantiation. You don't have to use the contract-addresses
package if you don't want to. Removing this method simplifies our API surface and will make contract-wrappers
easier to use/understand. Leaving it in would mean that there are now two ways to get the address and just creates a little bit of clutter.
Given that AFAIK we don't have any mechanism for marking functions/methods as deprecated, I think we should just remove outdated code like this.
If this was the only breaking change, I would leave it in. However, we already have some breaking changes to the contract-wrappers
package in this PR and this one is definitely on the easy side in terms of updating.
* @param networkId Desired networkId | ||
* @param web3Wrapper Web3Wrapper instance to use. | ||
* @param networkId Desired networkId. | ||
* @param address (Optional) The address of the OrderValidator contract. If |
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.
remove (optional)
@@ -112,7 +107,7 @@ describe('Revert Validation ExchangeWrapper', () => { | |||
makerTokenBalance, | |||
); | |||
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); | |||
expect( | |||
return expect( |
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.
Can we add a TSLint rule for this? If not now, let's add a task to the backlog.
packages/website/ts/blockchain.ts
Outdated
this._contractWrappers = new ContractWrappers(provider, { networkId }); | ||
const contractWrappersConfig = { | ||
networkId, | ||
contractAddresses: getContractAddressesForNetwork(networkId), |
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.
Omit this. It defaults to doing this internally.
{ "path": "./packages/assert" }, | ||
{ "path": "./packages/asset-buyer" }, | ||
{ "path": "./packages/base-contract" }, | ||
{ "path": "./packages/connect" }, | ||
{ "path": "./packages/contract-artifacts" }, |
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.
Do we need an entry here for contract-addressses
?
"compilerOptions": { | ||
"outDir": "lib", | ||
"rootDir": ".", | ||
"resolveJsonModule": true |
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 I tried to do that I've hit a problem that TSC auto-generates types for JSON files from their content and we had some partial artifacts in some modules, but I guess now that you standardized it - it's not a problem any more. Happy about that.
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.
Yes, I still had to include some type assertions in the contracts
package for the reasons you mentioned. I think it only works for contract-artifacts
right now because we are using the trimmed down contract artifacts there.
653ef71
to
462bf02
Compare
@@ -103,6 +103,9 @@ export class DocGenerateAndUploadUtils { | |||
switch (node.kind) { | |||
case ts.SyntaxKind.ExportDeclaration: { | |||
const exportClause = (node as any).exportClause; | |||
if (_.isUndefined(exportClause)) { |
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.
@fabioberger commenting here to make sure you get a chance to review the changes I made to this file. It's easy to miss in a PR of this size.
5472bf2
to
f0e4837
Compare
ca26a2b
to
e093864
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.
Approve for website and asset-buyer
7e0e126
to
5bdfad9
Compare
Removed WIP label. Ready for final review. |
…directory OUTSIDE of the monorepo root, so that the installed packages can't reference the hoisted node_modules folder
…ailable to users of the library
…s available to users of the library
…s available to users of the library
… available to users of the library
…it's available to users of the library
|
||
You may also be interested in the | ||
[@0xproject/contract-wrappers](../contract-wrappers/README.md) package which | ||
includes some higher-level features. |
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.
❤️
Description
This PR is a huge reorganization of how we generate, store, and copy generated files related to contracts (specifically artifacts and contract wrappers generated by abi-gen).
Here's a summary of the biggest changes (some of which are not fully complete yet):
1. The contracts package is now a public package that will be published with the name@0xproject/contracts
2. All artifacts have been moved out of
@0xproject/migrations
and into@0xproject/contracts
. Artifacts are encapsulated in an npm package and have a version number indicated by a package.json file. You can import artifacts withimport { artifacts } from '@0xproject/contracts'
.3. All contract wrappers have been moved into
@0xproject/contracts
. Like artifacts, they are encapsulated in an npm package. You can import them withimport { wrappers } from '@0xproject/contracts'
.4. Addresses of deployed contracts are no longer stored in artifact files. For tests, you can get the addresses of contracts that were deployed during migrations with the new
getContractAddresses
function in@0xproject/migrations
. (I'm still not sure if I'm happy with this exact interface, but in any case there will be some mechanism to get the contract addresses after you runrunMigrationsAsync
). For contracts that have been deployed to the mainnet or various testnets, the addresses will be exposed as an object in one of our public packages (exact details TBD). This approach leads to a better separation between things that are temporary (addresses of contracts that were deployed for testing purposes) and permanent (addresses, abis, and source code of contracts that have already been deployed to mainnet or a testnet).(A lot has changed. See below for an updated high-level description).
Testing instructions
Types of changes
Checklist:
[WIP]
if necessary.[sol-cov] Fixed bug
.