import { Address } from "@celo/utils/lib/address"; import { Multicall } from "@node-fi/multicall"; import BigNumber from "bignumber.js"; import invariant from "tiny-invariant"; import Web3 from "web3"; import { TransactionReceipt } from "web3-core"; import { NodeRoute } from "../Routing"; import { Token, TokenAmount } from "../Tokens"; import { BigNumberString, ChainId, CHAINS_TO_RPC } from "../constants"; import { Wallet } from "../wallet"; type EarnType = "SNX" | "LG3" | "Aave-V2"; export interface Reward { _type: "VeToken" | "SNX"; order: number; contract: string; rewardToken: string; totalRewardRate?: string; } export interface EarnBase { _type: EarnType; eid: number; name: string; protocol: string; apr: number; tvl: number; depositToken: string; chainId: ChainId; totalSupply: string; } export interface YieldFarmDetails extends EarnBase { address: string; rewards: Reward[]; } export interface DepositData { addr: string; extra: string; } export interface LendingMarketDetails extends EarnBase { debtToken: string; } export interface DepositTransactionDetails { expectedOut?: BigNumber; routerAddress?: string; routes?: NodeRoute[]; depositData?: DepositData; oneClickFarmAddress?: Address; error?: string; } export abstract class EarnPosition { protected web3: Web3; private amountDeposited?: TokenAmount; private depositToken: Token; private totalSupply: TokenAmount; constructor(protected base: EarnBase, private wallet?: Wallet) { this.web3 = new Web3( new Web3.providers.HttpProvider(CHAINS_TO_RPC[base.chainId].http) ); this.depositToken = new Token(base.chainId, base.depositToken, 18); this.totalSupply = new TokenAmount(this.depositToken, base.totalSupply); } get id() { return this.base.eid; } get tvl() { return this.base.tvl; } get protocol() { return this.base.protocol; } get _type() { return this.base._type; } get apr() { return this.base.apr; } get supply() { return this.totalSupply; } get deposited() { return this.amountDeposited; } setAmountDeposited(amount: string | BigNumber) { this.amountDeposited = new TokenAmount(this.depositToken, amount); } abstract balanceOfMulticall(owner: string): Multicall; abstract getExpectedDeposit( tokenIn: Address, amountIn: BigNumberString ): Promise<{ expectedOut?: BigNumber; routerAddress?: string; routes?: NodeRoute[]; depositData?: DepositData; oneClickFarmAddress?: Address; error?: string; }>; abstract withdrawToUnderlying(amount: BigNumber): Promise<{ directOutput?: { [address: Address]: BigNumber }; error?: string; }>; abstract withdrawToSelectedToken( amountToWithdraw: BigNumber, tokenOut: Address ): Promise<{ directOutput?: { [address: Address]: BigNumber }; expectedOut?: BigNumber | undefined; routerAddress?: string | undefined; underlyingRoutes?: NodeRoute[] | undefined; error?: string | undefined; }>; public async performDeposit( details: DepositTransactionDetails, to: Address, slippage: number, wallet?: Wallet ): Promise { invariant(!!wallet, "Wallet required!"); const payload = this.formatTransaction(details, to, slippage); invariant(!!payload, "Ill-formated deposit!"); const receipt = await wallet.signAndSendTransaction([payload]); return receipt; } abstract formatTransaction( details: DepositTransactionDetails, to: Address, slippage: number ): { to: string; data: string; gas: string } | undefined; abstract canDirectlySwap(): boolean; }