Linking libraries from generated type has inconsistent behavior
See original GitHub issueEthers Beta Version
v6-beta
Describe the Problem
It is not clear when linking libraries (linkLibraryAddresses
) is required or how this functionality works. I have two extremely similar contracts, and the generated types are quite different. One is the normal type that I am used to ContractA
, but the other ContractB
has additional functionality around linking libraries and a required argument in the constructor.
Ideally, I would like ContractB
to not require linking libraries at all. At the very least, though, I would like to know why this is a requirement for one of the contracts but not the other. Some documentation around library linking would also be good, as the only other relevant piece of information after an extensive search was just when this feature got added: https://github.com/ethers-io/ethers.js/issues/195
Code Snippet
Normal contract factory type ContractA__factory.ts
that uses libraries has a familiar constructor that just requires a signer address:
type ContractAConstructorParams =
| [signer?: Signer]
| ConstructorParameters<typeof ContractFactory>;
const isSuperArgs = (
xs: ContractAConstructorParams
): xs is ConstructorParameters<typeof ContractFactory> => xs.length > 1;
export class ContractA__factory extends ContractFactory {
constructor(...args: ContractAConstructorParams) {
if (isSuperArgs(args)) {
super(...args);
} else {
super(_abi, _bytecode, args[0]);
}
this.contractName = "ContractA";
}
Abnormal contract factory type ContractB__factory.ts
that uses libraries has a constructor that requires linkLibraryAddresses
parameter and additional functionality such as linkBytecode()
:
type ContractBConstructorParams =
| [linkLibraryAddresses: ContractBLibraryAddresses, signer?: Signer]
| ConstructorParameters<typeof ContractFactory>;
const isSuperArgs = (
xs: ContractBConstructorParams
): xs is ConstructorParameters<typeof ContractFactory> => {
return (
typeof xs[0] === "string" ||
(Array.isArray as (arg: any) => arg is readonly any[])(xs[0]) ||
"_isInterface" in xs[0]
);
};
export class ContractB__factory extends ContractFactory {
constructor(...args: ContractBConstructorParams) {
if (isSuperArgs(args)) {
super(...args);
} else {
const [linkLibraryAddresses, signer] = args;
super(
_abi,
ContractB__factory.linkBytecode(linkLibraryAddresses),
signer
);
}
this.contractName = "ContractB";
}
static linkBytecode(
linkLibraryAddresses: ContractBLibraryAddresses
): string {
let linkedBytecode = _bytecode;
linkedBytecode = linkedBytecode.replace(
new RegExp("__\\$0d23297b21f644609e8328ff630c929483\\$__", "g"),
linkLibraryAddresses[
"contracts/libraries/MyLib.sol:MyLib"
]
.replace(/^0x/, "")
.toLowerCase()
);
return linkedBytecode;
}
export interface ContractBLibraryAddresses {
["contracts/libraries/MyLib.sol:MyLib"]: string;
}
Errors
If I try to manually turn ContractB
’s type into a type resembling ContractA
(removing the link library requirement), I get errors about bytecode mismatch. Likely not important for this issue but can post if useful.
Environment
node.js, Hardhat
Environment (Other)
typechain
Issue Analytics
- State:
- Created a year ago
- Comments:5 (1 by maintainers)
Top GitHub Comments
If the compiled contract’s artifact mentions that it needs a library to be linked, then typechain would generate ethers types that require you to supply the library address. You can checkout solidity docs here.
TLDR; If your contract imports any library and uses any of it’s
external
methods in it’s code, then that library is needed to deployed separately and it’s address to be included in the bytecode.Thank you so much, turns out I didn’t have an
external
function in the library, but I did have apublic
!SOLUTION: Move all external/public functions to internal/private.