Skip to content

snxgrants/flashburn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FlashBurn - Burn sUSD Debt with Staked SNX

CI

This monorepo contains the smart contracts and interface for FlashBurn.

Description

FlashBurn allows you to burn your sUSD debt using staked SNX. Stakers who are low on liquidity or unable to acquire sUSD can use this tool to sell off their SNX and pay off their sUSD debt in 1 transaction.

The smart contract works by taking a sUSD flash loan from Aave V2 to burn a specified amount of the users sUSD debt. In doing so, the users SNX unstakes and becomes transferrable. The contract transfers the SNX from the user, then sells it on an approved DEX (e.g. 1inch) for sUSD to pay back the flash loan.

Contract Addresses

Latest SNXFlashLoanTool deployment at tag v1.0.8:

Network Explorer
Mainnet 0x96D2d1e49aF59b6bBD179713b99aB1D3e6410D16
Kovan 0x231e7959852509E4872C3374554784a46EB8d680

Installation

The contracts package can be installed via npm (npm link):

# Yarn
yarn add @snx-flash-tool/contracts
# npm
npm install @snx-flash-tool/contracts

Solidity

You can import the Solidity contracts from @snx-flash-tool/contracts/contracts and the interfaces from @snx-flash-tool/contracts/contracts/interfaces:

import { SNXFlashLoanTool } from "@snx-flash-tool/contracts/contracts/SNXFlashLoanTool.sol";
import { ISNXFlashLoanTool } from "@snx-flash-tool/contracts/contracts/interfaces/ISNXFlashLoanTool.sol";

JavaScript

You can import the contract addresses from @snx-flash-tool/contracts/constants and the Typechain contract interfaces from @snx-flash-tool/contracts/types:

import { addresses } from "@snx-flash-tool/contracts/constants";
import {
  SNXFlashLoanTool,
  SNXFlashLoanTool__factory,
  ISNXFlashLoanTool,
  ISNXFlashLoanTool__factory,
} from "@snx-flash-tool/contracts/types";
const chainId = 1;
const address = addresses[chainId].snxFlashTool;
// JavaScript
const snxFlashLoanTool = SNXFlashLoanTool__factory.connect(
  address,
  provider /* ethers.js provider */
);
// TypeScript
const snxFlashLoanTool: SNXFlashLoanTool = SNXFlashLoanTool__factory.connect(
  address,
  provider /* ethers.js provider */
);

You can import the contract artifacts from @snx-flash-tool/contracts/artifacts/contracts:

import SNXFlashLoanTool from "@snx-flash-tool/contracts/artifacts/contracts/SNXFlashLoanTool.sol/SNXFlashLoanTool.json";
const abi = SNXFlashLoanTool.abi;
const bytecode = SNXFlashLoanTool.bytecode;

Documentation

SNXFlashLoanTool

The burn function allows SNX to be swapped for sUSD on an approved DEX. The mainnet deployment has approved the 1inch AggregationRouterV3 contract address. A swap is done by passing the exchangeData (calldata to call contract with, for swap) parameter to the burn function. Before calling the burn function you can fetch the swap data from the 1inch API: https://api.1inch.exchange/v3.0/1/swap?fromTokenAddress=0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F&toTokenAddress=0x57Ab1ec28D129707052df4dF418D58a2D46d5f51&amount=11980809705297140381&disableEstimate=true&fromAddress=0x96D2d1e49aF59b6bBD179713b99aB1D3e6410D16&slippage=1 (swap 11.9 SNX to sUSD with a slippage of 1%). This will return an object data containing the swap data. You can then call burn with data.tx.data for exchangeData.

To burn all sUSD debt, call burn with the sUSDAmount parameter set to the maximum value representable by the uint256 type. In Solidity this is type(uint256).max, in ethers.js this is ethers.constants.MaxUint256.

The caller of the burn function must approve the contract to burn sUSD on the callers behalf. This can be done by calling approveBurnOnBehalf on the DelegateApprovals contract. The caller must also approve snxAmount of SNX to be spent by the contract. Both of these must be done before calling the contract.

  • Solidity
    IDelegateApprovals(delegateApprovals).approveBurnOnBehalf(snxFlashLoanTool);
    IERC20(snx).approve(snxFlashLoanTool, snxAmount);
    // If burning specified amount of sUSD debt
    ISNXFlashLoanTool(snxFlashLoanTool).burn(sUSDAmount, snxAmount, exchangeData);
    // If burning all sUSD debt
    ISNXFlashLoanTool(snxFlashLoanTool).burn(type(uint256).max, snxAmount, exchangeData);
  • JavaScript
    const data = await fetch(
      `https://api.1inch.exchange/v3.0/1/swap?fromTokenAddress=0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F&toTokenAddress=0x57Ab1ec28D129707052df4dF418D58a2D46d5f51&amount=${snxAmount.toString()}&disableEstimate=true&fromAddress=${
        snxFlashLoanTool.address
      }&slippage=${slippage}`
    ).then((r) => r.json());
    await delegateApprovals.approveBurnOnBehalf(snxFlashLoanTool.address);
    await snx.approve(snxFlashLoanTool.address, snxAmount);
    // If burning specified amount of sUSD debt
    await snxFlashLoanTool.burn(sUSDAmount, snxAmount, data.tx.to, data.tx.data);
    // If burning all sUSD debt
    await snxFlashLoanTool.burn(
      ethers.constants.MaxUint256,
      snxAmount,
      data.tx.data
    );

Note that the fee to flash loan on Aave V2 is 0.09%, so you must specify an snxAmount high enough to swap to sUSDAmount * 1.0009.

Development

Directory Structure

  • packages: Contains all the typescript packages and contracts
    • contracts: Solidity smart contracts for the tool
    • interface: Web interface for interacting with the contracts

Dependencies

Environment variables

The environment variable ALCHEMY_API_KEY must be set to an Alchemy mainnet key before development. For example:

export ALCHEMY_API_KEY=En1...

Optionally, to enable non-mainnet deployments of the contracts and Infura support on the interface, set INFURA_ID and NEXT_PUBLIC_INFURA_ID (e.g. da68...). INFURA_ID is for the contracts, NEXT_PUBLIC_INFURA_ID is for the interface. INFURA_ID and NEXT_PUBLIC_INFURA_ID can use the same value.

Optional environment variables:

  • PRIVATE_KEY Private key (with the 0x in the beginning removed) to deploy contracts
  • COINMARKETCAP CoinMarketCap API key to view gas costs in USD
  • ETHERSCAN Etherscan API key to verify deployed contracts on Etherscan
  • NEXT_PUBLIC_SITE_URL (e.g. https://flashburn.gcubed.io/) For improving HTML metadata

Setup

Clone the repository, open it, and install Node.js packages with yarn:

git clone https://github.com/snxgrants/flashburn.git
cd flashburn
yarn install

Building the TypeScript packages

To build all of the TypeScript packages, run:

yarn build

To build the contracts, run:

yarn build:contracts

To build the interface, run:

yarn build:interface

Running tests

To run unit tests for the contracts, run:

yarn test:contracts

Run the development server

To run the development server for the interface, run:

yarn dev:interface

Formatting and linting

To format all of the TypeScript packages, run:

yarn prettier

To lint all of the TypeScript packages, run:

yarn lint

Cleaning

To clean the compiled contracts, run:

yarn clean:contracts

Local deployment

A Hardhat node must be started before deploying locally:

yarn evm:contracts

Then you can deploy the contracts:

yarn migrate:contracts --network localhost

Deployment

Before deploying contracts to mainnet you must set the ALCHEMY_API_KEY and PRIVATE_KEY environment variable. To deploy on other networks you must set an INFURA_ID and replace mainnet with the network name:

yarn migrate:contracts --network mainnet

To deploy the interface you must use yarn build as the build command, and the output directory will be packages/interface/.next. The environment variable NEXT_PUBLIC_INFURA_ID can be set for enabling WalletConnect support, and NEXT_PUBLIC_SITE_URL can be set for improving metadata in the HTML head.