-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 5069 testnet onboarding lh agents works (#5074)
* feat: hh task for claiming linea message * feat: gas cost for linea propagate * test: LH tests for linea
- Loading branch information
1 parent
92dff94
commit c00fc1e
Showing
8 changed files
with
767 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 35 additions & 5 deletions
40
packages/agents/lighthouse/src/tasks/propagate/helpers/linea.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,56 @@ | ||
import { createLoggingContext, RequestContext } from "@connext/nxtp-utils"; | ||
import { utils } from "ethers"; | ||
import { BigNumber } from "ethers"; | ||
|
||
import { LineaSDK, getBestProvider } from "../../../mockable"; | ||
import { getContext } from "../propagate"; | ||
import { ExtraPropagateParam } from "../operations/propagate"; | ||
import { NoProviderForDomain } from "../errors"; | ||
|
||
// example at https://github.com/OffchainLabs/arbitrum-tutorials/blob/master/packages/greeter/scripts/exec.js | ||
// https://docs.linea.build/use-mainnet/bridges-of-linea#manual-vs-automatic-claiming | ||
export const getPropagateParams = async ( | ||
l2domain: string, | ||
l2ChainId: number, | ||
l1ChainId: number, | ||
_requestContext: RequestContext, | ||
): Promise<ExtraPropagateParam> => { | ||
const { logger } = getContext(); | ||
const { config, logger } = getContext(); | ||
const { methodContext, requestContext } = createLoggingContext(getPropagateParams.name, _requestContext); | ||
logger.info("Getting propagate params for Linea", requestContext, methodContext, { | ||
l2domain, | ||
l1ChainId, | ||
l2ChainId, | ||
}); | ||
|
||
// the additional optional "postman" fee = 0 currently | ||
const _fee = utils.parseEther("0").toString(); | ||
const l2RpcUrl = await getBestProvider(config.chains[l2domain]?.providers ?? []); | ||
if (!l2RpcUrl) { | ||
throw new NoProviderForDomain(l2domain, requestContext, methodContext); | ||
} | ||
const l1RpcUrl = await getBestProvider(config.chains[config.hubDomain]?.providers ?? []); | ||
if (!l1RpcUrl) { | ||
throw new NoProviderForDomain(config.hubDomain, requestContext, methodContext); | ||
} | ||
|
||
// Postman Fee = target layer gas price * (gas estimated + gas limit surplus) * margin | ||
// where target layer gas price is eth_gasPrice on the target layer, gas estimated = 100,000, gas limit surplus = 6000, and margin = 2. | ||
const sdk = new LineaSDK({ | ||
l1RpcUrl: l1RpcUrl, // L1 rpc url | ||
l2RpcUrl: l2RpcUrl, // L2 rpc url | ||
network: config.network === "mainnet" ? "linea-mainnet" : "linea-goerli", // network you want to interact with (either linea-mainnet or linea-goerli) | ||
mode: "read-only", // contract wrapper class mode (read-only or read-write), read-only: only read contracts state, read-write: read contracts state and claim messages | ||
}); | ||
const gasPrice = (await sdk.getL2Contract().get1559Fees()).maxFeePerGas; | ||
|
||
// On linea-goerli claimMessage gasLimit was 83717 | ||
// https://goerli.lineascan.build/tx/0x4c477dfcbc22cd99b461cfe714a6ad60796331d3c13e55a74a6de51c3cd9aab6 | ||
const gasLimit = BigNumber.from("120000"); | ||
|
||
const _fee = gasPrice.mul(gasLimit).toString(); | ||
|
||
logger.info("Got propagate params for Linea", requestContext, methodContext, { | ||
gasPrice: gasPrice.toString(), | ||
gasLimit: gasLimit.toString(), | ||
fee: _fee.toString(), | ||
}); | ||
|
||
return { _connector: "", _fee, _encodedData: "0x" }; | ||
}; |
56 changes: 56 additions & 0 deletions
56
packages/agents/lighthouse/test/tasks/propagate/helpers/linea.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { createRequestContext, expect, mkAddress } from "@connext/nxtp-utils"; | ||
import { SinonStub, stub, createStubInstance, SinonStubbedInstance } from "sinon"; | ||
import { NoProviderForDomain } from "../../../../src/tasks/propagate/errors"; | ||
|
||
import { getPropagateParams } from "../../../../src/tasks/propagate/helpers/linea"; | ||
import * as Mockable from "../../../../src/mockable"; | ||
import { getBestProviderMock, propagateCtxMock } from "../../../globalTestHook"; | ||
import { mock } from "../../../mock"; | ||
import { BigNumber } from "ethers"; | ||
|
||
const requestContext = createRequestContext("test"); | ||
|
||
let getL2Contract: SinonStub; | ||
|
||
class MockLineaSDK { | ||
public getL2Contract = getL2Contract; | ||
} | ||
|
||
describe("Helpers: Linea", () => { | ||
describe("#getPropagateParams", () => { | ||
beforeEach(() => { | ||
stub(Mockable, "LineaSDK").value(MockLineaSDK); | ||
getL2Contract = stub().returns({ | ||
get1559Fees: stub().resolves({ | ||
maxFeePerGas: BigNumber.from(10), | ||
maxPriorityFeePerGas: BigNumber.from(1), | ||
}), | ||
} as any); | ||
}); | ||
|
||
it("should throw an error if no provider for spoke domain", async () => { | ||
delete propagateCtxMock.config.chains[mock.domain.B]; | ||
getBestProviderMock.resolves(undefined); | ||
await expect( | ||
getPropagateParams(mock.domain.B, +mock.chain.B, +mock.chain.A, requestContext), | ||
).to.eventually.be.rejectedWith(NoProviderForDomain); | ||
}); | ||
|
||
it("should throw an error if no provider for hub domain", async () => { | ||
delete propagateCtxMock.config.chains[mock.domain.A]; | ||
getBestProviderMock.resolves(undefined); | ||
await expect( | ||
getPropagateParams(mock.domain.B, +mock.chain.B, +mock.chain.A, requestContext), | ||
).to.eventually.be.rejectedWith(NoProviderForDomain); | ||
}); | ||
|
||
it("should return necessary data successfully", async () => { | ||
const data = await getPropagateParams(mock.domain.B, +mock.chain.B, +mock.chain.A, requestContext); | ||
expect(data).to.deep.eq({ | ||
_connector: "", | ||
_fee: BigNumber.from(10).mul(BigNumber.from(120000)).toString(), | ||
_encodedData: "0x", | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
packages/deployments/contracts/tasks/connector/claimLinea.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { Wallet } from "ethers"; | ||
import { task } from "hardhat/config"; | ||
import { LineaSDK } from "@consensys/linea-sdk"; | ||
|
||
import { Env, getMessagingProtocolConfig, mustGetEnv, ProtocolNetwork } from "../../src/utils"; | ||
|
||
import { getProviderUrlFromHardhatConfig } from "../../src"; | ||
|
||
type TaskArgs = { | ||
hash: string; | ||
networkType?: ProtocolNetwork; | ||
env?: Env; | ||
}; | ||
|
||
export default task("claim-linea", "Claim messages on both of L1 and L2") | ||
.addParam("hash", "The transaction hash of the message sent. network should be origin side.") | ||
.addOptionalParam("env", "Environment of contracts") | ||
.addOptionalParam("networkType", "Type of network of contracts") | ||
.setAction(async ({ hash, networkType: _networkType, env: _env }: TaskArgs, hre) => { | ||
const deployer = Wallet.fromMnemonic(process.env.MNEMONIC!); | ||
|
||
const env = mustGetEnv(_env); | ||
const networkType = _networkType ?? ProtocolNetwork.TESTNET; | ||
console.log("networkType: ", networkType); | ||
console.log("env:", env); | ||
console.log("transaction hash", hash); | ||
console.log("deployer", deployer.address); | ||
|
||
// get config | ||
const protocolConfig = getMessagingProtocolConfig(networkType); | ||
const hub = protocolConfig.hub; | ||
const chainId = hre.network.config.chainId!; | ||
|
||
// Right now this only works on arbitrum, error if that is not the correct network | ||
if (chainId != hub && protocolConfig.configs[chainId].prefix != "Linea") { | ||
throw new Error(`Only linea / linea goerli supported`); | ||
} | ||
|
||
const spoke = hub == 1 ? 59144 : 59140; | ||
const sdk = new LineaSDK({ | ||
l1RpcUrl: getProviderUrlFromHardhatConfig(hub), // L1 rpc url | ||
l2RpcUrl: getProviderUrlFromHardhatConfig(spoke), // L2 rpc url | ||
l1SignerPrivateKey: deployer.privateKey ?? "", // L1 account private key (optional if you use mode = read-only) | ||
l2SignerPrivateKey: deployer.privateKey ?? "", // L2 account private key (optional if you use mode = read-only) | ||
network: hub == 1 ? "linea-mainnet" : "linea-goerli", // network you want to interact with (either linea-mainnet or linea-goerli) | ||
mode: "read-write", // contract wrapper class mode (read-only or read-write), read-only: only read contracts state, read-write: read contracts state and claim messages | ||
}); | ||
|
||
// get L1/L2 contract | ||
const originContract = chainId == hub ? sdk.getL1Contract() : sdk.getL2Contract(); | ||
|
||
// get Message Status | ||
const messages = await originContract.getMessagesByTransactionHash(hash); | ||
|
||
if (!messages?.length) { | ||
throw new Error(`${hash} has no message sent`); | ||
} | ||
console.log("message: ", messages[0]); | ||
|
||
const destContract = chainId == hub ? sdk.getL2Contract() : sdk.getL1Contract(); | ||
|
||
// returns on-chain message status by message hash | ||
const messageStatus = await destContract.getMessageStatus(messages[0].messageHash); | ||
console.log("message status: ", messageStatus); | ||
|
||
if (messageStatus === "CLAIMED") { | ||
console.log("message already claimed!! skipping..."); | ||
} else if (messageStatus === "CLAIMABLE") { | ||
console.log("Claimable message status. "); | ||
let claimMessage = await destContract.claim({ | ||
// claims message by message | ||
...messages[0], | ||
feeRecipient: deployer.address, // address that will receive fees. by default it is the message sender | ||
}); | ||
console.log(claimMessage); | ||
} else { | ||
console.log("unknown message status. skipping..."); | ||
} | ||
}); |
Oops, something went wrong.