Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions spot-vaults/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The official mainnet addresses are:
- Bill Broker (SPOT-USDC): [0xA088Aef966CAD7fE0B38e28c2E07590127Ab4ccB](https://etherscan.io/address/0xA088Aef966CAD7fE0B38e28c2E07590127Ab4ccB)
- SpotAppraiser: [0x965FBFebDA76d9AA11642C1d0074CdF02e546F3c](https://etherscan.io/address/0x965FBFebDA76d9AA11642C1d0074CdF02e546F3c)
- WethWamplManager: [0x6785fa26191eb531c54fd093931f395c4b01b583](https://etherscan.io/address/0x6785fa26191eb531c54fd093931f395c4b01b583)
- UsdcSpotManager: [0x780eB92040bf24cd9BF993505390e88E8ED59935](https://etherscan.io/address/0x780eB92040bf24cd9BF993505390e88E8ED59935)

The official testnet addresses are:

Expand Down
4 changes: 2 additions & 2 deletions spot-vaults/contracts/UsdcSpotManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ contract UsdcSpotManager {
function getSpotUSDPrice() public view returns (uint256) {
uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(VAULT.getTwap());
uint256 ratioX192 = uint256(sqrtPriceX96) * sqrtPriceX96;
uint256 spotPerUsdc = FullMath.mulDiv(ONE, ratioX192, (1 << 192));
return FullMath.mulDiv(spotPerUsdc, ONE_USDC, ONE_SPOT);
uint256 usdcPerSpot = FullMath.mulDiv(ONE, (1 << 192), ratioX192);
return FullMath.mulDiv(usdcPerSpot, ONE_SPOT, ONE_USDC);
}

/// @notice Checks the vault is overweight SPOT, and looking to sell the extra SPOT for USDC.
Expand Down
37 changes: 37 additions & 0 deletions spot-vaults/tasks/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,40 @@ task("deploy:WethWamplManager")
console.log("Skipping verification");
}
});

task("deploy:UsdcSpotManager")
.addParam(
"vault",
"the address of the usdc-spot charm vault",
undefined,
types.string,
false,
)
.addParam(
"spotAppraiser",
"the address of the spot appraiser",
undefined,
types.string,
false,
)
.addParam("verify", "flag to set false for local deployments", true, types.boolean)
.setAction(async function (args: TaskArguments, hre) {
const deployer = (await hre.ethers.getSigners())[0];
console.log("Signer", await deployer.getAddress());

const { vault, spotAppraiser } = args;

const UsdcSpotManager = await hre.ethers.getContractFactory("UsdcSpotManager");
const manager = await UsdcSpotManager.deploy(vault, spotAppraiser);
console.log("usdcSpotManager", manager.target);

if (args.verify) {
await sleep(30);
await hre.run("verify:contract", {
address: manager.target,
constructorArguments: [vault, spotAppraiser],
});
} else {
console.log("Skipping verification");
}
});
41 changes: 40 additions & 1 deletion spot-vaults/tasks/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ task("info:WethWamplManager")
console.log("dataValid:", r[1]);
console.log("isOverweightWampl:", await manager.isOverweightWampl());
console.log("prevDeviation:", pp(await manager.prevDeviation(), managerDecimals));
console.log("amplDeviation:", pp(deviation, managerDecimals));
console.log("deviation:", pp(deviation, managerDecimals));
console.log(
"activeLiqPerc:",
pp(await manager.computeActiveLiqPerc(deviation), managerDecimals),
Expand All @@ -180,3 +180,42 @@ task("info:WethWamplManager")
console.log("rebalanceActive:", rebalanceActive);
console.log("---------------------------------------------------------------");
});

task("info:UsdcSpotManager")
.addPositionalParam(
"address",
"the address of the usdc-spot mananger contract",
undefined,
types.string,
false,
)
.setAction(async function (args: TaskArguments, hre) {
const { address } = args;

const manager = await hre.ethers.getContractAt("UsdcSpotManager", address);
const managerDecimals = await manager.decimals();
console.log("---------------------------------------------------------------");
console.log("UsdcSpotManager:", manager.target);
console.log("owner:", await manager.owner());
console.log("spotAppraiser:", await manager.spotAppraiser());

console.log("---------------------------------------------------------------");
const spotPrice = await manager.getSpotUSDPrice();
console.log("spotPrice:", pp(spotPrice, managerDecimals));

const r = await manager.computeDeviationFactor.staticCall();
const deviation = r[0];
console.log("dataValid:", r[1]);
console.log("isOverweightSpot:", await manager.isOverweightSpot());
console.log("prevDeviation:", pp(await manager.prevDeviation(), managerDecimals));
console.log("deviation:", pp(deviation, managerDecimals));

let rebalanceActive = true;
try {
await manager.rebalance.staticCall();
} catch (e) {
rebalanceActive = false;
}
console.log("rebalanceActive:", rebalanceActive);
console.log("---------------------------------------------------------------");
});
5 changes: 2 additions & 3 deletions spot-vaults/tasks/tools.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { task, types } from "hardhat/config";
import { TaskArguments } from "hardhat/types";

export async function sleep(sleepSec: number) {
await new Promise(resolve => setTimeout(resolve, sleepSec));
}
export const sleep = seconds =>
new Promise(resolve => setTimeout(resolve, seconds * 1000));

task("accounts", "Prints the list of accounts", async (_taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
Expand Down
56 changes: 28 additions & 28 deletions spot-vaults/test/UsdcSpotManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe("UsdcSpotManager", function () {
await mockVault.mockMethod("fullUpper()", [800000]);
await mockVault.mockMethod("baseLower()", [45000]);
await mockVault.mockMethod("baseUpper()", [55000]);
await mockVault.mockMethod("getTwap()", [71000]);
await mockVault.mockMethod("getTwap()", [67200]);
await mockVault.mockMethod("limitThreshold()", [800000]);

const mockPool = new DMock("IUniswapV3Pool");
Expand Down Expand Up @@ -176,7 +176,7 @@ describe("UsdcSpotManager", function () {
const { manager, mockAppraiser } = await loadFixture(setupContracts);
await mockAppraiser.mockMethod("perpPrice()", [priceFP("1.2"), false]);
const r = await manager.computeDeviationFactor.staticCall();
expect(r[0]).to.eq(percFP("1.009614109343384160"));
expect(r[0]).to.eq(percFP("1.0057863765655975"));
expect(r[1]).to.eq(false);
});
});
Expand All @@ -186,47 +186,47 @@ describe("UsdcSpotManager", function () {
const { manager, mockAppraiser } = await loadFixture(setupContracts);
await mockAppraiser.mockMethod("usdPrice()", [priceFP("0.8"), false]);
const r = await manager.computeDeviationFactor.staticCall();
expect(r[0]).to.eq(percFP("1.009614109343384160"));
expect(r[0]).to.eq(percFP("1.0057863765655975"));
expect(r[1]).to.eq(false);
});
});

it("should return deviation factor", async function () {
const { manager } = await loadFixture(setupContracts);
const r = await manager.computeDeviationFactor.staticCall();
expect(r[0]).to.eq(percFP("1.009614109343384160"));
expect(r[0]).to.eq(percFP("1.0057863765655975"));
expect(r[1]).to.eq(true);
});

it("should return deviation factor", async function () {
const { manager, mockVault } = await loadFixture(setupContracts);
await mockVault.mockMethod("getTwap()", [72500]);
await mockVault.mockMethod("getTwap()", [65800]);
const r = await manager.computeDeviationFactor.staticCall();
expect(r[0]).to.eq(percFP("1.172995447264373845"));
expect(r[0]).to.eq(percFP("1.1569216182711425"));
expect(r[1]).to.eq(true);
});

it("should return deviation factor", async function () {
const { manager, mockVault } = await loadFixture(setupContracts);
await mockVault.mockMethod("getTwap()", [70500]);
await mockVault.mockMethod("getTwap()", [67800]);
const r = await manager.computeDeviationFactor.staticCall();
expect(r[0]).to.eq(percFP("0.960377048978079093"));
expect(r[0]).to.eq(percFP("0.947216779268338333"));
expect(r[1]).to.eq(true);
});

it("should return deviation factor", async function () {
const { manager, mockAppraiser } = await loadFixture(setupContracts);
await mockAppraiser.mockMethod("perpPrice()", [priceFP("1.5"), true]);
const r = await manager.computeDeviationFactor.staticCall();
expect(r[0]).to.eq(percFP("0.807691287474707328"));
expect(r[0]).to.eq(percFP("0.804629101252478"));
expect(r[1]).to.eq(true);
});

it("should return deviation factor", async function () {
const { manager, mockAppraiser } = await loadFixture(setupContracts);
await mockAppraiser.mockMethod("perpPrice()", [priceFP("1"), true]);
const r = await manager.computeDeviationFactor.staticCall();
expect(r[0]).to.eq(percFP("1.211536931212060992"));
expect(r[0]).to.eq(percFP("1.206943651878717"));
expect(r[1]).to.eq(true);
});

Expand Down Expand Up @@ -267,7 +267,7 @@ describe("UsdcSpotManager", function () {
it("should keep limit range", async function () {
const { manager, mockVault } = await loadFixture(setupContracts);

await mockVault.mockMethod("getTwap()", [72000]);
await mockVault.mockMethod("getTwap()", [66200]);
await mockVault.mockMethod("limitLower()", [40000]);
await mockVault.mockMethod("limitUpper()", [45000]);

Expand All @@ -279,15 +279,15 @@ describe("UsdcSpotManager", function () {
expect(await manager.prevDeviation()).to.eq("0");
expect(await manager.isOverweightSpot()).to.eq(true);
await expect(manager.rebalance()).not.to.be.reverted;
expect(await manager.prevDeviation()).to.eq(percFP("1.11579057353024426"));
expect(await manager.prevDeviation()).to.eq(percFP("1.111560295732100833"));
});
});

describe("when overweight usdc", function () {
it("should remove limit range", async function () {
const { manager, mockVault, mockPool } = await loadFixture(setupContracts);

await mockVault.mockMethod("getTwap()", [72000]);
await mockVault.mockMethod("getTwap()", [66200]);
await mockVault.mockMethod("limitLower()", [73000]);
await mockVault.mockMethod("limitUpper()", [75000]);
await mockPool.mockCall(
Expand All @@ -309,7 +309,7 @@ describe("UsdcSpotManager", function () {
expect(await manager.prevDeviation()).to.eq("0");
expect(await manager.isOverweightSpot()).to.eq(false);
await expect(manager.rebalance()).not.to.be.reverted;
expect(await manager.prevDeviation()).to.eq(percFP("1.115790573530244260"));
expect(await manager.prevDeviation()).to.eq(percFP("1.111560295732100833"));
});
});
});
Expand All @@ -319,7 +319,7 @@ describe("UsdcSpotManager", function () {
it("should remove limit range", async function () {
const { manager, mockVault, mockPool } = await loadFixture(setupContracts);

await mockVault.mockMethod("getTwap()", [72000]);
await mockVault.mockMethod("getTwap()", [66200]);
await mockVault.mockMethod("limitLower()", [40000]);
await mockVault.mockMethod("limitUpper()", [45000]);
await mockVault.mockMethod("period()", [86400]);
Expand All @@ -328,7 +328,7 @@ describe("UsdcSpotManager", function () {
await mockVault.mockMethod("rebalance()", []);
await manager.rebalance();

await mockVault.mockMethod("getTwap()", [70000]);
await mockVault.mockMethod("getTwap()", [67800]);
await mockVault.mockMethod("limitLower()", [60000]);
await mockVault.mockMethod("limitUpper()", [65000]);
await mockPool.mockCall(
Expand All @@ -343,18 +343,18 @@ describe("UsdcSpotManager", function () {
[],
);

expect(await manager.prevDeviation()).to.eq(percFP("1.115790573530244260"));
expect(await manager.prevDeviation()).to.eq(percFP("1.111560295732100833"));
expect(await manager.isOverweightSpot()).to.eq(true);
await expect(manager.rebalance()).not.to.be.reverted;
expect(await manager.prevDeviation()).to.eq(percFP("0.913541191300990579"));
expect(await manager.prevDeviation()).to.eq(percFP("0.947216779268338333"));
});
});

describe("when overweight usdc", function () {
it("should keep limit range", async function () {
const { manager, mockVault } = await loadFixture(setupContracts);

await mockVault.mockMethod("getTwap()", [72000]);
await mockVault.mockMethod("getTwap()", [66200]);
await mockVault.mockMethod("limitLower()", [40000]);
await mockVault.mockMethod("limitUpper()", [45000]);
await mockVault.mockMethod("period()", [86400]);
Expand All @@ -363,15 +363,15 @@ describe("UsdcSpotManager", function () {
await mockVault.mockMethod("rebalance()", []);
await manager.rebalance();

await mockVault.mockMethod("getTwap()", [70000]);
await mockVault.mockMethod("getTwap()", [67800]);
await mockVault.mockMethod("limitLower()", [75000]);
await mockVault.mockMethod("limitUpper()", [80000]);
await mockVault.clearMockMethod("emergencyBurn(int24,int24,uint128)");

expect(await manager.prevDeviation()).to.eq(percFP("1.115790573530244260"));
expect(await manager.prevDeviation()).to.eq(percFP("1.111560295732100833"));
expect(await manager.isOverweightSpot()).to.eq(false);
await expect(manager.rebalance()).not.to.be.reverted;
expect(await manager.prevDeviation()).to.eq(percFP("0.913541191300990579"));
expect(await manager.prevDeviation()).to.eq(percFP("0.947216779268338333"));
});
});
});
Expand All @@ -381,7 +381,7 @@ describe("UsdcSpotManager", function () {
it("should not force rebalance", async function () {
const { manager, mockVault, mockPool } = await loadFixture(setupContracts);

await mockVault.mockMethod("getTwap()", [70500]);
await mockVault.mockMethod("getTwap()", [67800]);
await mockVault.mockMethod("limitLower()", [40000]);
await mockVault.mockMethod("limitUpper()", [45000]);
await mockVault.mockMethod("rebalance()", []);
Expand All @@ -399,7 +399,7 @@ describe("UsdcSpotManager", function () {
expect(await manager.prevDeviation()).to.eq("0");
expect(await manager.isOverweightSpot()).to.eq(true);
await expect(manager.rebalance()).not.to.be.reverted;
expect(await manager.prevDeviation()).to.eq(percFP("0.960377048978079093"));
expect(await manager.prevDeviation()).to.eq(percFP("0.947216779268338333"));
});
});
});
Expand All @@ -409,7 +409,7 @@ describe("UsdcSpotManager", function () {
it("should not force rebalance", async function () {
const { manager, mockVault, mockPool } = await loadFixture(setupContracts);

await mockVault.mockMethod("getTwap()", [72000]);
await mockVault.mockMethod("getTwap()", [66200]);
await mockVault.mockMethod("limitLower()", [40000]);
await mockVault.mockMethod("limitUpper()", [45000]);
await mockVault.mockMethod("period()", [86400]);
Expand All @@ -423,7 +423,7 @@ describe("UsdcSpotManager", function () {
await mockVault.clearMockCall("period()", []);
await mockVault.clearMockMethod("emergencyBurn(int24,int24,uint128)");

await mockVault.mockMethod("getTwap()", [71500]);
await mockVault.mockMethod("getTwap()", [66800]);
await mockVault.mockMethod("limitLower()", [75000]);
await mockVault.mockMethod("limitUpper()", [80000]);
await mockPool.mockCall(
Expand All @@ -433,10 +433,10 @@ describe("UsdcSpotManager", function () {
);
await mockVault.mockMethod("emergencyBurn(int24,int24,uint128)", []);

expect(await manager.prevDeviation()).to.eq(percFP("1.115790573530244260"));
expect(await manager.prevDeviation()).to.eq(percFP("1.111560295732100833"));
expect(await manager.isOverweightSpot()).to.eq(false);
await expect(manager.rebalance()).not.to.be.reverted;
expect(await manager.prevDeviation()).to.eq(percFP("1.061375478380992817"));
expect(await manager.prevDeviation()).to.eq(percFP("1.0468312037404625"));
});
});
});
Expand Down