diff --git a/README.md b/README.md index 642bb70..f1acc47 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,7 @@ It is a converter between `Mkr` and `Sky` (both ways). Using the `mint` and `bur **Note:** if one of the tokens removes `mint` capabilities to this contract, it means that the path which gives that token to the user won't be available. **Note 2:** In the MKR -> SKY conversion, if the user passes a `wad` amount not multiple of `rate`, it causes that a dusty value will be lost. + +### SupplySync + +A contract with permissionless functionality that syncs the SKY supply to include also the MKR supply (thus MKR acts as wrapper of SKY). diff --git a/deploy/SkyDeploy.sol b/deploy/SkyDeploy.sol index 96da375..e08f751 100644 --- a/deploy/SkyDeploy.sol +++ b/deploy/SkyDeploy.sol @@ -20,6 +20,7 @@ import { ScriptTools } from "dss-test/ScriptTools.sol"; import { Sky } from "src/Sky.sol"; import { MkrSky } from "src/MkrSky.sol"; +import { SupplySync } from "src/SupplySync.sol"; import { SkyInstance } from "./SkyInstance.sol"; @@ -46,4 +47,12 @@ library SkyDeploy { sky = address(new Sky()); ScriptTools.switchOwner(sky, deployer, owner); } + + function deploySupplySync( + address mkr, + address sky, + address owner + ) internal returns (address supplySync) { + supplySync = address(new SupplySync(mkr, sky, owner)); + } } diff --git a/deploy/SkyInit.sol b/deploy/SkyInit.sol index 90dae83..3207ba0 100644 --- a/deploy/SkyInit.sol +++ b/deploy/SkyInit.sol @@ -21,6 +21,7 @@ import { SkyInstance } from "./SkyInstance.sol"; interface SkyLike { function rely(address) external; + function allowance(address, address) external view returns (uint256); } interface MkrSkyLike { @@ -29,6 +30,11 @@ interface MkrSkyLike { function rate() external view returns (uint256); } +interface SupplySyncLike { + function mkr() external view returns (address); + function sky() external view returns (address); +} + interface MkrLike { function authority() external view returns (address); } @@ -54,4 +60,18 @@ library SkyInit { dss.chainlog.setAddress("SKY", instance.sky); dss.chainlog.setAddress("MKR_SKY", instance.mkrSky); } + + function initSupplySync( + DssInstance memory dss, + address supplySync + ) internal { + SkyLike sky = SkyLike(dss.chainlog.getAddress("SKY")); + + require(SupplySyncLike(supplySync).mkr() == dss.chainlog.getAddress("MCD_GOV"), "SkyInit/mkr-does-not-match"); + require(SupplySyncLike(supplySync).sky() == address(sky), "SkyInit/sky-does-not-match"); + require(sky.allowance(supplySync, dss.chainlog.getAddress("MCD_PAUSE_PROXY")) == type(uint256).max, "SkyInit/allowance-not-set"); + + sky.rely(supplySync); + dss.chainlog.setAddress("SKY_SUPPLY_SYNC", supplySync); + } } diff --git a/src/SupplySync.sol b/src/SupplySync.sol new file mode 100644 index 0000000..5d93a35 --- /dev/null +++ b/src/SupplySync.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +/// MkrSky.sol -- Mkr/Sky Exchanger + +// Copyright (C) 2023 Dai Foundation +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity ^0.8.21; + +interface GemLike { + function totalSupply() external view returns (uint256); + function balanceOf(address) external view returns (uint256); + function approve(address, uint256) external; + function mint(address, uint256) external; + function burn(address, uint256) external; +} + +contract SupplySync { + GemLike public immutable mkr; + GemLike public immutable sky; + + constructor(address mkr_, address sky_, address owner) { + mkr = GemLike(mkr_); + sky = GemLike(sky_); + + // Allow owner (pause proxy) to burn the sky in this contract, if ever needed to wind down + sky.approve(owner, type(uint256).max); + } + + function sync() external { + uint256 mkrSupplyInSky = mkr.totalSupply() * 24_000; + uint256 skyBalance = sky.balanceOf(address(this)); + + unchecked { + if (mkrSupplyInSky > skyBalance) { + sky.mint(address(this), mkrSupplyInSky - skyBalance); + } else { + sky.burn(address(this), skyBalance - mkrSupplyInSky); + } + } + } +}