How can I link library and contract in one file?

Viewed 1523

I am writing smart contract in solidity with hardhat and facing error like missing links for the following libraries.

I think I should do something with sample-nft-test.js, but I don't have any idea what to do.

Does any body know how to solve the problem?

% bin/npm-install
% bin/test
  SampleNft
    1) "before each" hook for "should be able to return token name by token id"


  0 passing (2s)
  1 failing

  1) SampleNft
       "before each" hook for "should be able to return token name by token id":
     NomicLabsHardhatPluginError: The contract SampleNft is missing links for the following libraries:
* contracts/SampleNft.sol:TokenTrait

Learn more about linking contracts at https://hardhat.org/plugins/nomiclabs-hardhat-ethers.html#library-linking

      at collectLibrariesAndLink (node_modules/@nomiclabs/hardhat-ethers/src/internal/helpers.ts:258:11)
      at getContractFactoryFromArtifact (node_modules/@nomiclabs/hardhat-ethers/src/internal/helpers.ts:149:32)
      at getContractFactory (node_modules/@nomiclabs/hardhat-ethers/src/internal/helpers.ts:93:12)
      at Context.<anonymous> (test/sample-nft-test.js:13:29)

Source codes to reproduce the error

sample-nft-test.js

const {
  expect,
} = require("chai");

const {
  ethers,
} = require("hardhat");

describe("SampleNft", () => {
  beforeEach(async () => {
    signers = await ethers.getSigners();

    const contractFactory = await ethers.getContractFactory("SampleNft", {
      signer: signers[0],
    });

    contract = await contractFactory.deploy();
  });

  it("should be able to return token name by token id", async () => {
    await contract.deployed();

    await contract.getName();
  });
});

SampleNft.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

struct Token {
    string name;
}

library TokenTrait {
    function getName(Token memory token) external pure returns (string memory) {
        return token.name;
    }
}

contract SampleNft {
    using TokenTrait for Token;

    uint tokenId;

    mapping(uint => Token) private _tokenIdTokenMap;

    constructor() {
        tokenId = 0;
    }

    function mint() external {
        Token memory token = Token("First Token");

        _tokenIdTokenMap[tokenId] = token;
    }

    function getName() external view returns (string memory) {
        Token memory token = _tokenIdTokenMap[tokenId];

        return token.getName();
    }
}
2 Answers

The library link should be added when deploying contract. Please replace with the following code for beforeEach part

  beforeEach(async () => {
    signers = await ethers.getSigners();

    const Lib = await ethers.getContractFactory("TokenTrait");
    const lib = await Lib.deploy();
    await lib.deployed();

    const contractFactory = await ethers.getContractFactory("SampleNft", {
      signer: signers[0],
      libraries: {
        TokenTrait: lib.address,
      },
    });

    contract = await contractFactory.deploy();
  });
library TokenTrait {
    function getName(Token memory token) external pure returns (string memory) {
        return token.name;
    }
}

You set the visibility of the function as external. In this case, you have to deploy the library and then need to specify a link for the library in contract deployment. The way to fix your issue is to make the function visibility internal.

Related