FlashBurn - Burn sUSD Debt with Staked SNX
This monorepo contains the smart contracts and interface for FlashBurn.
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.
Latest SNXFlashLoanTool
deployment at tag v1.0.8:
Network | Explorer |
---|---|
Mainnet | 0x96D2d1e49aF59b6bBD179713b99aB1D3e6410D16 |
Kovan | 0x231e7959852509E4872C3374554784a46EB8d680 |
The contracts package can be installed via npm
(npm link):
# Yarn
yarn add @snx-flash-tool/contracts
# npm
npm install @snx-flash-tool/contracts
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";
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;
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
.
packages
: Contains all the typescript packages and contracts
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 the0x
in the beginning removed) to deploy contractsCOINMARKETCAP
CoinMarketCap API key to view gas costs in USDETHERSCAN
Etherscan API key to verify deployed contracts on EtherscanNEXT_PUBLIC_SITE_URL
(e.g.https://flashburn.gcubed.io/
) For improving HTML metadata
Clone the repository, open it, and install Node.js packages with yarn
:
git clone https://github.com/snxgrants/flashburn.git
cd flashburn
yarn install
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
To run unit tests for the contracts, run:
yarn test:contracts
To run the development server for the interface, run:
yarn dev:interface
To format all of the TypeScript packages, run:
yarn prettier
To lint all of the TypeScript packages, run:
yarn lint
To clean the compiled contracts, run:
yarn clean:contracts
A Hardhat node must be started before deploying locally:
yarn evm:contracts
Then you can deploy the contracts:
yarn migrate:contracts --network localhost
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.