ethers/lib/utils#id TypeScript Examples

The following examples show how to use ethers/lib/utils#id. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: fixtures.ts    From fyDai with GNU General Public License v3.0 6 votes vote down vote up
public static async setupController(treasury: Contract, fyDais: Array<Contract>) {
    const fyDaiAddrs = fyDais.map((c) => c.address)
    const controller = await Controller.new(treasury.address, fyDaiAddrs)
    const treasuryFunctions = ['pushDai', 'pullDai', 'pushChai', 'pullChai', 'pushWeth', 'pullWeth'].map((func) =>
      id(func + '(address,uint256)')
    )
    await treasury.batchOrchestrate(controller.address, treasuryFunctions)

    for (const fyDai of fyDais) {
      await fyDai.batchOrchestrate(controller.address, [id('mint(address,uint256)'), id('burn(address,uint256)')])
    }

    return controller
  }
Example #2
Source File: fixtures.ts    From fyDai with GNU General Public License v3.0 6 votes vote down vote up
public static async setupFYDais(treasury: Contract, maturities: Array<number>): Promise<Array<Contract>> {
    return await Promise.all(
      maturities.map(async (maturity) => {
        const fyDai = await FYDai.new(treasury.address, maturity, 'Name', 'Symbol')
        await treasury.orchestrate(fyDai.address, id('pullDai(address,uint256)'))
        return fyDai
      })
    )
  }
Example #3
Source File: fixtures.ts    From fyDai with GNU General Public License v3.0 6 votes vote down vote up
public static async setup(maturities: Array<number>) {
    const { maker, treasury, controller, fyDais } = await YieldEnvironmentLite.setup(maturities)

    const liquidations = await Liquidations.new(controller.address)
    await controller.orchestrate(liquidations.address, id('erase(bytes32,address)'))
    await treasury.batchOrchestrate(liquidations.address, [
      id('pushDai(address,uint256)'),
      id('pullWeth(address,uint256)'),
    ])

    const unwind = await Unwind.new(maker.end.address, liquidations.address)
    await treasury.registerUnwind(unwind.address)
    await controller.orchestrate(unwind.address, id('erase(bytes32,address)'))
    await liquidations.orchestrate(unwind.address, id('erase(address)'))

    for (const fyDai of fyDais) {
      await fyDai.orchestrate(unwind.address, id('burn(address,uint256)'))
    }

    return new YieldEnvironment(maker, treasury, controller, fyDais, liquidations, unwind)
  }
Example #4
Source File: fixtures.ts    From fyDai with GNU General Public License v3.0 5 votes vote down vote up
public async newFYDai(maturity: number, name: string, symbol: string) {
    const fyDai = await FYDai.new(this.treasury.address, maturity, name, symbol)
    await this.treasury.orchestrate(fyDai.address, id('pullDai(address,uint256)'))
    return fyDai
  }
Example #5
Source File: 111_treasury_lending.ts    From fyDai with GNU General Public License v3.0 4 votes vote down vote up
contract('Treasury - Lending', async (accounts: string[]) => {
  let [owner, user] = accounts

  let treasury: Contract
  let vat: Contract
  let weth: Contract
  let wethJoin: Contract
  let chai: Contract
  let dai: Contract

  beforeEach(async () => {
    const maker = await MakerEnvironment.setup()
    treasury = await YieldEnvironment.setupTreasury(maker)
    vat = maker.vat
    weth = maker.weth
    wethJoin = maker.wethJoin
    chai = maker.chai
    dai = maker.dai

    // Setup tests - Allow owner to interact directly with Treasury, not for production
    const treasuryFunctions = ['pushDai', 'pullDai', 'pushChai', 'pullChai', 'pushWeth', 'pullWeth'].map((func) =>
      id(func + '(address,uint256)')
    )
    await treasury.batchOrchestrate(owner, treasuryFunctions)
  })

  it('get the size of the contract', async () => {
    console.log()
    console.log('    ·--------------------|------------------|------------------|------------------·')
    console.log('    |  Contract          ·  Bytecode        ·  Deployed        ·  Constructor     |')
    console.log('    ·····················|··················|··················|···················')

    const bytecode = treasury.constructor._json.bytecode
    const deployed = treasury.constructor._json.deployedBytecode
    const sizeOfB = bytecode.length / 2
    const sizeOfD = deployed.length / 2
    const sizeOfC = sizeOfB - sizeOfD
    console.log(
      '    |  ' +
        treasury.constructor._json.contractName.padEnd(18, ' ') +
        '|' +
        ('' + sizeOfB).padStart(16, ' ') +
        '  ' +
        '|' +
        ('' + sizeOfD).padStart(16, ' ') +
        '  ' +
        '|' +
        ('' + sizeOfC).padStart(16, ' ') +
        '  |'
    )
    console.log('    ·--------------------|------------------|------------------|------------------·')
    console.log()
  })

  it('should fail for failed weth transfers', async () => {
    // Let's check how WETH is implemented, maybe we can remove this one.
  })

  it('allows to post collateral', async () => {
    assert.equal(await weth.balanceOf(wethJoin.address), web3.utils.toWei('0'))

    await weth.deposit({ from: owner, value: wethTokens1 })
    await weth.approve(treasury.address, wethTokens1, { from: owner })
    await treasury.pushWeth(owner, wethTokens1, { from: owner })

    // Test transfer of collateral
    assert.equal(await weth.balanceOf(wethJoin.address), wethTokens1)

    // Test collateral registering via `frob`
    assert.equal((await vat.urns(WETH, treasury.address)).ink, wethTokens1)
  })

  describe('with posted collateral', () => {
    beforeEach(async () => {
      await weth.deposit({ from: owner, value: wethTokens1 })
      await weth.approve(treasury.address, wethTokens1, { from: owner })
      await treasury.pushWeth(owner, wethTokens1, { from: owner })

      // Add some funds to the system to allow for rounding losses
      await weth.deposit({ from: owner, value: 1000 })
      await weth.approve(treasury.address, 2, { from: owner })
      await treasury.pushWeth(owner, 2, { from: owner })
    })

    it('allows to withdraw collateral for user', async () => {
      assert.equal(await weth.balanceOf(user), 0)
      const ink = (await vat.urns(WETH, treasury.address)).ink.toString()

      await treasury.pullWeth(user, ink, { from: owner })

      // Test transfer of collateral
      assert.equal(await weth.balanceOf(user), ink)

      // Test collateral registering via `frob`
      assert.equal((await vat.urns(WETH, treasury.address)).ink, 0)
    })

    it('pulls dai borrowed from MakerDAO for user', async () => {
      await treasury.pullDai(user, daiTokens1, { from: owner })

      assert.equal(await dai.balanceOf(user), daiTokens1)
      assert.equal((await vat.urns(WETH, treasury.address)).art, daiDebt1)
    })

    it('pulls chai converted from dai borrowed from MakerDAO for user', async () => {
      // Test with two different stability rates, if possible.
      await treasury.pullChai(user, chaiTokens1, { from: owner })

      assert.equal(await chai.balanceOf(user), chaiTokens1)
      assert.equal((await vat.urns(WETH, treasury.address)).art, daiDebt1)
    })

    it("shouldn't allow borrowing beyond power", async () => {
      const ink = (await vat.urns(WETH, treasury.address)).ink.toString()
      const toBorrow = subBN(mulRay(ink, spot), 10).toString() // Rounding means that ink * spot is a few wei (2) above what we can actually borrow
      await treasury.pullDai(user, toBorrow, { from: owner })
      almostEqual(await treasury.debt(), toBorrow, precision)
      await expectRevert(treasury.pullDai(user, 10, { from: owner }), 'Vat/not-safe')
    })

    describe('with a dai debt towards MakerDAO', () => {
      beforeEach(async () => {
        await treasury.pullDai(user, daiTokens1, { from: owner })
      })

      it('returns treasury debt', async () => {
        assert.equal(await treasury.debt(), daiTokens1, 'Should return borrowed dai')
      })

      it('pushes dai that repays debt towards MakerDAO', async () => {
        // Test `normalizedAmount >= normalizedDebt`
        await dai.approve(treasury.address, daiTokens1, { from: user })
        await treasury.pushDai(user, daiTokens1, { from: owner })

        assert.equal(await dai.balanceOf(user), 0)
        almostEqual((await vat.urns(WETH, treasury.address)).art, 0, precision)
      })

      it('pushes chai that repays debt towards MakerDAO', async () => {
        await dai.approve(chai.address, daiTokens1, { from: user })
        await chai.join(user, daiTokens1, { from: user })
        await chai.approve(treasury.address, chaiTokens1, { from: user })
        await treasury.pushChai(user, chaiTokens1, { from: owner })

        assert.equal(await dai.balanceOf(user), 0)
        almostEqual((await vat.urns(WETH, treasury.address)).art, 0, precision)
      })
    })
  })
})
Example #6
Source File: 112_treasury_saving.ts    From fyDai with GNU General Public License v3.0 4 votes vote down vote up
contract('Treasury - Saving', async (accounts) => {
  let [owner, user] = accounts

  let treasury: Contract
  let chai: Contract
  let dai: Contract

  beforeEach(async () => {
    const maker = await MakerEnvironment.setup()
    treasury = await YieldEnvironment.setupTreasury(maker)
    chai = maker.chai
    dai = maker.dai

    // Setup tests - Allow owner to interact directly with Treasury, not for production
    const treasuryFunctions = ['pushDai', 'pullDai', 'pushChai', 'pullChai', 'pushWeth', 'pullWeth'].map((func) =>
      id(func + '(address,uint256)')
    )
    await treasury.batchOrchestrate(owner, treasuryFunctions)

    // Borrow some dai
    await maker.getDai(user, daiTokens1, rate1)
  })

  it('allows to save dai', async () => {
    assert.equal(await chai.balanceOf(treasury.address), 0, 'Treasury has chai')
    assert.equal(await treasury.savings(), 0, 'Treasury has savings in dai units')
    assert.equal(await dai.balanceOf(user), daiTokens1, 'User does not have dai')

    await dai.approve(treasury.address, daiTokens1, { from: user })
    await treasury.pushDai(user, daiTokens1, { from: owner })

    // Test transfer of collateral
    assert.equal(await chai.balanceOf(treasury.address), chaiTokens1, 'Treasury should have chai')
    almostEqual(await treasury.savings(), daiTokens1, precision)
    assert.equal(await dai.balanceOf(user), 0, 'User should not have dai')
  })

  it('allows to save chai', async () => {
    assert.equal(await chai.balanceOf(treasury.address), 0, 'Treasury has chai')
    assert.equal(await treasury.savings(), 0, 'Treasury has savings in dai units')
    assert.equal(await dai.balanceOf(user), daiTokens1, 'User does not have dai')

    await dai.approve(chai.address, daiTokens1, { from: user })
    await chai.join(user, daiTokens1, { from: user })
    await chai.approve(treasury.address, chaiTokens1, { from: user })
    await treasury.pushChai(user, chaiTokens1, { from: owner })

    // Test transfer of collateral
    assert.equal(await chai.balanceOf(treasury.address), chaiTokens1, 'Treasury should have chai')
    almostEqual(await treasury.savings(), daiTokens1, precision)
    assert.equal(await chai.balanceOf(user), 0, 'User should not have chai')
  })

  describe('with savings', () => {
    beforeEach(async () => {
      await dai.approve(treasury.address, daiTokens1, { from: user })
      await treasury.pushDai(user, daiTokens1, { from: owner })
    })

    it('pulls dai from savings', async () => {
      assert.equal(await chai.balanceOf(treasury.address), chaiTokens1, 'Treasury does not have chai')
      almostEqual(await treasury.savings(), daiTokens1, precision)
      assert.equal(await dai.balanceOf(user), 0, 'User has dai')

      const toPull = await treasury.savings()
      await treasury.pullDai(user, toPull, { from: owner })

      assert.equal(await chai.balanceOf(treasury.address), 0, 'Treasury should not have chai')
      almostEqual(await treasury.savings(), 0, precision)
      assert.equal(await dai.balanceOf(user), toPull.toString(), 'User should have dai')
    })

    it('pulls chai from savings', async () => {
      assert.equal(await chai.balanceOf(treasury.address), chaiTokens1, 'Treasury does not have chai')
      almostEqual(await treasury.savings(), daiTokens1, precision)
      assert.equal(await dai.balanceOf(user), 0, 'User has dai')

      const toPull = divRay((await treasury.savings()).toString(), chi1)
      await treasury.pullChai(user, toPull, { from: owner })

      almostEqual(await chai.balanceOf(treasury.address), 0, precision)
      almostEqual(await treasury.savings(), 0, precision)
      assert.equal(await chai.balanceOf(user), toPull.toString(), 'User should have chai')
    })
  })
})
Example #7
Source File: 121_fyDai.ts    From fyDai with GNU General Public License v3.0 4 votes vote down vote up
contract('fyDai', async (accounts) => {
  let [owner, user1, user2] = accounts

  // const rate2 = toRay(1.82)
  const chi2 = toRay(1.5)
  const chiDifferential = divRay(chi2, chi1)
  const daiTokens2 = mulRay(daiTokens1, chiDifferential)
  const wethTokens2 = mulRay(wethTokens1, chiDifferential)

  let maturity: number
  let snapshot: any
  let snapshotId: string

  let treasury: Contract
  let vat: Contract
  let weth: Contract
  let pot: Contract
  let dai: Contract
  let fyDai1: Contract
  let flashMinter: Contract
  let env: YieldEnvironmentLite
  let flashMintRedeemer: Contract

  beforeEach(async () => {
    snapshot = await helper.takeSnapshot()
    snapshotId = snapshot['result']

    const block = await web3.eth.getBlockNumber()
    maturity = (await web3.eth.getBlock(block)).timestamp + 1000

    env = await YieldEnvironmentLite.setup([maturity])
    treasury = env.treasury
    weth = env.maker.weth
    pot = env.maker.pot
    vat = env.maker.vat
    dai = env.maker.dai

    fyDai1 = env.fyDais[0]

    // Test setup
    // Setup Flash Minter
    flashMinter = await FlashMinterMock.new({ from: owner })

    flashMintRedeemer = await FlashMintRedeemerMock.new({ from: owner })

    // Deposit some weth to treasury the sneaky way so that redeem can pull some dai
    await treasury.orchestrate(owner, id('pushWeth(address,uint256)'), { from: owner })
    await weth.deposit({ from: owner, value: wethTokens2.mul(2).toString() })
    await weth.approve(treasury.address, wethTokens2.mul(2), { from: owner })
    await treasury.pushWeth(owner, wethTokens2.mul(2), { from: owner })

    // Mint some fyDai1 the sneaky way, only difference is that the Controller doesn't record the user debt.
    await fyDai1.orchestrate(owner, id('mint(address,uint256)'), { from: owner })
    await fyDai1.mint(user1, daiTokens1, { from: owner })
  })

  afterEach(async () => {
    await helper.revertToSnapshot(snapshotId)
  })

  it('should setup fyDai1', async () => {
    assert.equal(await fyDai1.chiGrowth(), toRay(1.0).toString(), 'chi not initialized')
    assert.equal(await fyDai1.rateGrowth(), toRay(1.0).toString(), 'rate not initialized')
    assert.equal(await fyDai1.maturity(), maturity.toString(), 'maturity not initialized')
  })

  it('should fail to set up fyDai with an invalid maturity date', async () => {
    const block = await web3.eth.getBlockNumber()
    const timestamp = (await web3.eth.getBlock(block)).timestamp
    const earlyMaturity = timestamp - 1000
    const lateMaturity = timestamp + 126144000 + 15

    await expectRevert(env.newFYDai(earlyMaturity, 'Name', 'Symbol'), 'FYDai: Invalid maturity')

    await expectRevert(env.newFYDai(lateMaturity, 'Name', 'Symbol'), 'FYDai: Invalid maturity')
  })

  it('fyDai1 is not mature before maturity', async () => {
    assert.equal(await fyDai1.isMature(), false)
  })

  it("fyDai1 can't be redeemed before maturity time", async () => {
    await expectRevert(fyDai1.redeem(user1, user2, daiTokens1, { from: user1 }), 'FYDai: fyDai is not mature')
  })

  it('fyDai1 cannot mature before maturity time', async () => {
    await expectRevert(fyDai1.mature(), 'FYDai: Too early to mature')
  })

  it('fyDai1 can mature at maturity time', async () => {
    await helper.advanceTime(1000)
    await helper.advanceBlock()
    await fyDai1.mature()
    assert.equal(await fyDai1.isMature(), true)
  })

  it('fyDai1 flash mints', async () => {
    const fyDaiSupply = await fyDai1.totalSupply()
    expectEvent(
      await flashMinter.flashMint(fyDai1.address, daiTokens1, web3.utils.fromAscii('DATA'), { from: user1 }),
      'Parameters',
      {
        amount: daiTokens1,
        data: web3.utils.fromAscii('DATA'),
      }
    )

    assert.equal(await flashMinter.flashBalance(), daiTokens1, 'FlashMinter should have seen the tokens')
    assert.equal(await fyDai1.totalSupply(), fyDaiSupply.toString(), 'There should be no change in fyDai supply')
  })

  it("fyDai1 can't reach more than 2**112 supply on flash mint", async () => {
    const halfLimit = new BN('2').pow(new BN('111'))
    await fyDai1.mint(user1, halfLimit, { from: owner })
    await expectRevert(
      flashMinter.flashMint(fyDai1.address, halfLimit, web3.utils.fromAscii('DATA'), { from: user1 }),
      'FYDai: Total supply limit exceeded'
    )
  })

  describe('once mature', () => {
    beforeEach(async () => {
      await helper.advanceTime(1000)
      await helper.advanceBlock()
      await fyDai1.mature()
    })

    it("fyDai1 can't mature more than once", async () => {
      await expectRevert(fyDai1.mature(), 'FYDai: Already mature')
    })

    it('fyDai1 chi gets fixed at maturity time', async () => {
      await pot.setChi(chi2, { from: owner })

      assert.equal((await fyDai1.chi0()).toString(), chi1.toString(), 'Chi at maturity should be ' + chi1)
    })

    it('fyDai1 still flash mints', async () => {
      const fyDaiSupply = await fyDai1.totalSupply()
      expectEvent(
        await flashMinter.flashMint(fyDai1.address, daiTokens1, web3.utils.fromAscii('DATA'), { from: user1 }),
        'Parameters',
        {
          amount: daiTokens1,
          data: web3.utils.fromAscii('DATA'),
        }
      )

      assert.equal(await flashMinter.flashBalance(), daiTokens1, 'FlashMinter should have seen the tokens')
      assert.equal(await fyDai1.totalSupply(), fyDaiSupply.toString(), 'There should be no change in fyDai supply')
    })

    it('fyDai1 cannot redeem during flash mint', async () => {
      const fyDaiSupply = await fyDai1.totalSupply()
      await expectRevert(
        flashMintRedeemer.flashMint(fyDai1.address, daiTokens1, web3.utils.fromAscii('DATA'), { from: user1 }),
        'FYDai: Locked'
      )
    })

    it('fyDai1 rate gets fixed at maturity time', async () => {
      const rate2 = toRay(1.82)
      await vat.fold(WETH, vat.address, subBN(rate2, rate1), { from: owner })

      assert.equal((await fyDai1.rate0()).toString(), rate1.toString(), 'Rate at maturity should be ' + rate1)
    })

    it('rateGrowth returns the rate differential between now and maturity', async () => {
      const rate2 = toRay(1.82)
      await vat.fold(WETH, vat.address, subBN(rate2, rate1), { from: owner })

      assert.equal(
        (await fyDai1.rateGrowth()).toString(),
        divrupRay(rate2, rate1).toString(),
        'Rate differential should be ' + divrupRay(rate2, rate1)
      )
    })

    it('chiGrowth always <= rateGrowth', async () => {
      await pot.setChi(chi2, { from: owner })

      assert.equal(
        (await fyDai1.chiGrowth()).toString(),
        (await fyDai1.rateGrowth()).toString(),
        'Chi differential should be ' + (await fyDai1.rateGrowth()) + ', instead is ' + (await fyDai1.chiGrowth())
      )
    })

    it('chiGrowth returns the chi differential between now and maturity', async () => {
      const rate2 = mulRay(rate1, chiDifferential).add(toRay(0.1))
      await vat.fold(WETH, vat.address, subBN(rate2, rate1), { from: owner })
      await pot.setChi(chi2, { from: owner })

      assert.equal(
        (await fyDai1.chiGrowth()).toString(),
        divRay(chi2, chi1).toString(),
        'Chi differential should be ' + divRay(chi2, chi1)
      )
    })

    it('redeem burns fyDai1 to return dai, pulls dai from Treasury', async () => {
      assert.equal(await fyDai1.balanceOf(user1), daiTokens1, 'User1 does not have fyDai1')
      assert.equal(await dai.balanceOf(user2), 0, 'User2 has dai')

      await fyDai1.approve(fyDai1.address, daiTokens1, { from: user1 })
      await fyDai1.redeem(user1, user2, daiTokens1, { from: user1 })

      assert.equal(await dai.balanceOf(user2), daiTokens1, 'User2 should have dai')
      assert.equal(await fyDai1.balanceOf(user1), 0, 'User1 should not have fyDai1')
    })

    describe('once chi increases', () => {
      beforeEach(async () => {
        const rate2 = mulRay(rate1, chiDifferential).add(toRay(0.1))
        await vat.fold(WETH, vat.address, subBN(rate2, rate1), { from: owner }) // Keeping above chi
        await pot.setChi(chi2, { from: owner })

        assert.equal(
          await fyDai1.chiGrowth(),
          chiDifferential.toString(),
          'chi differential should be ' + chiDifferential + ', instead is ' + (await fyDai1.chiGrowth())
        )
      })

      it('redeem with increased chi returns more dai', async () => {
        // Redeem `daiTokens1` fyDai to obtain `daiTokens1` * `chiDifferential`

        assert.equal(await fyDai1.balanceOf(user1), daiTokens1, 'User1 does not have fyDai1')

        await fyDai1.approve(fyDai1.address, daiTokens1, { from: user1 })
        await fyDai1.redeem(user1, user1, daiTokens1, { from: user1 })

        assert.equal(
          await dai.balanceOf(user1),
          daiTokens2.toString(),
          'User1 should have ' + daiTokens2 + ' dai, instead has ' + (await dai.balanceOf(user1))
        )
        assert.equal(
          await fyDai1.balanceOf(user1),
          0,
          'User2 should have no fyDai left, instead has ' + (await fyDai1.balanceOf(user1))
        )
      })
    })
  })
})
Example #8
Source File: 122_fyDai_delegation.ts    From fyDai with GNU General Public License v3.0 4 votes vote down vote up
contract('fyDai - Delegation', async (accounts) => {
  let [owner, holder, other] = accounts

  let maturity1: number
  let maturity2: number

  let snapshot: any
  let snapshotId: string

  let treasury: Contract
  let vat: Contract
  let weth: Contract
  let dai: Contract
  let fyDai1: Contract

  beforeEach(async () => {
    snapshot = await helper.takeSnapshot()
    snapshotId = snapshot['result']

    // Setup fyDai
    const block = await web3.eth.getBlockNumber()
    maturity1 = (await web3.eth.getBlock(block)).timestamp + 1000
    maturity2 = (await web3.eth.getBlock(block)).timestamp + 2000

    const env = await YieldEnvironmentLite.setup([maturity1, maturity2])
    treasury = env.treasury
    weth = env.maker.weth
    vat = env.maker.vat
    dai = env.maker.dai

    fyDai1 = env.fyDais[0]

    // Post collateral to MakerDAO through Treasury
    await treasury.orchestrate(owner, id('pushWeth(address,uint256)'), { from: owner })
    const initialCapital = bnify(wethTokens1).add(10).toString()
    await weth.deposit({ from: owner, value: initialCapital })
    await weth.approve(treasury.address, initialCapital, { from: owner })
    await treasury.pushWeth(owner, initialCapital, { from: owner })
    assert.equal((await vat.urns(WETH, treasury.address)).ink, initialCapital.toString())

    // Mint some fyDai the sneaky way
    await fyDai1.orchestrate(owner, id('mint(address,uint256)'), { from: owner })
    await fyDai1.mint(holder, daiTokens1, { from: owner })

    // fyDai matures
    await helper.advanceTime(1000)
    await helper.advanceBlock()
    await fyDai1.mature()

    assert.equal(await fyDai1.balanceOf(holder), daiTokens1, 'Holder does not have fyDai')
    assert.equal(await treasury.savings(), 0, 'Treasury has no savings')
  })

  afterEach(async () => {
    await helper.revertToSnapshot(snapshotId)
  })

  it('redeem is allowed for account holder', async () => {
    await fyDai1.approve(fyDai1.address, daiTokens1, { from: holder })
    await fyDai1.redeem(holder, holder, daiTokens1, { from: holder })

    assert.equal(await treasury.debt(), daiTokens1, 'Treasury should have debt')
    assert.equal(await dai.balanceOf(holder), daiTokens1, 'Holder should have dai')
  })

  it('redeem is not allowed for non designated accounts', async () => {
    await fyDai1.approve(fyDai1.address, daiTokens1, { from: holder })
    await expectRevert(fyDai1.redeem(holder, holder, daiTokens1, { from: other }), 'FYDai: Only Holder Or Delegate')
  })

  it('redeem is allowed for delegates', async () => {
    await fyDai1.approve(fyDai1.address, daiTokens1, { from: holder })
    expectEvent(await fyDai1.addDelegate(other, { from: holder }), 'Delegate', {
      user: holder,
      delegate: other,
      enabled: true,
    })
    await fyDai1.redeem(holder, holder, daiTokens1, { from: other })

    assert.equal(await treasury.debt(), daiTokens1, 'Treasury should have debt')
    assert.equal(await dai.balanceOf(holder), daiTokens1, 'Holder should have dai')
  })

  describe('with delegates', async () => {
    beforeEach(async () => {
      await fyDai1.addDelegate(other, { from: holder })
    })

    it('redeem is not allowed if delegation revoked', async () => {
      expectEvent(await fyDai1.revokeDelegate(other, { from: holder }), 'Delegate', {
        user: holder,
        delegate: other,
        enabled: false,
      })

      await expectRevert(fyDai1.redeem(holder, holder, daiTokens1, { from: other }), 'FYDai: Only Holder Or Delegate')
    })

    it('cannot add delegate again or remove delegate twice', async () => {
      await expectRevert(fyDai1.addDelegate(other, { from: holder }), 'Delegable: Already delegated')

      expectEvent(await fyDai1.revokeDelegate(other, { from: holder }), 'Delegate', {
        user: holder,
        delegate: other,
        enabled: false,
      })

      await expectRevert(fyDai1.revokeDelegate(other, { from: holder }), 'Delegable: Already undelegated')
    })
  })
})
Example #9
Source File: 133_controller_chai.ts    From fyDai with GNU General Public License v3.0 4 votes vote down vote up
contract('Controller - Chai', async (accounts) => {
  let [owner, user1, user2] = accounts

  let snapshot: any
  let snapshotId: string
  let maker: MakerEnvironment
  let env: YieldEnvironmentLite

  let dai: Contract
  let vat: Contract
  let pot: Contract
  let controller: Contract
  let fyDai1: Contract
  let chai: Contract
  let treasury: Contract

  let maturity1: number
  let maturity2: number

  beforeEach(async () => {
    snapshot = await helper.takeSnapshot()
    snapshotId = snapshot['result']

    const block = await web3.eth.getBlockNumber()
    maturity1 = (await web3.eth.getBlock(block)).timestamp + 1000
    maturity2 = (await web3.eth.getBlock(block)).timestamp + 2000

    env = await YieldEnvironmentLite.setup([maturity1, maturity2])
    maker = env.maker
    controller = env.controller
    treasury = env.treasury
    pot = env.maker.pot
    vat = env.maker.vat
    dai = env.maker.dai
    chai = env.maker.chai

    fyDai1 = env.fyDais[0]

    // Tests setup
    await maker.getChai(user1, chaiTokens1, chi1, rate1)
  })

  afterEach(async () => {
    await helper.revertToSnapshot(snapshotId)
  })

  it('allows user to post chai', async () => {
    assert.equal(await chai.balanceOf(treasury.address), 0, 'Treasury has chai')
    assert.equal(await controller.powerOf(CHAI, user1), 0, 'User1 has borrowing power')

    await chai.approve(treasury.address, chaiTokens1, { from: user1 })
    await controller.post(CHAI, user1, user1, chaiTokens1, { from: user1 })

    assert.equal(await chai.balanceOf(treasury.address), chaiTokens1, 'Treasury should have chai')
    almostEqual(await controller.powerOf(CHAI, user1), daiTokens1)
  })

  describe('with posted chai', () => {
    beforeEach(async () => {
      // Add some funds to the system to allow for rounding losses
      await maker.getChai(owner, 1000, chi1, rate1)
      await chai.approve(treasury.address, 10, { from: owner })
      await controller.post(CHAI, owner, owner, 10, { from: owner })

      await chai.approve(treasury.address, chaiTokens1, { from: user1 })
      await controller.post(CHAI, user1, user1, chaiTokens1, { from: user1 })
    })

    it('allows user to withdraw chai', async () => {
      almostEqual(await controller.powerOf(CHAI, user1), daiTokens1, precision)
      assert.equal(await chai.balanceOf(user1), 0, 'User1 has collateral in hand')

      await controller.withdraw(CHAI, user1, user1, chaiTokens1, { from: user1 })

      assert.equal(await chai.balanceOf(user1), chaiTokens1, 'User1 should have collateral in hand')
      assert.equal(await controller.powerOf(CHAI, user1), 0, 'User1 should not have borrowing power')
    })

    it('allows to borrow fyDai', async () => {
      almostEqual(await controller.powerOf(CHAI, user1), daiTokens1, precision)
      almostEqual(await fyDai1.balanceOf(user1), 0, precision)
      almostEqual(await controller.debtDai(CHAI, maturity1, user1), 0, precision)

      const toBorrow = await controller.powerOf(CHAI, user1)
      await controller.borrow(CHAI, maturity1, user1, user1, toBorrow, { from: user1 })

      almostEqual(await fyDai1.balanceOf(user1), daiTokens1, precision)
      almostEqual(await controller.debtDai(CHAI, maturity1, user1), daiTokens1, precision)
    })

    it("doesn't allow to borrow fyDai beyond borrowing power", async () => {
      almostEqual(await controller.powerOf(CHAI, user1), daiTokens1, precision)
      almostEqual(await controller.debtDai(CHAI, maturity1, user1), 0, precision)

      const toBorrow = bnify(await env.unlockedOf(CHAI, user1)).add(precision)
      await expectRevert(
        controller.borrow(CHAI, maturity1, user1, user1, toBorrow, { from: user1 }),
        'Controller: Too much debt'
      )
    })

    describe('with borrowed fyDai', () => {
      beforeEach(async () => {
        const toBorrow = await env.unlockedOf(CHAI, user1)
        await controller.borrow(CHAI, maturity1, user1, user1, toBorrow, { from: user1 })
      })

      it("doesn't allow to withdraw and become undercollateralized", async () => {
        almostEqual(await controller.powerOf(CHAI, user1), daiTokens1, precision)
        almostEqual(await controller.debtDai(CHAI, maturity1, user1), daiTokens1, precision)

        await expectRevert(
          controller.borrow(CHAI, maturity1, user1, user1, chaiTokens1, { from: user1 }),
          'Controller: Too much debt'
        )
      })

      it('allows to repay fyDai', async () => {
        almostEqual(await fyDai1.balanceOf(user1), daiTokens1, precision)
        const debt = await controller.debtFYDai(CHAI, maturity1, user1)

        await fyDai1.approve(treasury.address, debt, { from: user1 })
        await controller.repayFYDai(CHAI, maturity1, user1, user1, debt, { from: user1 })

        almostEqual(await fyDai1.balanceOf(user1), 0, precision)
        assert.equal(await controller.debtDai(CHAI, maturity1, user1), 0, 'User1 should not have debt')
      })

      it('allows to repay fyDai with dai', async () => {
        // Borrow dai
        await maker.getDai(user1, daiTokens1, rate1)

        assert.equal(await dai.balanceOf(user1), daiTokens1, 'User1 does not have dai')
        const debt = await controller.debtDai(CHAI, maturity1, user1)
        almostEqual(debt.toString(), daiTokens1, precision)

        await dai.approve(treasury.address, debt, { from: user1 })
        await controller.repayDai(CHAI, maturity1, user1, user1, debt, { from: user1 })

        almostEqual(await dai.balanceOf(user1), 0, precision)
        assert.equal(await controller.debtDai(CHAI, maturity1, user1), 0, 'User1 should not have debt')
      })

      it('when dai is provided in excess for repayment, only the necessary amount is taken', async () => {
        // Mint some fyDai the sneaky way
        await fyDai1.orchestrate(owner, id('mint(address,uint256)'), { from: owner })
        await fyDai1.mint(user1, 1, { from: owner }) // 1 extra fyDai wei
        const fyDaiTokens = addBN(daiTokens1, 1) // daiTokens1 + 1 wei

        almostEqual(await fyDai1.balanceOf(user1), fyDaiTokens.toString(), precision)
        almostEqual(await controller.debtDai(CHAI, maturity1, user1), daiTokens1, precision)

        await fyDai1.approve(treasury.address, fyDaiTokens, { from: user1 })
        await controller.repayFYDai(CHAI, maturity1, user1, user1, fyDaiTokens, { from: user1 })

        assert.equal(await fyDai1.balanceOf(user1), 1, 'User1 should have fyDai left')
        assert.equal(await controller.debtDai(CHAI, maturity1, user1), 0, 'User1 should not have debt')
      })

      let chiDifferential: BigNumber
      let rate2: BigNumber
      let chi2: BigNumber

      describe('after maturity, with a chi increase', () => {
        beforeEach(async () => {
          almostEqual(await fyDai1.balanceOf(user1), daiTokens1, precision)
          almostEqual(await controller.debtDai(CHAI, maturity1, user1), daiTokens1, precision)
          // fyDai matures
          await helper.advanceTime(1000)
          await helper.advanceBlock()
          await fyDai1.mature()

          // Increase chi
          const chiIncrease = toRay(0.25)
          chiDifferential = divRay(addBN(chi1, chiIncrease), chi1)
          chi2 = chi1.add(chiIncrease)
          await pot.setChi(chi2, { from: owner })

          // Increase rate by a factor larger than chi
          rate2 = mulRay(rate1, chiDifferential).add(toRay(0.1))
          await vat.fold(WETH, vat.address, subBN(rate2, rate1), { from: owner }) // Keeping above chi
        })

        it('as chi increases after maturity, so does the debt in when measured in dai', async () => {
          almostEqual(await controller.debtDai(CHAI, maturity1, user1), mulRay(daiTokens1, chiDifferential), precision)
        })

        it("as chi increases after maturity, the debt doesn't in when measured in fyDai", async () => {
          almostEqual(await controller.debtFYDai(CHAI, maturity1, user1), daiTokens1, precision)
        })

        it('borrowing after maturity is still allowed', async () => {
          const fyDaiDebt = daiTokens1
          const increasedChai: BigNumber = mulRay(chaiTokens1, chiDifferential)
          await maker.getChai(user2, addBN(increasedChai, 1), chi2, rate2)
          await chai.approve(treasury.address, increasedChai, { from: user2 })
          await controller.post(CHAI, user2, user2, increasedChai, { from: user2 })
          await controller.borrow(CHAI, maturity1, user2, user2, fyDaiDebt, { from: user2 })

          assert.equal(
            await controller.debtFYDai(CHAI, maturity1, user2),
            fyDaiDebt.toString(),
            'User2 should have ' +
              fyDaiDebt +
              ' fyDai debt, instead has ' +
              (await controller.debtFYDai(CHAI, maturity1, user2))
          )
          almostEqual(await controller.debtDai(CHAI, maturity1, user2), mulRay(daiTokens1, chiDifferential))
        })
      })
    })
  })
})
Example #10
Source File: 151_unwind_treasury.ts    From fyDai with GNU General Public License v3.0 4 votes vote down vote up
contract('Unwind - Treasury', async (accounts) => {
  let [owner, user] = accounts

  let snapshot: any
  let snapshotId: string

  let env: YieldEnvironment

  let dai: Contract
  let vat: Contract
  let controller: Contract
  let treasury: Contract
  let weth: Contract
  let liquidations: Contract
  let unwind: Contract
  let end: Contract
  let chai: Contract

  let maturity1: number
  let maturity2: number

  const tag = divRay(toRay(0.9), spot)
  const treasuryDebt = mulRay(wethTokens1, spot).sub(1000) // No need to max out the debt of the Treasury to the last wei
  const taggedWeth = mulRay(treasuryDebt, tag)
  const fix = divRay(toRay(1.1), spot)
  const fixedWeth = mulRay(treasuryDebt, fix)

  beforeEach(async () => {
    snapshot = await helper.takeSnapshot()
    snapshotId = snapshot['result']

    env = await YieldEnvironment.setup([])
    controller = env.controller
    treasury = env.treasury
    liquidations = env.liquidations
    unwind = env.unwind

    vat = env.maker.vat
    dai = env.maker.dai
    weth = env.maker.weth
    end = env.maker.end
    chai = env.maker.chai

    // Allow owner to interact directly with Treasury, not for production
    const treasuryFunctions = ['pushDai', 'pullDai', 'pushChai', 'pullChai', 'pushWeth', 'pullWeth'].map((func) =>
      id(func + '(address,uint256)')
    )
    await treasury.batchOrchestrate(owner, treasuryFunctions)
    await end.rely(owner, { from: owner }) // `owner` replaces MKR governance
  })

  afterEach(async () => {
    await helper.revertToSnapshot(snapshotId)
  })

  describe('with posted weth', () => {
    beforeEach(async () => {
      await weth.deposit({ from: owner, value: wethTokens1 })
      await weth.approve(treasury.address, wethTokens1, { from: owner })
      await treasury.pushWeth(owner, wethTokens1, { from: owner })

      assert.equal(
        (await vat.urns(WETH, treasury.address)).ink,
        wethTokens1,
        'Treasury should have ' + wethTokens1 + ' weth wei as collateral'
      )
    })

    it('does not allow to unwind if MakerDAO is live', async () => {
      await expectRevert(unwind.unwind({ from: owner }), 'Unwind: MakerDAO not shutting down')
    })

    describe('with Dss unwind initiated and tag defined', () => {
      beforeEach(async () => {
        await end.cage({ from: owner })
        await end.setTag(WETH, tag, { from: owner })
      })

      it('allows to unwind', async () => {
        await unwind.unwind({ from: owner })

        assert.equal(await unwind.live(), false, 'Unwind should be activated')
        assert.equal(await treasury.live(), false, 'Treasury should not be live')
        assert.equal(await controller.live(), false, 'Controller should not be live')
        assert.equal(await liquidations.live(), false, 'Liquidations should not be live')
      })

      describe('with fyDai in unwind', () => {
        beforeEach(async () => {
          await unwind.unwind({ from: owner })
        })

        it('allows to free system collateral without debt', async () => {
          await unwind.settleTreasury({ from: owner })

          assert.equal(
            await weth.balanceOf(unwind.address, { from: owner }),
            wethTokens1,
            'Treasury should have ' +
              wethTokens1 +
              ' weth in hand, instead has ' +
              (await weth.balanceOf(unwind.address, { from: owner }))
          )
        })

        it('does not allow to push or pull assets', async () => {
          await expectRevert(
            treasury.pushWeth(user, wethTokens1, { from: owner }),
            'Treasury: Not available during unwind'
          )
          await expectRevert(
            treasury.pushChai(user, chaiTokens1, { from: owner }),
            'Treasury: Not available during unwind'
          )
          await expectRevert(
            treasury.pushDai(user, daiTokens1, { from: owner }),
            'Treasury: Not available during unwind'
          )
          await expectRevert(treasury.pullWeth(owner, 1, { from: owner }), 'Treasury: Not available during unwind')
          await expectRevert(treasury.pullChai(owner, 1, { from: owner }), 'Treasury: Not available during unwind')
          await expectRevert(treasury.pullDai(owner, 1, { from: owner }), 'Treasury: Not available during unwind')
        })
      })
    })

    describe('with debt', () => {
      beforeEach(async () => {
        await treasury.pullDai(owner, treasuryDebt, { from: owner })
        expect((await vat.urns(WETH, treasury.address)).art).to.be.bignumber.gt(ZERO)
        expect(await treasury.debt()).to.be.bignumber.gt(ZERO)

        // Adding some extra unlocked collateral
        await weth.deposit({ from: owner, value: 1 })
        await weth.approve(treasury.address, 1, { from: owner })
        await treasury.pushWeth(owner, 1, { from: owner })
      })

      describe('with unwind initiated', () => {
        beforeEach(async () => {
          await end.cage({ from: owner })
          await end.setTag(WETH, tag, { from: owner })
          await unwind.unwind({ from: owner })
        })

        it('allows to settle treasury debt', async () => {
          await unwind.settleTreasury({ from: owner })

          almostEqual(
            await weth.balanceOf(unwind.address, { from: owner }),
            bnify(wethTokens1).sub(taggedWeth).toString(),
            precision
          )
        })
      })
    })

    describe('with savings', () => {
      beforeEach(async () => {
        await env.maker.getDai(owner, daiTokens1, rate1)

        await dai.approve(treasury.address, daiTokens1, { from: owner })
        await treasury.pushDai(owner, daiTokens1, { from: owner })

        assert.equal(
          await chai.balanceOf(treasury.address),
          chaiTokens1,
          'Treasury should have ' + daiTokens1 + ' savings (as chai).'
        )
      })

      describe('with Dss unwind initiated and fix defined', () => {
        beforeEach(async () => {
          await env.maker.getDai(user, bnify(daiTokens1).mul(2), rate1)

          await end.cage({ from: owner })
          await end.setTag(WETH, tag, { from: owner })
          await end.setDebt(1, { from: owner })
          await end.setFix(WETH, fix, { from: owner })

          // Settle some random guy's debt for end.sol to have weth
          await end.skim(WETH, user, { from: user })

          await unwind.unwind({ from: owner })
        })

        it('allows to cash dai for weth', async () => {
          assert.equal(await vat.gem(WETH, unwind.address), 0, 'Unwind should have no weth in WethJoin')

          await unwind.cashSavings({ from: owner })

          // Fun fact, MakerDAO rounds in your favour when determining how much collateral to take to settle your debt.
          assert.equal(await chai.balanceOf(treasury.address), 0, 'Treasury should have no savings (as chai).')
          almostEqual(
            await weth.balanceOf(unwind.address, { from: owner }),
            fixedWeth.toString(),
            precision // We are off by more than the usual precision here, not a big deal
          )
        })
      })
    })
  })
})
Example #11
Source File: 153_unwind_liquidations.ts    From fyDai with GNU General Public License v3.0 4 votes vote down vote up
contract('Unwind - Liquidations', async (accounts) => {
  let [owner, user1, user2, user3] = accounts

  let snapshot: any
  let snapshotId: string

  let env: YieldEnvironment

  let vat: Contract
  let fyDai1: Contract
  let controller: Contract
  let treasury: Contract
  let weth: Contract
  let liquidations: Contract
  let unwind: Contract
  let end: Contract

  let maturity1: number
  let maturity2: number

  const rate2 = toRay(1.3) // At the time of writing, rate is 1.02004188

  const fix = divRay(toRay(1.0), mulRay(spot, toRay(1.1)))

  beforeEach(async () => {
    snapshot = await helper.takeSnapshot()
    snapshotId = snapshot['result']

    const block = await web3.eth.getBlockNumber()
    maturity1 = (await web3.eth.getBlock(block)).timestamp + 1000
    maturity2 = (await web3.eth.getBlock(block)).timestamp + 2000

    env = await YieldEnvironment.setup([maturity1, maturity2])
    controller = env.controller
    treasury = env.treasury
    unwind = env.unwind
    liquidations = env.liquidations

    vat = env.maker.vat
    weth = env.maker.weth
    end = env.maker.end

    // Setup fyDai
    fyDai1 = env.fyDais[0]
    await end.rely(owner, { from: owner }) // `owner` replaces MKR governance
  })

  afterEach(async () => {
    await helper.revertToSnapshot(snapshotId)
  })

  describe('with posted collateral and borrowed fyDai', () => {
    beforeEach(async () => {
      // Weth setup
      await env.postWeth(user2, wethTokens1)
      let toBorrow = await env.unlockedOf(WETH, user2)
      await controller.borrow(WETH, maturity1, user2, user2, toBorrow, { from: user2 })

      await env.postWeth(user3, bnify(wethTokens1).mul(2))
      toBorrow = bnify(await env.unlockedOf(WETH, user3))
        .div(2)
        .toString()
      await controller.borrow(WETH, maturity1, user3, user3, toBorrow, { from: user3 })
      await controller.borrow(WETH, maturity2, user3, user3, toBorrow, { from: user3 })

      await env.postChai(user1, chaiTokens1, chi1, rate1)

      // Make sure that end.sol will have enough weth to cash chai savings
      await env.maker.getDai(owner, bnify(wethTokens1).mul(10), rate1)

      // Make fyDai1 borrowers go under by raising the rate, then liquidate them
      await helper.advanceTime(1000)
      await helper.advanceBlock()
      await fyDai1.mature()
      await vat.fold(WETH, vat.address, subBN(rate2, rate1), { from: owner })
      await liquidations.liquidate(user2, { from: user1 })
      await liquidations.liquidate(user3, { from: user1 })
    })

    it('allows orchestrated contracts to erase liquidations vaults', async () => {
      // Allow `owner` to bypass orchestration restrictions
      await liquidations.orchestrate(owner, id('erase(address)'), { from: owner })
      const userVault = await liquidations.vaults(user2, { from: owner })
      const totals = await liquidations.totals({ from: owner })
      const totalRemainingDebt = subBN(totals.debt.toString(), userVault.debt.toString())
      const totalRemainingCollateral = subBN(totals.collateral.toString(), userVault.collateral.toString())

      await liquidations.erase(user2, { from: owner })
      expect(new BN(userVault.debt)).to.be.bignumber.gt(new BN(0))
      expect(new BN(userVault.collateral)).to.be.bignumber.gt(new BN(0))
      expect(new BN(totals.debt)).to.be.bignumber.gt(new BN(0))
      expect(new BN(totals.collateral)).to.be.bignumber.gt(new BN(0))
      assert.equal((await liquidations.vaults(user2, { from: owner })).debt, 0, 'User debt should have been erased')
      assert.equal(
        (await liquidations.vaults(user2, { from: owner })).debt,
        0,
        'User collateral should have been erased'
      )
      assert.equal(
        (await liquidations.totals({ from: owner })).debt,
        totalRemainingDebt.toString(),
        'Total debt should have been ' +
          totalRemainingDebt +
          ', instead is ' +
          (await liquidations.totals({ from: owner })).debt
      )
      assert.equal(
        (await liquidations.totals({ from: owner })).collateral,
        totalRemainingCollateral.toString(),
        'Total collateral should have been ' +
          totalRemainingCollateral +
          ', instead is ' +
          (await liquidations.totals({ from: owner })).collateral
      )
    })

    describe('once shutdown', () => {
      beforeEach(async () => {
        await env.shutdown(owner, user1, user2)
      })

      it('allows users to settle liquidations vaults', async () => {
        const userBalance = await weth.balanceOf(user2, { from: user2 })
        const userVault = await liquidations.vaults(user2, { from: owner })
        const settlingCost = mulRay(userVault.debt.toString(), fix)
        const wethRemainder = bnify(userVault.collateral.toString()).sub(settlingCost)

        assert.equal(userBalance, 0, 'User2 should have no weth')

        await unwind.settleLiquidations(user2, { from: owner })

        if (wethRemainder.lt(ZERO)) {
          assert.equal(
            await weth.balanceOf(user2, { from: user2 }),
            0,
            'User2 should have no weth, instead has ' + (await weth.balanceOf(user2, { from: user2 }))
          )
        } else {
          assert.equal(
            await weth.balanceOf(user2, { from: user2 }),
            wethRemainder.toString(),
            'User2 should have ' +
              wethRemainder +
              ' weth, instead has ' +
              (await weth.balanceOf(user2, { from: user2 }))
          )
        }
      })
    })
  })
})