Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unsigned calculation, fix deployments #9

Merged
merged 2 commits into from
Apr 14, 2024
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
14 changes: 7 additions & 7 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fuzz = { runs = 1_000 }
gas_reports = ["*"]
libs = ["lib"]
# optimizer = true (default)
optimizer_runs = 100
optimizer_runs = 200
fs_permissions = [{ access = "read-write", path = "./" }]
solc = "0.8.20"

Expand All @@ -23,25 +23,25 @@ quote_style = "double"
tab_width = 4
wrap_comments = true

# [etherscan]
# arbitrum_one = { key = "${API_KEY_ARBISCAN}" }
[etherscan]
arbitrum_one = { key = "${API_KEY_ARBISCAN}" }
# avalanche = { key = "${API_KEY_SNOWTRACE}" }
# bnb_smart_chain = { key = "${API_KEY_BSCSCAN}" }
# gnosis_chain = { key = "${API_KEY_GNOSISSCAN}" }
# goerli = { key = "${API_KEY_ETHERSCAN}" }
# mainnet = { key = "${API_KEY_ETHERSCAN}" }
mainnet = { key = "${API_KEY_ETHERSCAN}" }
# optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" }
# polygon = { key = "${API_KEY_POLYGONSCAN}" }
# sepolia = { key = "${API_KEY_ETHERSCAN}" }

# [rpc_endpoints]
# arbitrum_one = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}"
[rpc_endpoints]
arbitrum_one = "https://arb-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}"
# avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}"
# bnb_smart_chain = "https://bsc-dataseed.binance.org"
# gnosis_chain = "https://rpc.gnosischain.com"
# goerli = "https://goerli.infura.io/v3/${API_KEY_INFURA}"
# localhost = "http://localhost:8545"
# mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}"
mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}"
# optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}"
# polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}"
# sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}"
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
},
"private": true,
"scripts": {
"clean": "rm -rf cache out",
"clean": "rm -rf cache out broadcast",
"lint": "yarn lint:sol && yarn prettier:write",
"lint:sol": "forge fmt && yarn solhint src/**/*.sol",
"prettier:check": "prettier --check **/*.{json,md,yml} --ignore-path=.prettierignore",
"prettier:write": "prettier --write **/*.{json,md,yml} --ignore-path=.prettierignore"
}
}
}
12 changes: 10 additions & 2 deletions script/Swap_Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { SwapFactory } from "@tenderize/swap/Factory.sol";
import { UD60x18 } from "@prb/math/UD60x18.sol";
import { ERC1967Proxy } from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol";

// TENDERIZE POOLS BASE FEE = 0.025% with K=4
// EXOTIC POOLS BASE FEE = 0.1% with K=4

address constant FACTORY = address(0);

contract Swap_Deploy is Script {
Expand All @@ -19,9 +22,14 @@ contract Swap_Deploy is Script {
// Start broadcasting with private key from `.env` file
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address underlying = vm.envAddress("UNDERLYING");
UD60x18 BASE_FEE = UD60x18.wrap(vm.envUint("BASE_FEE"));
UD60x18 K = UD60x18.wrap(vm.envUint("K"));

// TENDERIZE POOLS
UD60x18 BASE_FEE = UD60x18.wrap(0.0005e18); // 0.05%
UD60x18 K = UD60x18.wrap(5e18);

// EXOTIC POOLS
// UD60x18 BASE_FEE = UD60x18.wrap(0.001e18); // 0.1%
// UD60x18 K = UD60x18.wrap(4e18);
ConstructorConfig cfg = ConstructorConfig({ UNDERLYING: ERC20(underlying), BASE_FEE: BASE_FEE, K: K });

function run() public {
Expand Down
67 changes: 36 additions & 31 deletions src/Swap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ contract TenderSwap is Initializable, UUPSUpgradeable, OwnableUpgradeable, SwapS
UD60x18 public immutable K;

// Minimum cut of the fee for LPs when an unlock is bought
UD60x18 public constant MIN_LP_CUT = UD60x18.wrap(0.05e18);
UD60x18 public constant MIN_LP_CUT = UD60x18.wrap(0.1e18); // 10%
// Cut of the fee for the treasury when an unlock is bought or redeemed
UD60x18 public constant TREASURY_CUT = UD60x18.wrap(0.01e18);
UD60x18 public constant TREASURY_CUT = UD60x18.wrap(0.1e18); // 10%
// Cut of the fee for the relayer when an unlock is redeemed
UD60x18 public constant RELAYER_CUT = UD60x18.wrap(0.01e18);
UD60x18 public constant RELAYER_CUT = UD60x18.wrap(0.025e18); // 2.5%

function initialize() public initializer {
Data storage $ = _loadStorageSlot();
Expand Down Expand Up @@ -504,46 +504,51 @@ contract TenderSwap is Initializable, UUPSUpgradeable, OwnableUpgradeable, SwapS

function _quote(uint256 amount, SwapParams memory p) internal view returns (uint256 out, uint256 fee) {
Data storage $ = _loadStorageSlot();
UD60x18 x = ud(amount);
UD60x18 nom = _calculateNominator(x, p, $);
UD60x18 denom = _calculateDenominator(p);

UD60x18 x = ud((amount));
UD60x18 L = ud(($.liabilities));
UD60x18 nom;
UD60x18 denom;

// (((u + x)*k - U + u)*((U + x)/L)**k + (-k*u + U - u)*(U/L)**k)*(S + U)/(k*(1 + k)*(s + u))

// in this formula (-k*u + U -u) can be rewritten as U-(k+1)*u
// if U < (k+1)*u then we must do (k+1)*u - U and subtract that from the first part of the sum in the nominator
// else we use the initial formula

{
UD60x18 sumA = p.u.add(x);
sumA = sumA.mul(K).sub(p.U).add(p.u);
sumA = sumA.mul(p.U.add(x).div(L).pow(K));
fee = BASE_FEE.mul(x).add(nom.div(denom)).unwrap();
fee = fee >= amount ? amount : fee;
unchecked {
out = amount - fee;
}
}

UD60x18 negator = K.add(UNIT_60x18).mul(p.u);
if (p.U < negator) {
UD60x18 sumB = negator.sub(p.U).mul(p.U.div(L).pow(K));
function _calculateNominator(UD60x18 x, SwapParams memory p, Data storage $) internal view returns (UD60x18 nom) {
UD60x18 L = ud($.liabilities);
UD60x18 sumA = p.u.add(x).mul(K).add(p.u);
UD60x18 negatorB = K.add(UNIT_60x18).mul(p.u);
UD60x18 util = p.U.div(L).pow(K);
UD60x18 util_change = p.U.add(x).div(L).pow(K);

if (sumA < p.U) {
sumA = p.U.sub(sumA).mul(util_change);
// we must subtract sumA from sumB
// we know sumB must always be positive so we
// can proceed with the regular calculation
UD60x18 sumB = p.U.sub(negatorB).mul(util);
nom = sumB.sub(sumA).mul(p.S.add(p.U));
} else {
// sumA is positive, sumB can be positive or negative
sumA = sumA.sub(p.U).mul(util_change);
if (p.U < negatorB) {
UD60x18 sumB = negatorB.sub(p.U).mul(util);
nom = sumA.sub(sumB).mul(p.S.add(p.U));
} else {
UD60x18 sumB = p.U.sub(negator).mul(p.U.div(L).pow(K));
UD60x18 sumB = p.U.sub(negatorB).mul(util);
nom = sumA.add(sumB).mul(p.S.add(p.U));
}

denom = K.mul(UNIT_60x18.add(K)).mul(p.s.add(p.u));
}
UD60x18 baseFee = BASE_FEE.mul(x);
fee = baseFee.add(nom.div(denom)).unwrap();

fee = fee >= amount ? amount : fee;
unchecked {
out = amount - fee;
}
}

function _calculateDenominator(SwapParams memory p) internal view returns (UD60x18) {
return K.mul(UNIT_60x18.add(K)).mul(p.s.add(p.u));
}
/**
* @notice checks if an asset is a valid tenderizer for `UNDERLYING`
*/

function _isValidAsset(address asset) internal view returns (bool) {
return REGISTRY.isTenderizer(asset) && Tenderizer(asset).asset() == address(UNDERLYING);
}
Expand Down
Loading