Provide protection programmatically

Learn how to provide protection by supplying assets to a protection market programmatically.

Cozy provides a protected borrowing marketplace for investing in decentralized finance protocols that offer protection against a loss of funds. You can participate in this marketplace by supplying assets to an existing protection market. After you deposit assets, you earn interest on the deposit from investors who borrow from the protection market. You continue earning interest as long If the protection market operates without having a trigger event occur. If there is a hack, exploit, or other problem that triggers protection in the market where you have supplied assets, all outstanding debts in that market are forgiven. The interest you earn is your reward for taking on the risk associated with trigger event. In evaluating the risk involved, you should consider how likely it will be for a particular market's trigger condition to be satisfied.

This guide illustrates how you can write a script using TypeScript that you can then use to supply assets to a protection market programmatically.

All code snippets are from the provide-protection.ts script in the Cozy Developer Guides repository. See that repository for more context, definitions of helper methods used, etc.

Everything in this guide assumes that you have experience with JavaScript, ethers.js, and Solidity.

Supply assets to a protection market

Before you can provide protection, you need to identify the asset you want to deposit and select the protection market where you want the assets you supply to be used.

All markets in Cozy—money markets and protection markets—have a trigger state variable. If the value of the trigger property is the zero address, the money market does not have a trigger contract associated with it. A money market that does not have a trigger contract associated with it cannot be used for protected borrowing, but can be used for ordinary borrowing. If the value of the trigger property is anything except the zero address, the value of the trigger property represents the address of the trigger contract associated with that market. Only Cozy markets that have a trigger contract can be used for protected borrowing.

If you want to provide assets to a market for protected borrowing and to earn interest from the borrowers participating in that market, you need to verify that the market is a supported protection market. To see how to do that, let's assume the following:

  • You have 1000 USDC that you want to deposit.

  • You want to use that asset to provide protection to a Yearn Protection Market for yUSDC.

The following code snippet illustrates how you can verify that the Yearn market is part of the Cozy protocol.

provide-protection.ts
// STEP 0: ENVIRONMENT SETUP
const supplyAmount = '1000'; // Amount of USDC we want to supply, in dollars (e.g. 1000 = $1000 = 1000 USDC)

// Since we are testing on a forked mainnet and our account has no
// funds, we need to initialize the account with the required tokens.
// This step is not needed when the private key in your .env file has
// funds on mainnet
const usdcAddress = getContractAddress('USDC', chainId);
await fundAccount(usdcAddress, supplyAmount, signer.address, hre);

// If the account also needed ETH, you could fund it as shown here
const ethAddress = getContractAddress('ETH', chainId);
await fundAccount(ethAddress, '10', signer.address, hre);

// STEP 1: VERIFY MARKET
// We know we'll need the Comptroller, so create an instance the Comptroller contract
const comptrollerAddress = getContractAddress('Comptroller', chainId);
const comptroller = new Contract(comptrollerAddress, comptrollerAbi, signer); // connect signer for sending transactions

// The first check is to make sure our protection market is a valid
// protection market that we can supply to
const yearnProtectionMarketAddress = getContractAddress('YearnProtectionMarket', chainId);
const allMarkets = await comptroller.getAllMarkets();
if (!allMarkets.includes(yearnProtectionMarketAddress)) {
  logFailure("Provided Protection Market address not found in the Comptroller's list of all markets");
  return;
}
logSuccess('Provided Protection Market address is valid');

After verifying the market to which you plan to supply assets is a valid protection market according to the Cozy protocol's Comptroller, you are ready to supply assets to the market. In this example, you are supplying 1000 USDC and USDC has 6 decimal places, so your script needs to account for that.

The following code and comments describe how to adjust the units to 6 decimal places, approve the protection market to spend your USDC, then supply funds to the protection market.

provide-protection.ts
// STEP 2: PROVIDE PROTECTION
// We're now ready to supply collateral (i.e. provide protection) to
// the market, but there's some preparation we need to do beforehand.
// First, recall that USDC has 6 decimal places, so we need to take
// that into account. We'll do this programmatically by querying the
// USDC contract for the number of decimals it has
const usdc = new Contract(usdcAddress, erc20Abi, signer);
const decimals = await usdc.decimals();
const parsedSupplyAmount = parseUnits(supplyAmount, decimals); // scale amount based on number of decimals

// Next we need to approve the protection market contract to spend
// our USDC. We trust the contract, so approve it to spend the
// maximum possible amount to avoid future approvals and save gas
const approveTx = await usdc.approve(yearnProtectionMarketAddress, MaxUint256);
await approveTx.wait();

// Let's verify this approve transaction was successful
const allowance = await usdc.allowance(signer.address, yearnProtectionMarketAddress);
if (!allowance.eq(MaxUint256)) {
  logFailure('CozyUSDC does not have sufficient allowance to spend our USDC. Exiting script');
  return;
}
logSuccess('Approval transaction successful. Ready to mint CozyUSDC with our USDC');

// Now we can supply funds to the protection market to provide
// protection. Just like with ordinary Money Markets, this mints a
// receipt token that is sent to our wallet
const yearnProtectionMarket = new Contract(yearnProtectionMarketAddress, cozyTokenAbi, signer);
const mintTx = await yearnProtectionMarket.mint(parsedSupplyAmount);
const { log: mintLog, receipt: mintReceipt } = await findLog(mintTx, yearnProtectionMarket, 'Mint', provider);
const yearnProtectionMarketSymbol = await yearnProtectionMarket.symbol();
const yearnProtectionMarketName = await yearnProtectionMarket.name();
logSuccess(
  `${yearnProtectionMarketName} (${yearnProtectionMarketSymbol}) successfully minted in transaction ${mintReceipt.transactionHash}`
);

Verify transaction results

In some cases, a mintTx transaction can appear to be successful without the mint operation actually being successful.

Because of how Cozy does error handling, transactions can be successful—and be displayed as successful on Etherscan and other block explorers—but without doing what you expected. For example, if a transaction returns an error code and emits a Failure event instead of reverting, it might appear as if the transaction was successful when it has actually failed.

You can read more about error codes and failures in Error codes, and see information about error handling history here.

You should manually ensure the mint transaction succeeded before continuing to the next step. You can use the findLog() helper method to simplify the verification process. For more details about using the findLog() method, see the utils.ts file in the Cozy developer Guides repository.

After you manually verify that your transaction was successful, your 1000 USDC are used to provide protection in the Yearn yUSDC protection market.

Last updated