Skip to content

Commit

Permalink
base repayer contract fix
Browse files Browse the repository at this point in the history
  • Loading branch information
ProblematicP committed Nov 5, 2024
1 parent 59f5298 commit 2ee1b3f
Show file tree
Hide file tree
Showing 6 changed files with 462 additions and 29 deletions.
48 changes: 35 additions & 13 deletions contracts/debt-repayer-base-andromeda/src/DebtRepayer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,58 @@ pragma solidity ^0.8.21;

import {ISynthetixCore} from "./lib/ISynthetixCore.sol";
import {ISpotMarket} from "./lib/ISpotMarket.sol";
import {IUSDToken} from "./lib/IUSDToken.sol";
import {IUSDToken} from "./lib/IUSDToken.sol"; // This is currently used as IERC20. Should use IERC20 instead.
import {ERC2771Context} from "./lib/ERC2771Context.sol";
import {IAccountProxy} from "./lib/IAccountProxy.sol";

/**
* Tiny contract which deposits the needed amount of collateral into synthetix v3 account for debt repayment
* Front end helper contract which repays debt through collateral in your account or wallet
*/
contract DebtRepayer {

error NoDebtToRepay();

function depositDebtToRepay(
address coreProxyAddress,
address spotMarketAddress,
address accountProxyAddress,
uint128 accountId,
uint128 poolId,
address collateralType,
uint128 spotMarketId
) public {
address msgSender = ERC2771Context._msgSender();
IAccountProxy accountProxy = IAccountProxy(accountProxyAddress);
accountProxy.transferFrom(msgSender, address(this), uint256(accountId));

ISynthetixCore coreProxy = ISynthetixCore(coreProxyAddress);
int256 debt = coreProxy.getPositionDebt(accountId, poolId, collateralType);

if (debt > 0) {
ISpotMarket spotMarket = ISpotMarket(spotMarketAddress);
(uint256 neededSynth,) = spotMarket.quoteSellExactOut(spotMarketId, uint256(debt), 0);
(address toWrapToken,) = spotMarket.getWrapper(spotMarketId);
uint256 toWrapTokenDecimals = IUSDToken(toWrapToken).decimals();
uint256 toWrapTokenAmount = neededSynth * (10 ** toWrapTokenDecimals) / (10 ** 18) + 1;
IUSDToken(toWrapToken).transferFrom(msgSender, address(this), uint256(toWrapTokenAmount));
IUSDToken(toWrapToken).approve(address(spotMarket), toWrapTokenAmount);
spotMarket.wrap(spotMarketId, toWrapTokenAmount, neededSynth);
spotMarket.sellExactOut(spotMarketId, uint256(debt), neededSynth, address(0));
IUSDToken(coreProxy.getUsdToken()).approve(address(coreProxy), uint256(debt));
coreProxy.deposit(accountId, coreProxy.getUsdToken(), uint256(debt));
uint256 depositedAmount = coreProxy.getAccountAvailableCollateral(accountId, coreProxy.getUsdToken());

if (uint256(debt) > depositedAmount) {
uint256 remainingDebt = uint256(debt) - depositedAmount;

ISpotMarket spotMarket = ISpotMarket(spotMarketAddress);
(uint256 neededSynth,) = spotMarket.quoteSellExactOut(spotMarketId, remainingDebt, 0);
(address toWrapToken,) = spotMarket.getWrapper(spotMarketId);
uint256 toWrapTokenAmount = neededSynth * (10 ** IUSDToken(toWrapToken).decimals()) / (10 ** 18) + 1; // Assumption: All synths are 18 decimals

IUSDToken(toWrapToken).transferFrom(msgSender, address(this), uint256(toWrapTokenAmount));
IUSDToken(toWrapToken).approve(address(spotMarket), toWrapTokenAmount);
spotMarket.wrap(spotMarketId, toWrapTokenAmount, neededSynth);
spotMarket.sellExactOut(spotMarketId, remainingDebt, neededSynth, address(0));

IUSDToken(coreProxy.getUsdToken()).approve(address(coreProxy), remainingDebt);
coreProxy.deposit(accountId, coreProxy.getUsdToken(), remainingDebt);
}

coreProxy.burnUsd(accountId, poolId, collateralType, uint256(debt));
} else if (debt < 0) {
coreProxy.mintUsd(accountId, poolId, collateralType, uint256(-debt));
}

accountProxy.transferFrom(address(this), msgSender, uint256(accountId));
}
}
63 changes: 63 additions & 0 deletions contracts/debt-repayer-base-andromeda/src/lib/IAccountProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IAccountProxy {
error ImplementationIsSterile(address implementation);
error NoChange();
error NotAContract(address contr);
error NotNominated(address addr);
error Unauthorized(address addr);
error UpgradeSimulationFailed();
error ZeroAddress();

event OwnerChanged(address oldOwner, address newOwner);
event OwnerNominated(address newOwner);
event Upgraded(address indexed self, address implementation);

function acceptOwnership() external;
function getImplementation() external view returns (address);
function nominateNewOwner(address newNominatedOwner) external;
function nominatedOwner() external view returns (address);
function owner() external view returns (address);
function renounceNomination() external;
function simulateUpgradeTo(address newImplementation) external;
function upgradeTo(address newImplementation) external;

error AlreadyInitialized();
error CannotSelfApprove(address addr);
error IndexOverrun(uint256 requestedIndex, uint256 length);
error InvalidOwner(address addr);
error InvalidParameter(string parameter, string reason);
error InvalidTransferRecipient(address addr);
error OverflowUint256ToUint128();
error TokenAlreadyMinted(uint256 id);
error TokenDoesNotExist(uint256 id);

event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

function approve(address to, uint256 tokenId) external;
function balanceOf(address holder) external view returns (uint256 balance);
function burn(uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function initialize(string memory tokenName, string memory tokenSymbol, string memory uri) external;
function isApprovedForAll(address holder, address operator) external view returns (bool);
function isInitialized() external view returns (bool);
function mint(address to, uint256 tokenId) external;
function name() external view returns (string memory);
function ownerOf(uint256 tokenId) external view returns (address);
function safeMint(address to, uint256 tokenId, bytes memory data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) external;
function setAllowance(uint256 tokenId, address spender) external;
function setApprovalForAll(address operator, bool approved) external;
function setBaseTokenURI(string memory uri) external;
function supportsInterface(bytes4 interfaceId) external view returns (bool);
function symbol() external view returns (string memory);
function tokenByIndex(uint256 index) external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
function tokenURI(uint256 tokenId) external view returns (string memory);
function totalSupply() external view returns (uint256);
function transferFrom(address from, address to, uint256 tokenId) external;
}
56 changes: 56 additions & 0 deletions contracts/debt-repayer-base-andromeda/test/BaseTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.0;

import "forge-std/src/Test.sol";

contract BaseTest is Test {
uint256 private seed;

function generateAddress(string memory _name, bool _isContract)
internal
returns (address)
{
return generateAddress(_name, _isContract, 0);
}

function generateAddress(string memory _name, bool _isContract, uint256 _ethBalance)
internal
returns (address newAddress_)
{
seed++;
newAddress_ = vm.addr(seed);

vm.label(newAddress_, _name);

if (_isContract) {
vm.etch(newAddress_, "Generated Contract Address");
}

vm.deal(newAddress_, _ethBalance);

return newAddress_;
}

function assertEqTolerance(
uint256 a,
uint256 b,
uint256 tolerancePercentage //4 decimals
) internal {
uint256 diff = b > a ? b - a : a - b;
uint256 maxForgivness = (b * tolerancePercentage) / 100_000;

if (maxForgivness < diff) {
emit log("Error: a == b not satisfied [with tolerance]");
emit log_named_uint(" A", a);
emit log_named_uint(" B", b);
emit log_named_uint(" Max tolerance", maxForgivness);
emit log_named_uint(" Actual Difference", diff);
fail();
}
}

// Makes expecting a function to not be called more explicit instead of calling same function and passing 0 in count
function expectNoCall(address target, bytes memory data) internal {
vm.expectCall(target, data, 0);
}
}
16 changes: 0 additions & 16 deletions contracts/debt-repayer-base-andromeda/test/DebtRepayerTest.sol

This file was deleted.

Loading

0 comments on commit 2ee1b3f

Please sign in to comment.