From c0507e9264cd53f2d6411568aa96dc7096962209 Mon Sep 17 00:00:00 2001 From: Edward Mulraney Date: Tue, 22 Jun 2021 13:37:48 +0100 Subject: [PATCH 1/3] add hint helper functions to Liquity resolver --- contracts/protocols/mainnet/liquity.sol | 74 ++++++++++++++++++++++++- test/liquity.js | 39 +++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/contracts/protocols/mainnet/liquity.sol b/contracts/protocols/mainnet/liquity.sol index c22f149..5e3907e 100644 --- a/contracts/protocols/mainnet/liquity.sol +++ b/contracts/protocols/mainnet/liquity.sol @@ -30,13 +30,53 @@ interface PoolLike { function getETH() external view returns (uint); } -contract DSMath { +interface HintHelpersLike { + function computeNominalCR(uint _coll, uint _debt) external pure returns (uint); + function computeCR(uint _coll, uint _debt, uint _price) external pure returns (uint); + function getApproxHint(uint _CR, uint _numTrials, uint _inputRandomSeed) external view returns ( + address hintAddress, + uint diff, + uint latestRandomSeed + ); + function getRedemptionHints(uint _LUSDamount, uint _price, uint _maxIterations) external view returns ( + address firstHint, + uint partialRedemptionHintNICR, + uint truncatedLUSDamount + ); +} + +interface SortedTrovesLike { + function getSize() external view returns (uint256); + function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address); +} + +contract Math { + /* DSMath add */ function add(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x, "math-not-safe"); } + + /* DSMath mul */ + function mul(uint x, uint y) internal pure returns (uint z) { + require(y == 0 || (z = x * y) / y == x, "math-not-safe"); + } + + /* Uniswap V2 sqrt */ + function sqrt(uint y) internal pure returns (uint z) { + if (y > 3) { + z = y; + uint x = y / 2 + 1; + while (x < z) { + z = x; + x = (y / x + x) / 2; + } + } else if (y != 0) { + z = 1; + } + } } -contract Helpers is DSMath { +contract Helpers is Math { TroveManagerLike internal constant troveManager = TroveManagerLike(0xA39739EF8b0231DbFA0DcdA07d7e29faAbCf4bb2); @@ -52,6 +92,12 @@ contract Helpers is DSMath { PoolLike internal constant defaultPool = PoolLike(0x896a3F03176f05CFbb4f006BfCd8723F2B0D741C); + HintHelpersLike internal constant hintHelpers = + HintHelpersLike(0xE84251b93D9524E0d2e621Ba7dc7cb3579F997C0); + + SortedTrovesLike internal constant sortedTroves = + SortedTrovesLike(0x8FdD3fbFEb32b28fb73555518f8b361bCeA741A6); + struct Trove { uint collateral; uint debt; @@ -120,6 +166,30 @@ contract Resolver is Helpers { bool isInRecoveryMode = troveManager.checkRecoveryMode(oracleEthPrice); return System(borrowFee, ethTvl, tcr, isInRecoveryMode); } + + function getTrovePositionHints(uint collateral, uint debt, uint searchIterations) external view returns ( + address upperHint, + address lowerHint + ) { + // See: https://github.com/liquity/dev#supplying-hints-to-trove-operations + uint nominalCr = hintHelpers.computeNominalCR(collateral, debt); + searchIterations = searchIterations == 0 ? mul(10, sqrt(sortedTroves.getSize())) : searchIterations; + (address hintAddress, ,) = hintHelpers.getApproxHint(nominalCr, searchIterations, 3); + return sortedTroves.findInsertPosition(nominalCr, hintAddress, hintAddress); + } + + function getRedemptionPositionHints(uint amount, uint oracleEthPrice, uint searchIterations) external view returns ( + uint partialHintNicr, + address firstHint, + address upperHint, + address lowerHint + ) { + // See: https://github.com/liquity/dev#hints-for-redeemcollateral + (firstHint, partialHintNicr, ) = hintHelpers.getRedemptionHints(amount, oracleEthPrice, 0); + searchIterations = searchIterations == 0 ? mul(10, sqrt(sortedTroves.getSize())) : searchIterations; + (address hintAddress, ,) = hintHelpers.getApproxHint(partialHintNicr, searchIterations, 3); + (upperHint, lowerHint) = sortedTroves.findInsertPosition(partialHintNicr, hintAddress, hintAddress); + } } contract InstaLiquityResolver is Resolver { diff --git a/test/liquity.js b/test/liquity.js index b4a9170..69dee3a 100644 --- a/test/liquity.js +++ b/test/liquity.js @@ -112,6 +112,45 @@ describe("InstaLiquityResolver", () => { expect(systemState).to.eql(expectedSystemState); }); }); + + describe("getTrovePositionHints()", () => { + it("returns the upper and lower address of Troves nearest to the given Trove", async () => { + const collateral = hre.ethers.utils.parseEther("10"); + const debt = hre.ethers.utils.parseUnits("5000", 18); + const searchIterations = 10; + const [upperHint, lowerHint] = await liquity.getTrovePositionHints( + collateral, + debt, + searchIterations + ); + + expect(upperHint).eq("0xbf9a4eCC4151f28C03100bA2C0555a3D3e439e69"); + expect(lowerHint).eq("0xa4FC81A7AB93360543eb1e814D0127f466012CED"); + }); + }); + + describe("getRedemptionPositionHints()", () => { + it("returns the upper and lower address of the range of Troves to be redeemed against the given amount", async () => { + const amount = hre.ethers.utils.parseUnits("10000", 18); // 10,000 LUSD + const oracleEthPrice = await liquityPriceOracle.callStatic.fetchPrice(); + const searchIterations = 10; + const [ + partialRedemptionHintNicr, + firstHint, + upperHint, + lowerHint, + ] = await liquity.getRedemptionPositionHints( + amount, + oracleEthPrice, + searchIterations + ); + + expect(partialRedemptionHintNicr).eq("69529933762909647"); + expect(firstHint).eq("0xc16aDd8bA17ab81B27e930Da8a67848120565d8c"); + expect(upperHint).eq("0x66882C005188F0F4d95825ED7A7F78ed3055f167"); + expect(lowerHint).eq("0x0C22C11a8ed4C23ffD19629283548B1692b58e92"); + }); + }); }); const resetHardhatBlockNumber = async (blockNumber) => { From 4df8b8bf56789de4150709214e5af15d875be7e7 Mon Sep 17 00:00:00 2001 From: Edward Mulraney Date: Wed, 23 Jun 2021 16:57:24 +0100 Subject: [PATCH 2/3] use object spec for tests --- test/liquity.js | 126 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/test/liquity.js b/test/liquity.js index 69dee3a..59efbcc 100644 --- a/test/liquity.js +++ b/test/liquity.js @@ -1,5 +1,6 @@ const { expect } = require("chai"); const hardhatConfig = require("../hardhat.config"); + const { BigNumber } = hre.ethers; // Deterministic block number to run these tests from on forked mainnet. If you change this, tests will break. @@ -13,28 +14,37 @@ const PRICE_FEED_ADDRESS = "0x4c517D4e2C851CA76d7eC94B805269Df0f2201De"; const PRICE_FEED_ABI = ["function fetchPrice() external returns (uint)"]; /* Begin: Mock test data (based on specified BLOCK_NUMBER and JUSTIN_SUN_ADDRESS) */ -const expectedTrovePosition = [ - /* collateral */ BigNumber.from("582880000000000000000000"), - /* debt */ BigNumber.from("372000200000000000000000000"), - /* icr */ BigNumber.from("3859882210893925325"), -]; -const expectedStabilityPosition = [ - /* deposit */ BigNumber.from("299979329615565997640451998"), - /* ethGain */ BigNumber.from("8629038660000000000"), - /* lqtyGain */ BigNumber.from("53244322633874479119945"), -]; -const expectedStakePosition = [ - /* amount */ BigNumber.from("981562996504090969804965"), - /* ethGain */ BigNumber.from("18910541408996344243"), - /* lusdGain */ BigNumber.from("66201062534511228032281"), -]; - -const expectedSystemState = [ - /* borrowFee */ BigNumber.from("6900285109012952"), - /* ethTvl */ BigNumber.from("852500462432421494350957"), - /* tcr */ BigNumber.from("3250195441371082828"), - /* isInRecoveryMode */ false, -]; +const expectedTrovePosition = { + collateral: BigNumber.from("582880000000000000000000"), + debt: BigNumber.from("372000200000000000000000000"), + icr: BigNumber.from("3859882210893925325"), +}; +const expectedStabilityPosition = { + deposit: BigNumber.from("299979329615565997640451998"), + ethGain: BigNumber.from("8629038660000000000"), + lqtyGain: BigNumber.from("53244322633874479119945"), +}; +const expectedStakePosition = { + amount: BigNumber.from("981562996504090969804965"), + ethGain: BigNumber.from("18910541408996344243"), + lusdGain: BigNumber.from("66201062534511228032281"), +}; +const expectedSystemState = { + borrowFee: BigNumber.from("6900285109012952"), + ethTvl: BigNumber.from("852500462432421494350957"), + tcr: BigNumber.from("3250195441371082828"), + isInRecoveryMode: false, +}; +const expectedTrovePositionHints = { + upperHint: "0xbf9a4eCC4151f28C03100bA2C0555a3D3e439e69", + lowerHint: "0xa4FC81A7AB93360543eb1e814D0127f466012CED", +}; +const expectedRedemptionPositionHints = { + partialRedemptionHintNicr: "69529933762909647", + firstHint: "0xc16aDd8bA17ab81B27e930Da8a67848120565d8c", + upperHint: "0x66882C005188F0F4d95825ED7A7F78ed3055f167", + lowerHint: "0x0C22C11a8ed4C23ffD19629283548B1692b58e92", +}; /* End: Mock test data */ describe("InstaLiquityResolver", () => { @@ -55,6 +65,7 @@ describe("InstaLiquityResolver", () => { ); liquity = await liquityFactory.deploy(); + await liquity.deployed(); }); @@ -69,7 +80,11 @@ describe("InstaLiquityResolver", () => { JUSTIN_SUN_ADDRESS, oracleEthPrice ); - expect(trovePosition).to.eql(expectedTrovePosition); + expect(trovePosition.collateral).to.equal( + expectedTrovePosition.collateral + ); + expect(trovePosition.debt).to.equal(expectedTrovePosition.debt); + expect(trovePosition.icr).to.equal(expectedTrovePosition.icr); }); }); @@ -78,14 +93,24 @@ describe("InstaLiquityResolver", () => { const stabilityPosition = await liquity.getStabilityDeposit( JUSTIN_SUN_ADDRESS ); - expect(stabilityPosition).to.eql(expectedStabilityPosition); + expect(stabilityPosition.deposit).to.equal( + expectedStabilityPosition.deposit + ); + expect(stabilityPosition.ethGain).to.equal( + expectedStabilityPosition.ethGain + ); + expect(stabilityPosition.lqtyGain).to.equal( + expectedStabilityPosition.lqtyGain + ); }); }); describe("getStake()", () => { it("returns a user's Stake position", async () => { const stakePosition = await liquity.getStake(JUSTIN_SUN_ADDRESS); - expect(stakePosition).to.eql(expectedStakePosition); + expect(stakePosition.amount).to.equal(expectedStakePosition.amount); + expect(stakePosition.ethGain).to.equal(expectedStakePosition.ethGain); + expect(stakePosition.lusdGain).to.equal(expectedStakePosition.lusdGain); }); }); @@ -96,12 +121,30 @@ describe("InstaLiquityResolver", () => { JUSTIN_SUN_ADDRESS, oracleEthPrice ); - const expectedPosition = [ - expectedTrovePosition, - expectedStabilityPosition, - expectedStakePosition, - ]; - expect(position).to.eql(expectedPosition); + const expectedPosition = { + trove: expectedTrovePosition, + stability: expectedStabilityPosition, + stake: expectedStakePosition, + }; + expect(position.trove.collateral).to.equal( + expectedPosition.trove.collateral + ); + expect(position.trove.debt).to.equal(expectedPosition.trove.debt); + expect(position.trove.icr).to.equal(expectedPosition.trove.icr); + + expect(position.stability.deposit).to.equal( + expectedPosition.stability.deposit + ); + expect(position.stability.ethGain).to.equal( + expectedPosition.stability.ethGain + ); + expect(position.stability.lqtyGain).to.equal( + expectedPosition.stability.lqtyGain + ); + + expect(position.stake.amount).to.equal(expectedPosition.stake.amount); + expect(position.stake.ethGain).to.equal(expectedPosition.stake.ethGain); + expect(position.stake.lusdGain).to.equal(expectedPosition.stake.lusdGain); }); }); @@ -109,7 +152,12 @@ describe("InstaLiquityResolver", () => { it("returns Liquity system state", async () => { const oracleEthPrice = await liquityPriceOracle.callStatic.fetchPrice(); const systemState = await liquity.getSystemState(oracleEthPrice); - expect(systemState).to.eql(expectedSystemState); + expect(systemState.borrowFee).to.equal(expectedSystemState.borrowFee); + expect(systemState.ethTvl).to.equal(expectedSystemState.ethTvl); + expect(systemState.tcr).to.equal(expectedSystemState.tcr); + expect(systemState.isInRecoveryMode).to.equal( + expectedSystemState.isInRecoveryMode + ); }); }); @@ -124,8 +172,8 @@ describe("InstaLiquityResolver", () => { searchIterations ); - expect(upperHint).eq("0xbf9a4eCC4151f28C03100bA2C0555a3D3e439e69"); - expect(lowerHint).eq("0xa4FC81A7AB93360543eb1e814D0127f466012CED"); + expect(upperHint).eq(expectedTrovePositionHints.upperHint); + expect(lowerHint).eq(expectedTrovePositionHints.lowerHint); }); }); @@ -145,10 +193,12 @@ describe("InstaLiquityResolver", () => { searchIterations ); - expect(partialRedemptionHintNicr).eq("69529933762909647"); - expect(firstHint).eq("0xc16aDd8bA17ab81B27e930Da8a67848120565d8c"); - expect(upperHint).eq("0x66882C005188F0F4d95825ED7A7F78ed3055f167"); - expect(lowerHint).eq("0x0C22C11a8ed4C23ffD19629283548B1692b58e92"); + expect(partialRedemptionHintNicr).eq( + expectedRedemptionPositionHints.partialRedemptionHintNicr + ); + expect(firstHint).eq(expectedRedemptionPositionHints.firstHint); + expect(upperHint).eq(expectedRedemptionPositionHints.upperHint); + expect(lowerHint).eq(expectedRedemptionPositionHints.lowerHint); }); }); }); From b471eab625fbdd54fde605df22ccd824918129ba Mon Sep 17 00:00:00 2001 From: Edward Mulraney Date: Sat, 26 Jun 2021 11:31:34 +0100 Subject: [PATCH 3/3] add randomSeed assss optional paramter to hint functions --- contracts/protocols/mainnet/liquity.sol | 298 ++++++++++++------------ test/liquity.js | 10 +- 2 files changed, 157 insertions(+), 151 deletions(-) diff --git a/contracts/protocols/mainnet/liquity.sol b/contracts/protocols/mainnet/liquity.sol index 5e3907e..04c5ee5 100644 --- a/contracts/protocols/mainnet/liquity.sol +++ b/contracts/protocols/mainnet/liquity.sol @@ -2,196 +2,198 @@ pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; interface TroveManagerLike { - function getBorrowingRateWithDecay() external view returns (uint); - function getTCR(uint _price) external view returns (uint); - function getCurrentICR(address _borrower, uint _price) external view returns (uint); - function checkRecoveryMode(uint _price) external view returns (bool); - function getEntireDebtAndColl(address _borrower) external view returns ( - uint debt, - uint coll, - uint pendingLUSDDebtReward, - uint pendingETHReward - ); + function getBorrowingRateWithDecay() external view returns (uint); + function getTCR(uint _price) external view returns (uint); + function getCurrentICR(address _borrower, uint _price) external view returns (uint); + function checkRecoveryMode(uint _price) external view returns (bool); + function getEntireDebtAndColl(address _borrower) external view returns ( + uint debt, + uint coll, + uint pendingLUSDDebtReward, + uint pendingETHReward + ); } interface StabilityPoolLike { - function getCompoundedLUSDDeposit(address _depositor) external view returns (uint); - function getDepositorETHGain(address _depositor) external view returns (uint); - function getDepositorLQTYGain(address _depositor) external view returns (uint); + function getCompoundedLUSDDeposit(address _depositor) external view returns (uint); + function getDepositorETHGain(address _depositor) external view returns (uint); + function getDepositorLQTYGain(address _depositor) external view returns (uint); } interface StakingLike { - function stakes(address owner) external view returns (uint); - function getPendingETHGain(address _user) external view returns (uint); - function getPendingLUSDGain(address _user) external view returns (uint); + function stakes(address owner) external view returns (uint); + function getPendingETHGain(address _user) external view returns (uint); + function getPendingLUSDGain(address _user) external view returns (uint); } interface PoolLike { - function getETH() external view returns (uint); + function getETH() external view returns (uint); } interface HintHelpersLike { - function computeNominalCR(uint _coll, uint _debt) external pure returns (uint); - function computeCR(uint _coll, uint _debt, uint _price) external pure returns (uint); - function getApproxHint(uint _CR, uint _numTrials, uint _inputRandomSeed) external view returns ( - address hintAddress, - uint diff, - uint latestRandomSeed - ); - function getRedemptionHints(uint _LUSDamount, uint _price, uint _maxIterations) external view returns ( - address firstHint, - uint partialRedemptionHintNICR, - uint truncatedLUSDamount - ); + function computeNominalCR(uint _coll, uint _debt) external pure returns (uint); + function computeCR(uint _coll, uint _debt, uint _price) external pure returns (uint); + function getApproxHint(uint _CR, uint _numTrials, uint _inputRandomSeed) external view returns ( + address hintAddress, + uint diff, + uint latestRandomSeed + ); + function getRedemptionHints(uint _LUSDamount, uint _price, uint _maxIterations) external view returns ( + address firstHint, + uint partialRedemptionHintNICR, + uint truncatedLUSDamount + ); } interface SortedTrovesLike { - function getSize() external view returns (uint256); - function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address); + function getSize() external view returns (uint256); + function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address); } contract Math { - /* DSMath add */ - function add(uint x, uint y) internal pure returns (uint z) { - require((z = x + y) >= x, "math-not-safe"); - } - - /* DSMath mul */ - function mul(uint x, uint y) internal pure returns (uint z) { - require(y == 0 || (z = x * y) / y == x, "math-not-safe"); + /* DSMath add */ + function add(uint x, uint y) internal pure returns (uint z) { + require((z = x + y) >= x, "math-not-safe"); } + + /* DSMath mul */ + function mul(uint x, uint y) internal pure returns (uint z) { + require(y == 0 || (z = x * y) / y == x, "math-not-safe"); + } - /* Uniswap V2 sqrt */ - function sqrt(uint y) internal pure returns (uint z) { - if (y > 3) { - z = y; - uint x = y / 2 + 1; - while (x < z) { - z = x; - x = (y / x + x) / 2; - } - } else if (y != 0) { - z = 1; - } - } + /* Uniswap V2 sqrt */ + function sqrt(uint y) internal pure returns (uint z) { + if (y > 3) { + z = y; + uint x = y / 2 + 1; + while (x < z) { + z = x; + x = (y / x + x) / 2; + } + } else if (y != 0) { + z = 1; + } + } } contract Helpers is Math { - TroveManagerLike internal constant troveManager = - TroveManagerLike(0xA39739EF8b0231DbFA0DcdA07d7e29faAbCf4bb2); + TroveManagerLike internal constant troveManager = + TroveManagerLike(0xA39739EF8b0231DbFA0DcdA07d7e29faAbCf4bb2); - StabilityPoolLike internal constant stabilityPool = - StabilityPoolLike(0x66017D22b0f8556afDd19FC67041899Eb65a21bb); + StabilityPoolLike internal constant stabilityPool = + StabilityPoolLike(0x66017D22b0f8556afDd19FC67041899Eb65a21bb); - StakingLike internal constant staking = - StakingLike(0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d); + StakingLike internal constant staking = + StakingLike(0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d); - PoolLike internal constant activePool = - PoolLike(0xDf9Eb223bAFBE5c5271415C75aeCD68C21fE3D7F); - - PoolLike internal constant defaultPool = - PoolLike(0x896a3F03176f05CFbb4f006BfCd8723F2B0D741C); + PoolLike internal constant activePool = + PoolLike(0xDf9Eb223bAFBE5c5271415C75aeCD68C21fE3D7F); + + PoolLike internal constant defaultPool = + PoolLike(0x896a3F03176f05CFbb4f006BfCd8723F2B0D741C); - HintHelpersLike internal constant hintHelpers = - HintHelpersLike(0xE84251b93D9524E0d2e621Ba7dc7cb3579F997C0); - - SortedTrovesLike internal constant sortedTroves = - SortedTrovesLike(0x8FdD3fbFEb32b28fb73555518f8b361bCeA741A6); - - struct Trove { - uint collateral; - uint debt; - uint icr; - } + HintHelpersLike internal constant hintHelpers = + HintHelpersLike(0xE84251b93D9524E0d2e621Ba7dc7cb3579F997C0); + + SortedTrovesLike internal constant sortedTroves = + SortedTrovesLike(0x8FdD3fbFEb32b28fb73555518f8b361bCeA741A6); + + struct Trove { + uint collateral; + uint debt; + uint icr; + } - struct StabilityDeposit { - uint deposit; - uint ethGain; - uint lqtyGain; - } + struct StabilityDeposit { + uint deposit; + uint ethGain; + uint lqtyGain; + } - struct Stake { - uint amount; - uint ethGain; - uint lusdGain; - } + struct Stake { + uint amount; + uint ethGain; + uint lusdGain; + } - struct Position { - Trove trove; - StabilityDeposit stability; - Stake stake; - } + struct Position { + Trove trove; + StabilityDeposit stability; + Stake stake; + } - struct System { - uint borrowFee; - uint ethTvl; - uint tcr; - bool isInRecoveryMode; - } + struct System { + uint borrowFee; + uint ethTvl; + uint tcr; + bool isInRecoveryMode; + } } contract Resolver is Helpers { - function getTrove(address owner, uint oracleEthPrice) public view returns (Trove memory) { - (uint debt, uint collateral, , ) = troveManager.getEntireDebtAndColl(owner); - uint icr = troveManager.getCurrentICR(owner, oracleEthPrice); - return Trove(collateral, debt, icr); - } + function getTrove(address owner, uint oracleEthPrice) public view returns (Trove memory) { + (uint debt, uint collateral, , ) = troveManager.getEntireDebtAndColl(owner); + uint icr = troveManager.getCurrentICR(owner, oracleEthPrice); + return Trove(collateral, debt, icr); + } - function getStabilityDeposit(address owner) public view returns (StabilityDeposit memory) { - uint deposit = stabilityPool.getCompoundedLUSDDeposit(owner); - uint ethGain = stabilityPool.getDepositorETHGain(owner); - uint lqtyGain = stabilityPool.getDepositorLQTYGain(owner); - return StabilityDeposit(deposit, ethGain, lqtyGain); - } + function getStabilityDeposit(address owner) public view returns (StabilityDeposit memory) { + uint deposit = stabilityPool.getCompoundedLUSDDeposit(owner); + uint ethGain = stabilityPool.getDepositorETHGain(owner); + uint lqtyGain = stabilityPool.getDepositorLQTYGain(owner); + return StabilityDeposit(deposit, ethGain, lqtyGain); + } - function getStake(address owner) public view returns (Stake memory) { - uint amount = staking.stakes(owner); - uint ethGain = staking.getPendingETHGain(owner); - uint lusdGain = staking.getPendingLUSDGain(owner); - return Stake(amount, ethGain, lusdGain); - } + function getStake(address owner) public view returns (Stake memory) { + uint amount = staking.stakes(owner); + uint ethGain = staking.getPendingETHGain(owner); + uint lusdGain = staking.getPendingLUSDGain(owner); + return Stake(amount, ethGain, lusdGain); + } - function getPosition(address owner, uint oracleEthPrice) external view returns (Position memory) { - Trove memory trove = getTrove(owner, oracleEthPrice); - StabilityDeposit memory stability = getStabilityDeposit(owner); - Stake memory stake = getStake(owner); - return Position(trove, stability, stake); - } + function getPosition(address owner, uint oracleEthPrice) external view returns (Position memory) { + Trove memory trove = getTrove(owner, oracleEthPrice); + StabilityDeposit memory stability = getStabilityDeposit(owner); + Stake memory stake = getStake(owner); + return Position(trove, stability, stake); + } - function getSystemState(uint oracleEthPrice) external view returns (System memory) { - uint borrowFee = troveManager.getBorrowingRateWithDecay(); - uint ethTvl = add(activePool.getETH(), defaultPool.getETH()); - uint tcr = troveManager.getTCR(oracleEthPrice); - bool isInRecoveryMode = troveManager.checkRecoveryMode(oracleEthPrice); - return System(borrowFee, ethTvl, tcr, isInRecoveryMode); - } + function getSystemState(uint oracleEthPrice) external view returns (System memory) { + uint borrowFee = troveManager.getBorrowingRateWithDecay(); + uint ethTvl = add(activePool.getETH(), defaultPool.getETH()); + uint tcr = troveManager.getTCR(oracleEthPrice); + bool isInRecoveryMode = troveManager.checkRecoveryMode(oracleEthPrice); + return System(borrowFee, ethTvl, tcr, isInRecoveryMode); + } - function getTrovePositionHints(uint collateral, uint debt, uint searchIterations) external view returns ( - address upperHint, - address lowerHint - ) { - // See: https://github.com/liquity/dev#supplying-hints-to-trove-operations - uint nominalCr = hintHelpers.computeNominalCR(collateral, debt); - searchIterations = searchIterations == 0 ? mul(10, sqrt(sortedTroves.getSize())) : searchIterations; - (address hintAddress, ,) = hintHelpers.getApproxHint(nominalCr, searchIterations, 3); - return sortedTroves.findInsertPosition(nominalCr, hintAddress, hintAddress); - } + function getTrovePositionHints(uint collateral, uint debt, uint searchIterations, uint randomSeed) external view returns ( + address upperHint, + address lowerHint + ) { + // See: https://github.com/liquity/dev#supplying-hints-to-trove-operations + uint nominalCr = hintHelpers.computeNominalCR(collateral, debt); + searchIterations = searchIterations == 0 ? mul(10, sqrt(sortedTroves.getSize())) : searchIterations; + randomSeed = randomSeed == 0 ? block.number : randomSeed; + (address hintAddress, ,) = hintHelpers.getApproxHint(nominalCr, searchIterations, randomSeed); + return sortedTroves.findInsertPosition(nominalCr, hintAddress, hintAddress); + } - function getRedemptionPositionHints(uint amount, uint oracleEthPrice, uint searchIterations) external view returns ( - uint partialHintNicr, - address firstHint, - address upperHint, - address lowerHint - ) { - // See: https://github.com/liquity/dev#hints-for-redeemcollateral - (firstHint, partialHintNicr, ) = hintHelpers.getRedemptionHints(amount, oracleEthPrice, 0); - searchIterations = searchIterations == 0 ? mul(10, sqrt(sortedTroves.getSize())) : searchIterations; - (address hintAddress, ,) = hintHelpers.getApproxHint(partialHintNicr, searchIterations, 3); - (upperHint, lowerHint) = sortedTroves.findInsertPosition(partialHintNicr, hintAddress, hintAddress); - } + function getRedemptionPositionHints(uint amount, uint oracleEthPrice, uint searchIterations, uint randomSeed) external view returns ( + uint partialHintNicr, + address firstHint, + address upperHint, + address lowerHint + ) { + // See: https://github.com/liquity/dev#hints-for-redeemcollateral + (firstHint, partialHintNicr, ) = hintHelpers.getRedemptionHints(amount, oracleEthPrice, 0); + searchIterations = searchIterations == 0 ? mul(10, sqrt(sortedTroves.getSize())) : searchIterations; + randomSeed = randomSeed == 0 ? block.number : randomSeed; + (address hintAddress, ,) = hintHelpers.getApproxHint(partialHintNicr, searchIterations, randomSeed); + (upperHint, lowerHint) = sortedTroves.findInsertPosition(partialHintNicr, hintAddress, hintAddress); + } } contract InstaLiquityResolver is Resolver { - string public constant name = "Liquity-Resolver-v1"; + string public constant name = "Liquity-Resolver-v1"; } diff --git a/test/liquity.js b/test/liquity.js index 59efbcc..b11b790 100644 --- a/test/liquity.js +++ b/test/liquity.js @@ -164,12 +164,14 @@ describe("InstaLiquityResolver", () => { describe("getTrovePositionHints()", () => { it("returns the upper and lower address of Troves nearest to the given Trove", async () => { const collateral = hre.ethers.utils.parseEther("10"); - const debt = hre.ethers.utils.parseUnits("5000", 18); + const debt = hre.ethers.utils.parseUnits("5000", 18); // 5,000 LUSD const searchIterations = 10; + const randomSeed = 3; const [upperHint, lowerHint] = await liquity.getTrovePositionHints( collateral, debt, - searchIterations + searchIterations, + randomSeed ); expect(upperHint).eq(expectedTrovePositionHints.upperHint); @@ -182,6 +184,7 @@ describe("InstaLiquityResolver", () => { const amount = hre.ethers.utils.parseUnits("10000", 18); // 10,000 LUSD const oracleEthPrice = await liquityPriceOracle.callStatic.fetchPrice(); const searchIterations = 10; + const randomSeed = 3; const [ partialRedemptionHintNicr, firstHint, @@ -190,7 +193,8 @@ describe("InstaLiquityResolver", () => { ] = await liquity.getRedemptionPositionHints( amount, oracleEthPrice, - searchIterations + searchIterations, + randomSeed ); expect(partialRedemptionHintNicr).eq(