Skip to content
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

bug: OFT Arbitrum-Sepolia -> Sepolia Config Errors and OFT contract Gas Estimation Problems #709

Open
berktec opened this issue Jul 17, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@berktec
Copy link

berktec commented Jul 17, 2024

Hi there,

Looking to deploy an OFT for multiple chains, but starting on Testnets of course. I have Both OFTs Deployed to Arbitrum-Sepolia and to ETH-Sepolia, and I have been facing two Errors.

  1. Error: An error occurred while getting the OApp configuration: AssertionError [ERR_ASSERTION]: Could not find a deployment for address '0xa96b3749D01A50c4353f66455dBD92a52778C65A' on ARBSEP_V2_TESTNET (arbitrumSepolia)

This error occurs when attempting to run the wiring with this command:
npx hardhat lz:oapp:peers:get --oapp-config layerzero.config.ts

and this HardHat Config

const config: HardhatUserConfig = {
    paths: {
        cache: 'cache/hardhat',
    },
    solidity: {
        compilers: [
            {
                version: '0.8.22',
                settings: {
                    optimizer: {
                        enabled: true,
                        runs: 200,
                    },
                },
            },
        ],
    },
    networks: {
        sepolia: {
            eid: EndpointId.SEPOLIA_V2_TESTNET,
            url: 'https://eth-sepolia.g.alchemy.com/v2/<API_KEY>',
            accounts,
        },
        arbitrumSepolia: {
            eid: EndpointId.ARBSEP_V2_TESTNET,
            url: 'https://arb-sepolia.g.alchemy.com/v2/<API_KEY>',
            accounts,
        },
    },
    namedAccounts: {
        deployer: {
            default: 0, // wallet address of index[0], of the mnemonic in .env
        },
    },

and this layerzeroConfig.ts

export const sepoliaContract: OmniPointHardhat = {
    eid: EndpointId.SEPOLIA_V2_TESTNET,

    contractName: 'GODOFT',
    address: '0xDee109faA9a53E6aBD3fE4ca2d1E423E4FC6D019',
}

export const arbSepContract: OmniPointHardhat = {
    eid: EndpointId.ARBSEP_V2_TESTNET,

    contractName: 'GODOFT',
    address: '0xa96b3749D01A50c4353f66455dBD92a52778C65A',
}

const config: OAppOmniGraphHardhat = {
    contracts: [
        {
            contract: arbSepContract,
        },
        {
            contract: sepoliaContract,
        },
    ],
    connections: [
        {
            from: arbSepContract,
            to: sepoliaContract,
        },
        {
            from: sepoliaContract,
            to: arbSepContract,
        },
    ],
}

export default config

Expected behavior
I would have expected in the first instance for the Configuration to find the Contract deployed on the Arbitrum Sepolia Network, as clearly the transactions and other infrastructure is functioning properly.

Both of the Contracts are IN-fact deployed and validated on testnets
https://sepolia.arbiscan.io/address/0xa96b3749D01A50c4353f66455dBD92a52778C65A
https://sepolia.etherscan.io/address/0xDee109faA9a53E6aBD3fE4ca2d1E423E4FC6D019

Screenshots

Environment (please complete the following information):

  • OS: [e.g. macOS]
  • OS Version [e.g. 14.1]
  • VM: [e.g. node]
  • VM Version: [e.g. 18.18.0] (Ive Attemped this on several different versions of Node 18 - 22)

Additional context
Additional Context about the Insane Gas Price issue:

Here is the Task that I created to test the bridging between the two test nets:

The second Problem is when trying to bridge any more than 1-10 tokens the Gas fee quickly jumps from Dust essentially to 10x or more as constituted by this error from hardhat.

 reason: 'insufficient funds for intrinsic transaction cost',
  code: 'INSUFFICIENT_FUNDS',
  error: ProviderError: insufficient funds for gas * price + value: balance 203411533023759192, queued cost 153614836329641605, tx cost 137077919996323037, overshot 87281223302205450
task('lz:oft:send', 'test send')
    .addParam('contractA', 'contract address on network A')
    .addParam('contractB', 'contract address on network B')
    .addParam('networkA', 'name of the network A')
    .addParam('networkB', 'name of the network B')
    .addParam('amount', 'amount to transfer in eth')
    .setAction(async (taskArgs, { ethers }) => {
        const eidA = getEidForNetworkName(taskArgs.networkA)
        const eidB = getEidForNetworkName(taskArgs.networkB)
        const contractA = taskArgs.contractA
        const contractB = taskArgs.contractB
        const environmentFactory = createGetHreByEid()
        const providerFactory = createProviderFactory(environmentFactory)
        const signer = (await providerFactory(eidA)).getSigner()

        const oftContractFactory = await ethers.getContractFactory('MyOFT', signer)
        const oft = oftContractFactory.attach(contractA)

        const decimals = await oft.decimals()
        const amount = ethers.utils.parseUnits(taskArgs.amount, decimals)
        const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex().toString()

        const sender = await signer.getAddress()
        const sendParam = [eidB, ethers.utils.zeroPad(sender, 32), amount, amount, options, '0x', '0x']
        console.log(sendParam)
        const [nativeFee] = await oft.quoteSend(sendParam, false)
        console.log({ eidA, eidB, contractA, contractB, amount, sender, nativeFee })
        console.log(
            `sending ${taskArgs.amount} token(s) from network ${taskArgs.networkA} to network ${taskArgs.networkB}`
        )
        console.log(sendParam, [nativeFee, 0], sender, { value: nativeFee })

        const r = await oft.send(sendParam, [nativeFee, 0], sender, { value: nativeFee })
        console.log(`Tx initiated. See: https://layerzeroscan.com/tx/${r.hash}`)
    })

In Summary:

What am I missing in terms of configuration here to prevent the wild fluctuation in Gas Prices: and How can I get the Arbitrum Sepolia testnet to recognize the Contracts I have deployed to use these tools effectively

@berktec berktec added the bug Something isn't working label Jul 17, 2024
@janjakubnanista
Copy link
Contributor

Hey @berktec! Thanks for submitting an issue.

As for the first problem, I see two things that could contribute to the problem:

  1. A minor thing - in your layerzeroConfig.ts, you are specifying both address and contractName - in general, it is sufficient to specify the contractName and the address will be grabbed from a deployment file
  2. Are you using hardhat-deploy in your project? Are the deployment files for these contracts present? For this to work I would expect two deployment files, one in deployments/sepolia/GODOFT.json and another one in deployments/arbitrumSepolia/GODOFT.json

Could you check those two please?

Also I notice you mentioned you are using the lz:oapp:peers:get to wire the OApp - this command will just check whether the peers have been set, to actually run the wiring you'll need to run lz:oapp:wire.

@berktec
Copy link
Author

berktec commented Jul 18, 2024

Hi @janjakubnanista

Thanks for looking into this: so with 1. I changed to this with no difference in the error

export const sepoliaContract: OmniPointHardhat = {
    eid: EndpointId.SEPOLIA_V2_TESTNET,

    // contractName: 'GODOFT',
    address: '0xDee109faA9a53E6aBD3fE4ca2d1E423E4FC6D019',
}

export const arbSepContract: OmniPointHardhat = {
    eid: EndpointId.ARBSEP_V2_TESTNET,

    // contractName: 'GODOFT',
    address: '0xa96b3749D01A50c4353f66455dBD92a52778C65A',
}

For 2. I did not use the hardhat deploy to deploy the contracts, I opted for a different tool to get them on chain.

and for your note at the bottom, that's true, and if I use either lz:oapp:peers:get or lz:oapp:wire

I get the same error still

info:    [OApp] Checking OApp configuration
error:   [OApp] Failed to check OApp configuration: AssertionError [ERR_ASSERTION]: Could not find a deployment for address '0xa96b3749D01A50c4353f66455dBD92a52778C65A' on ARBSEP_V2_TESTNET (arbitrumSepolia)
An unexpected error occurred:

Error: An error occurred while getting the OApp configuration: AssertionError [ERR_ASSERTION]: Could not find a deployment for address '0xa96b3749D01A50c4353f66455dBD92a52778C65A' on ARBSEP_V2_TESTNET (arbitrumSepolia)

@janjakubnanista
Copy link
Contributor

Hey @berktec!

Yeah that's the problem. We use hardhat-deploy to get the contract ABIs, that's why you're seeing the error there.

There are several ways around this, depending on whether you want a quick solution or something more sturdy & possibly sustainable:

Easy but fragile

Create a fake deployment files for your contracts, the only fields they need to contain are the address and abi

E.g. put this into deployments/sepolia/GODOFT.json:

{
  "address": "0xDee109faA9a53E6aBD3fE4ca2d1E423E4FC6D019",
  "abi": [
    /* You'll have to copy the ABI from the solc output /*
  ]
}

More involved but sturdy

We use something we call an OAppFactory, a sync/async function (up to the implementor, we'll accept both) that creates an OApp SDK based on an OmniPoint (a type that stands for { eid: EndpointId, address: string }). The SDK needs an ethers.js Contract to communicate with the chain. We call a Contract + EndpointId an OmniContract and a function that creates an OmniContractFactory.

If you're not using hardhat-deploy to deploy your contracts, you'll need to provide your own OmniContractFactory, whose code depends on what tools you used to deploy your contracts or what tools you have at hand to get a contract ABI based on either the deployed name or its address. You can see our implementation here for inspiration but your code will most likely be very different.

// hardhat.config.ts

// These are the additional imports you'll need (you'll probably want to add these to your devDependencies)
import { Contract } from '@ethersproject/contracts'
import type { OmniContractFactory } from '@layerzerolabs/devtools-evm';
import { createConnectedContractFactory } from '@layerzerolabs/devtools-evm-hardhat';
import type { IOApp, OAppOmniGraph, OAppFactory } from '@layerzerolabs/ua-devtools'
import { createOAppFactory } from '@layerzerolabs/ua-devtools-evm'
import { createEndpointV2Factory } from '@layerzerolabs/protocol-devtools-evm'
import {
    SUBTASK_LZ_OAPP_CONFIG_LOAD,
    SUBTASK_LZ_OAPP_WIRE_CONFIGURE,
    type SubtaskConfigureTaskArgs,
    type SubtaskLoadConfigTaskArgs,
} from '@layerzerolabs/ua-devtools-evm-hardhat'

// We want to hook into the SUBTASK_LZ_OAPP_WIRE_CONFIGURE subtask
// that compiles the list of transactions that need to be submitted
// by comparing the on-chain information and your config
//
// This is the subtask that needs to know how to talk to the chain (i.e. it needs the SDK)
subtask(
        SUBTASK_LZ_OAPP_WIRE_CONFIGURE,
        'Configure GOD OFT',
        (args: SubtaskConfigureTaskArgs<OAppOmniGraph, IOApp>, hre, runSuper) =>
            runSuper({
                ...args,
                // We want to override the sdkFactory and provide our own
                //
                // See the default implementation here https://github.com/stargate-protocol/stargate-v2/blob/main/packages/ua-devtools-evm/src/oapp/factory.ts
                sdkFactory: createOAppFactory(
                  // We will wrap our contract factory with createConnectedContractFactory to attach providers to it
                  createConnectedContractFactory(myOFTContractFactory), 
                  // createOAppFactory also requires and EndpointV2 SDK factory
                  // 
                  // In the default implementation, it is optional and based on the contract factory above
                  // However, since we made that one only work with our specific OFT (i.e. it would not know where to get the EndpointV2 ABI),
                  // we need to supply the default one
                  createEndpointV2Factory(createConnectedContractFactory())
                ),
            })
    )

const myOFTContractFactory: OmniContractFactory = async ({ eid, address }) => {
  // Here the goal is to grab your OFT's ABI and create an ether.js contract based on it
  // 
  // You can go crazy here, make it very specific to your OFT or very generic
  const abi = ...

  return new Contract(address, abi)
}

There are a couple of variations to this approach, most notably you can make myOFTContractFactory fall back to a factory from createContractFactory from @layerzerolabs/devtools-evm-hardhat if the address you passed is not your OFT contract. In this way, you can drop the createEndpointV2Factory above.

@janjakubnanista
Copy link
Contributor

Hey @berktec!

Small update just to keep you in the loop. We are considering moving from ethers to viem and this change will include a change that might reduce the code you need to write for your project.

The ABIs for the standard contracts will be embedded into the project so you'll no longer need to override any factories as such. It will take some time to work it in and, it being a breaking change, will also affect any code you might have written.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants