A tutorial on how to make a script for buying NFTs on caviar
For this tutorial we will use the ethers.js library and alchemy to programatically buy a few BAYC NFTs on the goerli testnet. To get goerli ETH you can claim some from paradigm's faucet.
The first step is to create an empty directory and install the dependencies:
Now before we start writing code we also need the required ABI files (caviar.abi.json and pair.abi.json) to interact with the contracts. Those can be found on the caviar github repository here. Copy and paste caviar.abi.json and pair.abi.json into the root of caviar-swap-example/.
Great, now we can start actually writing the script. Create a file buy.js and then open it and import the required dependencies along with making an empty entrypoint function called main().
Now we need to setup an ethers.js signer so that we can interact with the chain and send transactions. Inside of the main() function:
constmain=async () => {// create the provider to connect to the networkconstprovider=newethers.providers.AlchemyProvider("goerli",process.env.ALCHEMY_API_KEY );// create the signer to sign transactions from a walletconstsigner=newethers.Wallet(process.env.PRIVATE_KEY, provider);};
And then create a .env file with the following content:
ALCHEMY_API_KEY=<your alchemy api key>PRIVATE_KEY=<your private key>
Now let's fetch the pair contract address for floor BAYC:ETH pair and create a contract instance that we can interact with. We can do this by querying the Caviar factory contract:
constmain=async () => {/// ... SNIP ... ///// create the contract instance for caviarconstGOERLI_CAVIAR_ADDRESS="0x15B9D8ba57E67D6683f3E7Bec24A32b98a7cdb6b";constGOERLI_BAYC_ADDRESS="0xc1a308d95344716054d4c078831376fc78c4fd72";constCaviar=newethers.Contract(GOERLI_CAVIAR_ADDRESS, caviarAbi, provider );// fetch the address of the floor pair for BAYC:ETHconstbaycEthPairAddress=awaitCaviar.pairs(GOERLI_BAYC_ADDRESS,// address of the NFT tokenethers.constants.AddressZero,// address of the base token (address(0) for ETH)ethers.constants.HashZero // hash of the merkle root for valid token ids (hash(0) for no merkle root) );// create the contract instance for the floor BAYC:ETH pairconstBaycEthPair=newethers.Contract(baycEthPairAddress, pairAbi, signer);console.log("BAYC:ETH pair address:", baycEthPairAddress);};
Cool, that's most of the setup out of the way. Now let's query the contract and figure out how much ETH we will have to spend to buy two BAYCs from the pool. To do this we need to query the reserves and implement some xy=k logic which is the same as buyQuote in the pair contract:
constmain=async () => {/// ... SNIP ... ///// fetch the reserves for the BAYC:ETH pairconstbaseTokenReserves=awaitBaycEthPair.baseTokenReserves();constfractionalTokenReserves=awaitBaycEthPair.fractionalTokenReserves();// calculate the amount of ETH to buy 2 BAYCs using xy=k formula// 2 BAYCs == 2 * 1e18 fractional tokensconstAMOUNT_TO_BUY=ethers.BigNumber.from("2").mul(ethers.utils.parseUnits("1",18));constethCost=AMOUNT_TO_BUY.mul(baseTokenReserves).mul("1000").div(fractionalTokenReserves.sub(AMOUNT_TO_BUY).mul("997")).add("1");console.log("ETHER cost to buy 2 BAYCs:",ethers.utils.formatEther(ethCost),"ETH");};
So we know how much ETH we will need to spend. All we need to do now is pick some NFTs that we want to buy from the pair. In order to do this we need to find out what NFTs the pair actually holds. This can be done via the alchemy NFT API:
constmain=async () => {/// ... SNIP ... ///// create the alchemy instanceconstalchemy=newAlchemy({ apiKey:process.env.ALCHEMY_API_KEY, network:Network.ETH_GOERLI, });// fetch some BAYC NFTs that are currently in the pair contractconst { ownedNfts } =awaitalchemy.nft.getNftsForOwner(baycEthPairAddress, { contractAddresses: [GOERLI_BAYC_ADDRESS], });consttokenIdsToBuy= [ownedNfts[0].tokenId, ownedNfts[1].tokenId];console.log("Token Ids to buy:", tokenIdsToBuy);constdeadline=parseInt((Date.now() +1000*60*60) /1000);console.log("Trade deadline unix timestamp:", deadline);};
Although you can choose any two NFTs, we will just choose the first two that were returned from the response. Now for the final moment! let's actually submit a transaction to buy:
constmain=async () => {/// ... SNIP ... //// submit the transaction to buy the NFTsconsttx=awaitBaycEthPair.nftBuy(tokenIdsToBuy, ethCost, deadline, { value: ethCost });console.log("Transaction:", tx);};
If all goes well you should have just bought two BAYCs. Here is a link to a transaction I submitted using this script on etherscan. Yours should look quite similar except with different token IDs. All of the code for this tutorial is available on github here. The next step is now to make a sell transaction. The sell tutorial is available here.