Fireblocks ethers provider

Hello everyone! Ricardo told me to post here. I’m having issues regarding the integration to and existing service. We are using nestjs, ethers.js library and a conctract deployed on Polygon AMOY testnet, so we used the EVM approach with:

@fireblocks/fireblocks-web3-provider.

Versions:

    "@fireblocks/fireblocks-web3-provider": "^1.3.7",
    "ethers": "5",

The apikey and secretkey are from a user with role EDITOR, as @Ricardo pointed us.

This is the code:



  gasLimit = 300000;

  constructor() {
    this.initialize().then(() => {
    this.loadContracts();
    }).catch(error => {
      console.error('Failed to initialize ethers:', error);
    });

    this.loadContracts();
  }

  private async initialize() {
    try {
      const fireblocksProvider = new FireblocksWeb3Provider({
        apiKey: process.env.FIREBLOCKS_API_KEY_EDITOR,
        privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_EDITOR,
        chainId: ChainId.POLYGON_AMOY,
        apiBaseUrl: ApiBaseUrl.Sandbox // Comment when not in the SANDBOX
      });

      this.provider = new ethers.providers.Web3Provider(fireblocksProvider);
      this.signer = this.provider.getSigner();
      
    } catch (error) {
      console.error('Failed to initialize Fireblocks provider:', error);
      throw new Error(`Initialization error: ${error.message}`);
    }
  }
  

  //This method reads a contract.json file that has every ABI of our contracts.
  private loadContracts() {
    const contractsPath = path.resolve(__dirname, '../contracts.json');
    console.log(`Loading contracts from: ${contractsPath}`);

    try {
      const contractsData = fs.readFileSync(contractsPath, 'utf8');
      this.contracts = JSON.parse(contractsData).contracts;
    } catch (error) {
      console.error('Failed to load contracts:', error);
      this.contracts = [];
    }
  }

  //This method throws back the selected contract ABI
  private getContract(contractId: number) {
    this.loadContracts();
    console.log("Contract ID requested:", contractId);
    if (typeof contractId === 'undefined' || contractId >= this.contracts.length) {
      console.error(`Invalid contract ID ${contractId}`);
      throw new Error(`Contract with ID ${contractId} not found`);
    }
    const contractInfo = this.contracts[contractId];
    const contract = new ethers.Contract(contractInfo.address, contractInfo.abi, this.signer);
    return contract.connect(this.signer);
  }
  
  public async transfer(contractId: number, from: string, to: string, tokenId: number, amount: number): Promise<void> {
    try {
      const contract = this.getContract(contractId);
      const gasLimit = this.gasLimit;
      const tx = await contract.safeTransferFrom(from, to, tokenId, amount, "0x00", { gasLimit });
      await tx.wait();
      console.log(`Transferred ${amount} of token ID ${tokenId} from ${from} to ${to}`);
    } catch (error) {
      console.error('Transfer failed:', error);
    }
  }

And this is the error:

Would be awesome if you guys could help us :slight_smile:
thx

Hey @Simon,

SMART_CONTRACT_EXECUTION_REVERTED subStatus basically means that the contract returned an error when we tried to simulate the function execution for gas estimation.

Unfortunately, I am not able to see the contract’s code as it’s not verified (Contract Address 0xf4a138ea240cfc2b4b58a165ebe62cd6b744607d | PolygonScan) but I can see that you’re calling the safeTransferFrom method so I assume that you’re trying to move an NFT.

In that case there might be a few potential reasons for the execution reverting issues (of course depending on the implementation of the function):

  1. The provided from address does not hold the NFT with the provided id
  2. The caller is not approved to execute this call on behalf of the source address (should be granted with approval by calling setApprovalForAll first)

I took the contractCallData that was sent in one of your failed transactions (fireblocks transaction ID: 516aa654-4c42-488f-b485-f9c57854592f) and was able to decode it:

Encoded data:
0xf242432a0000000000000000000000009745dcab234f4dcf8a299b22e2e18ce31fe6a04f0000000000000000000000005f73d68c43a5a191586530b5fe0a8240ce0dbfc00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000

Decoded:

signature hash: 0xf242432a
function: safeTransferFrom 
from - address: 0x9745DcAb234f4DcF8A299B22e2E18cE31fe6A04F
to - address: 0x5F73d68C43A5a191586530B5Fe0A8240ce0DbfC0
id - uint256: 1
amount - uint256: 1
data - bytes: 0x00

In addition, I can see that you are calling it from vault account with id=0 which has the following AMOY address: 0xb9AE82581F27d97Cf84Ea2770982aEdDca2Baa55.

From a quick look on the transaction history of this contract, I do not see any mint calls that actually minted tokens to 0x9745DcAb234f4DcF8A299B22e2E18cE31fe6A04F. Are you sure that this is indeed the correct source address that you want to move the tokens from?

1 Like

Post 1/2

Hello, @SlavaSereb! Sorry for the delay in my reply.

Yes, i may post a picture of one of the hundreds of payloads i tried, and it can be wrong. But i can assure i only get that type of error.

More details:

Contract address: Contract Address 0xf4a138ea240cfc2b4b58a165ebe62cd6b744607d | PolygonScan

The account address where that contract lives:

Now, for example i’m trying to mint a NFT in the mother account where that contract lives:

 fireblocks-web3-provider:error Simulate the failed transaction on Tenderly: https://dashboard.tenderly.co/simulator/new?gas=300000&from=0xb9ae82581f27d97cf84ea2770982aeddca2baa55&contractAddress=0xf4a138ea240cfc2b4b58a165ebe62cd6b744607d&rawFunctionInput=0x731133e90000000000000000000000009745dcab234f4dcf8a299b22e2e18ce31fe6a04f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000&network=80002 +34s
Minting failed: Error: Fireblocks transaction e37d33d5-9ccc-4fbf-b930-a0e92ed536e1 was not completed successfully. Final Status: FAILED (SMART_CONTRACT_EXECUTION_FAILED)
    at FireblocksWeb3Provider.createTransaction (/api/node_modules/@fireblocks/fireblocks-web3-provider/src/provider.ts:625:18)

Now, trying to mint into a personal address:

  fireblocks-web3-provider:error Simulate the failed transaction on Tenderly: https://dashboard.tenderly.co/simulator/new?gas=300000&from=0xb9ae82581f27d97cf84ea2770982aeddca2baa55&contractAddress=0xf4a138ea240cfc2b4b58a165ebe62cd6b744607d&rawFunctionInput=0x731133e90000000000000000000000005f73d68c43a5a191586530b5fe0a8240ce0dbfc000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000&network=80002 +5m
Minting failed: Error: Fireblocks transaction 140a62b5-0ab1-417b-95a8-707ed6d3b021 was not completed successfully. Final Status: FAILED (SMART_CONTRACT_EXECUTION_FAILED)
    at FireblocksWeb3Provider.createTransaction (/api/node_modules/@fireblocks/fireblocks-web3-provider/src/provider.ts:625:18)

2/2 (workaround for the max 2 links limit)

Before adding the fireblocks provider, i actually made the same operations as you can see on the smart contract history. For example, minting to my personal polygon address:

And the contract, is a pretty simple one, an ERC1155 wrapper.

Hey @Simon,

I can see from this contract code that the only account that is allowed to mint tokens on this contract is the owner of the contract.

You can see that by looking on the mint function modifier that is set to onlyOwner and this is being checked by the Ownable contract (by calling a private method _checkOwner()) that you’re importing from OpenZeppelin.

By default the owner of the contract is the wallet that actually deployed the contract unless specified otherwise. Unfortunately, I cannot verify your contract on Polygon scan hence I cannot decode the deployment transaction and see the constructor arguments on this initial deployment but my educated guess would be that the owner was set to the address that actually deployed the contract, which is:
0x9745DcAb234f4DcF8A299B22e2E18cE31fe6A04F (I can see that there were successful minting calls from this address therefore this is definitely a good indication).

In order to be able to mint tokens on this contract from your Fireblocks wallet, you have 2 options:

  1. Redeploy the contract via your Fireblocks Wallet so the newly deployed contract will be owned by the Fireblocks Wallet that actually deployed the contract.

  2. Transfer the contract’s ownership - this can be done by calling the transferOwnership(address newOwner) method on your contract.

I suggest to go with #2 as it quite easier at this stage and will keep your existing contract address etc.

Kindly note that transferOwnership(address newOwner) should be called by the existing owner of the contract, which is the wallet that you have outside of Fireblocks and the newOwner argument is your Fireblocks Amoy wallet that you want to be the new owner.

Once the ownership is transferred, you will be able to mint tokens from your Fireblocks Amoy wallet that was defined as the new owner.

Hope it sheds some light on the issue.

1 Like

Hello! Thanks for all the support @SlavaSereb.

We already transferred the ownership of the contract and everything is working like a charm.