This is my approach. I'm open to others if there is a way to simplify it.
from pathlib import Path
from solcx import compile_files, link_code
from brownie import web3, accounts, Contract
library_addresses = {"Lib1": <Lib1 address>, "Lib2", <Lib2 address>}
remappings = [
'@openzeppelin=Openzeppelin/[email protected]',
'libraries=contracts/libraries'
]
contract_file = Path("contracts/MyContract.sol")
contract_compiled_sol = compile_files(
[contract_file],
import_remappings=remappings,
output_values=["abi", "bin"],
solc_version=<solc version (e.g. "0.8.14")>,
optimize=True,
)
abi = contract_compiled_sol["contracts/MyContract.sol:MyContract"]["abi"]
bin = contract_compiled_sol["contracts/MyContract.sol:MyContract"]["bin"]
nonce = web3.eth.getTransactionCount(accounts[0].address)
# Here is the MAGIC!!!
bin = link_code(
bin,
{
f"contracts/libraries/{lib}.sol:{lib}": library_addresses[lib]
for lib in library_addresses.keys()
},
)
MyContract = web3.eth.contract(abi=abi, bytecode=bin)
tx_data = {
"from": accounts[0].address,
"nonce": nonce,
"gasPrice": int(web3.eth.gas_price * 1.2), # or whatever you want...
"gas": 3000000, # or whatever makes sense for your contract...
}
tx = MyContract.constructor(
arg1,
arg2,
...
).buildTransaction(tx_data)
print(web3.eth.estimateGas(tx)) # Can rebuild transaction with gas estimate
signed_tx = web3.eth.account.signTransaction(
tx,
private_key=accounts[0].private_key,
)
tx_hash = web3.eth.sendRawTransaction(signed_tx.rawTransaction)
receipt = web3.eth.waitForTransactionReceipt(tx_hash, timeout=360)
print(f"\tTransaction: {tx_hash.hex()}")
print(
f"\tMyContract successfully deployed to {receipt.contractAddress}"
)
# Now you can interact with your new contract :)
myContract = Contract.from_abi(
"MyContract",
receipt.contractAddress,
abi,
)
Please let me know if there was anything I missed. Also please note, I added in the Openzeppelin remapping because it's a common library remapping added to configurations. So more of an additional help for anyone.