From e78089d97e2512655fd15d36b2353fd3de67a33c Mon Sep 17 00:00:00 2001 From: KillariDev Date: Fri, 13 Feb 2026 14:49:55 +0200 Subject: [PATCH 1/8] security pool forker --- solidity/contracts/peripherals/Auction.sol | 6 +- .../PriceOracleManagerAndOperatorQueuer.sol | 2 +- .../contracts/peripherals/SecurityPool.sol | 310 ++++++--------- .../peripherals/SecurityPoolForker.sol | 248 ++++++++++++ .../peripherals/SecurityPoolUtils.sol | 2 +- .../contracts/peripherals/YesNoMarkets.sol | 37 ++ .../peripherals/factories/AuctionFactory.sol | 2 +- ...eOracleManagerAndOperatorQueuerFactory.sol | 3 +- .../factories/SecurityPoolFactory.sol | 50 ++- .../factories/ShareTokenFactory.sol | 7 +- .../peripherals/interfaces/IAugur.sol | 2 +- .../peripherals/interfaces/ISecurityPool.sol | 52 +-- .../interfaces/ISecurityPoolForker.sol | 16 + .../peripherals/interfaces/IShareToken.sol | 46 +-- .../peripherals/interfaces/IWeth9.sol | 2 +- .../contracts/peripherals/tokens/ERC1155.sol | 2 +- .../peripherals/tokens/ForkedERC1155.sol | 25 +- .../peripherals/tokens/ShareToken.sol | 61 +-- .../contracts/peripherals/tokens/TokenId.sol | 16 +- solidity/ts/tests/testPeripherals.ts | 373 +++++++++++------- .../utils/contracts/deployPeripherals.ts | 203 ++++++++++ .../simulator/utils/contracts/deployments.ts | 141 +++++++ .../simulator/utils/contracts/peripherals.ts | 277 +++++++++++++ .../utils/contracts/peripheralsTestUtils.ts | 86 ++++ .../simulator/utils/contracts/securityPool.ts | 228 +++++++++++ .../utils/contracts/securityPoolForker.ts | 134 +++++++ .../ts/testsuite/simulator/utils/testUtils.ts | 10 + .../testsuite/simulator/utils/typescript.ts | 74 ++-- .../ts/testsuite/simulator/utils/utilities.ts | 174 +------- 29 files changed, 1921 insertions(+), 668 deletions(-) create mode 100644 solidity/contracts/peripherals/SecurityPoolForker.sol create mode 100644 solidity/contracts/peripherals/YesNoMarkets.sol create mode 100644 solidity/contracts/peripherals/interfaces/ISecurityPoolForker.sol create mode 100644 solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts create mode 100644 solidity/ts/testsuite/simulator/utils/contracts/deployments.ts create mode 100644 solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts create mode 100644 solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts create mode 100644 solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts create mode 100644 solidity/ts/testsuite/simulator/utils/contracts/securityPoolForker.ts create mode 100644 solidity/ts/testsuite/simulator/utils/testUtils.ts diff --git a/solidity/contracts/peripherals/Auction.sol b/solidity/contracts/peripherals/Auction.sol index 265c981..f62d18c 100644 --- a/solidity/contracts/peripherals/Auction.sol +++ b/solidity/contracts/peripherals/Auction.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; uint256 constant AUCTION_TIME = 1 weeks; @@ -39,12 +39,12 @@ contract Auction { emit AuctionStarted(ethAmountToBuy, repAvailable); } - function finalizeAuction() public { + function finalizeAuction(address receiver) public { //require(block.timestamp > auctionStarted + AUCTION_TIME, 'Auction needs to have ended first'); // caller checks require(msg.sender == owner, 'Only owner can finalize'); require(!finalized, 'Already finalized'); finalized = true; - (bool sent, ) = payable(owner).call{value: address(this).balance}(''); + (bool sent, ) = payable(receiver).call{value: address(this).balance}(''); require(sent, 'Failed to send Ether'); } } diff --git a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol index bfc8d9e..02db81c 100644 --- a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol +++ b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; import { IWeth9 } from './interfaces/IWeth9.sol'; diff --git a/solidity/contracts/peripherals/SecurityPool.sol b/solidity/contracts/peripherals/SecurityPool.sol index 1ab9b5a..219a2a4 100644 --- a/solidity/contracts/peripherals/SecurityPool.sol +++ b/solidity/contracts/peripherals/SecurityPool.sol @@ -1,35 +1,42 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; import { Auction } from './Auction.sol'; -import { Zoltar } from '../Zoltar.sol'; +import { Zoltar, FORK_TRESHOLD_DIVISOR } from '../Zoltar.sol'; import { ReputationToken } from '../ReputationToken.sol'; import { IShareToken } from './interfaces/IShareToken.sol'; import { PriceOracleManagerAndOperatorQueuer, QueuedOperation } from './PriceOracleManagerAndOperatorQueuer.sol'; import { ISecurityPool, SecurityVault, SystemState, QuestionOutcome, ISecurityPoolFactory } from './interfaces/ISecurityPool.sol'; import { OpenOracle } from './openOracle/OpenOracle.sol'; import { SecurityPoolUtils } from './SecurityPoolUtils.sol'; +import { EscalationGameFactory } from './factories/EscalationGameFactory.sol'; +import { EscalationGame } from './EscalationGame.sol'; +import { YesNoMarkets } from './YesNoMarkets.sol'; +import { SecurityPoolForker } from './SecurityPoolForker.sol'; +import { ISecurityPoolForker } from './interfaces/ISecurityPoolForker.sol'; + +uint256 constant TODO_INITIAL_ESCALATION_GAME_DEPOSIT = 1 ether; // todo, how to get this value? // Security pool for one question, one universe, one denomination (ETH) contract SecurityPool is ISecurityPool { - uint56 public immutable questionId; - uint192 public immutable universeId; + uint256 public immutable marketId; + uint248 public immutable universeId; Zoltar public immutable zoltar; ISecurityPool immutable public parent; IShareToken public immutable shareToken; - Auction public immutable truthAuction; - ISecurityPoolFactory public immutable securityPoolFactory; ReputationToken public immutable repToken; PriceOracleManagerAndOperatorQueuer public immutable priceOracleManagerAndOperatorQueuer; OpenOracle public immutable openOracle; + EscalationGameFactory public immutable escalationGameFactory; + EscalationGame public escalationGame; + YesNoMarkets public yesNoMarkets; + address public securityPoolForker; + ISecurityPoolFactory public securityPoolFactory; - uint256 public securityBondAllowance; - uint256 public auctionedSecurityBondAllowance; + uint256 public totalSecurityBondAllowance; uint256 public completeSetCollateralAmount; // amount of eth that is backing complete sets, `address(this).balance - completeSetCollateralAmount` are the fees belonging to REP pool holders uint256 public poolOwnershipDenominator; - uint256 public repAtFork; - uint256 public migratedRep; uint256 public securityMultiplier; uint256 public shareTokenSupply; @@ -39,25 +46,14 @@ contract SecurityPool is ISecurityPool { uint256 public currentRetentionRate; mapping(address => SecurityVault) public securityVaults; - mapping(address => bool) public claimedAuctionProceeds; - - ISecurityPool[3] public children; - uint256 public truthAuctionStarted; SystemState public systemState; event SecurityBondAllowanceChange(address vault, uint256 from, uint256 to); event PerformWithdrawRep(address vault, uint256 amount); event PoolRetentionRateChanged(uint256 retentionRate); - event ForkSecurityPool(uint256 repAtFork); - event MigrateVault(address vault, QuestionOutcome outcome, uint256 poolOwnership, uint256 securityBondAllowance); - event TruthAuctionStarted(uint256 completeSetCollateralAmount, uint256 repMigrated, uint256 repAtFork); - event TruthAuctionFinalized(); - event ClaimAuctionProceeds(address vault, uint256 amount, uint256 poolOwnershipAmount, uint256 poolOwnershipDenominator); - event MigrateRepFromParent(address vault, uint256 parentSecurityBondAllowance, uint256 parentpoolOwnership); event DepositRep(address vault, uint256 repAmount, uint256 poolOwnership); event RedeemShares(address redeemer, uint256 sharesAmount, uint256 ethValue); - event FinalizeAuction(uint256 repAvailable, uint256 migratedRep, uint256 repPurchased, uint256 poolOwnershipDenominator); event UpdateVaultFees(address vault, uint256 feeIndex, uint256 unpaidEthFees); event RedeemFees(address vault, uint256 fees); event UpdateCollateralAmount(uint256 totalFeesOvedToVaults, uint256 completeSetCollateralAmount); @@ -65,30 +61,43 @@ contract SecurityPool is ISecurityPool { event PerformLiquidation(address callerVault, address targetVaultAddress, uint256 debtAmount, uint256 debtToMove, uint256 repToMove); event RedeemRep(address caller, address vault, uint256 repAmount); - modifier isOperational { - (,, uint256 forkTime) = zoltar.universes(universeId); - require(forkTime == 0, 'Zoltar has forked'); + modifier isOperational { // todo, system can be operational if the fork has happened after this market has finalized + require(zoltar.getForkTime(universeId) == 0, 'Zoltar has forked'); require(systemState == SystemState.Operational, 'System is not operational'); _; } - constructor(ISecurityPoolFactory _securityPoolFactory, Auction _truthAuction, PriceOracleManagerAndOperatorQueuer _priceOracleManagerAndOperatorQueuer, IShareToken _shareToken, OpenOracle _openOracle, ISecurityPool _parent, Zoltar _zoltar, uint192 _universeId, uint56 _questionId, uint256 _securityMultiplier) { + modifier onlyValidOracle { + require(msg.sender == address(priceOracleManagerAndOperatorQueuer), 'OnlyOracle'); + require(priceOracleManagerAndOperatorQueuer.isPriceValid(), 'Stale price'); + _; + } + + modifier onlyForker { + require(msg.sender == securityPoolForker, 'Only Forker'); + _; + } + + constructor(address _securityPoolForker, ISecurityPoolFactory _securityPoolFactory, YesNoMarkets _yesNoMarkets, EscalationGameFactory _escalationGameFactory, PriceOracleManagerAndOperatorQueuer _priceOracleManagerAndOperatorQueuer, IShareToken _shareToken, OpenOracle _openOracle, ISecurityPool _parent, Zoltar _zoltar, uint248 _universeId, uint256 _marketId, uint256 _securityMultiplier) { universeId = _universeId; securityPoolFactory = _securityPoolFactory; - questionId = _questionId; + marketId = _marketId; securityMultiplier = _securityMultiplier; zoltar = _zoltar; parent = _parent; openOracle = _openOracle; - truthAuction = _truthAuction; + escalationGameFactory = _escalationGameFactory; priceOracleManagerAndOperatorQueuer = _priceOracleManagerAndOperatorQueuer; + securityPoolForker = _securityPoolForker; + yesNoMarkets = _yesNoMarkets; if (address(parent) == address(0x0)) { // origin universe never does truthAuction systemState = SystemState.Operational; } else { systemState = SystemState.ForkMigration; } shareToken = _shareToken; - (repToken, , ) = zoltar.universes(universeId); + repToken = zoltar.getRepToken(universeId); + repToken.approve(address(zoltar), type(uint256).max); } function setStartingParams(uint256 _currentRetentionRate, uint256 _repEthPrice, uint256 _completeSetCollateralAmount) public { @@ -100,19 +109,19 @@ contract SecurityPool is ISecurityPool { } function updateCollateralAmount() public { - if (securityBondAllowance == 0) return; - uint256 forkTime; - (,,forkTime) = zoltar.universes(universeId); - (uint64 endTime,,,) = zoltar.questions(questionId); + if (totalSecurityBondAllowance == 0) return; + uint256 forkTime = zoltar.getForkTime(universeId); + uint256 endTime = yesNoMarkets.getMarketEndDate(marketId); uint256 feeEndDate = forkTime == 0 ? endTime : forkTime; uint256 clampedCurrentTimestamp = block.timestamp > feeEndDate ? feeEndDate : block.timestamp; + if (lastUpdatedFeeAccumulator > clampedCurrentTimestamp) return; // todo, this probably souldnt here as we shouldn't be getting any fees? uint256 timeDelta = clampedCurrentTimestamp - lastUpdatedFeeAccumulator; if (timeDelta == 0) return; uint256 newCompleteSetCollateralAmount = completeSetCollateralAmount * SecurityPoolUtils.rpow(currentRetentionRate, timeDelta, SecurityPoolUtils.PRICE_PRECISION) / SecurityPoolUtils.PRICE_PRECISION; uint256 delta = completeSetCollateralAmount - newCompleteSetCollateralAmount; totalFeesOvedToVaults += delta; - feeIndex += delta * SecurityPoolUtils.PRICE_PRECISION / securityBondAllowance; + feeIndex += delta * SecurityPoolUtils.PRICE_PRECISION / totalSecurityBondAllowance; completeSetCollateralAmount = newCompleteSetCollateralAmount; lastUpdatedFeeAccumulator = feeEndDate < block.timestamp ? feeEndDate : block.timestamp; @@ -120,9 +129,9 @@ contract SecurityPool is ISecurityPool { } function updateRetentionRate() public { - if (securityBondAllowance == 0) return; + if (totalSecurityBondAllowance == 0) return; if (systemState != SystemState.Operational) return; // if system state is not operational do not change fees - currentRetentionRate = SecurityPoolUtils.calculateRetentionRate(completeSetCollateralAmount, securityBondAllowance); + currentRetentionRate = SecurityPoolUtils.calculateRetentionRate(completeSetCollateralAmount, totalSecurityBondAllowance); emit PoolRetentionRateChanged(currentRetentionRate); } @@ -147,16 +156,14 @@ contract SecurityPool is ISecurityPool { // withdrawing rep //////////////////////////////////////// - function performWithdrawRep(address vault, uint256 repAmount) public isOperational { - require(msg.sender == address(priceOracleManagerAndOperatorQueuer), 'only priceOracleManagerAndOperatorQueuer can call'); - require(priceOracleManagerAndOperatorQueuer.isPriceValid(), 'no valid price'); + function performWithdrawRep(address vault, uint256 repAmount) public isOperational onlyValidOracle { uint256 ownershipToWithdraw = repToPoolOwnership(repAmount); uint256 withdrawOwnership = ownershipToWithdraw + repToPoolOwnership(SecurityPoolUtils.MIN_REP_DEPOSIT) > securityVaults[vault].poolOwnership ? securityVaults[vault].poolOwnership : ownershipToWithdraw; uint256 withdrawRepAmount = poolOwnershipToRep(withdrawOwnership); uint256 oldRep = poolOwnershipToRep(securityVaults[vault].poolOwnership); require((oldRep - withdrawRepAmount) * SecurityPoolUtils.PRICE_PRECISION >= securityVaults[vault].securityBondAllowance * priceOracleManagerAndOperatorQueuer.lastPrice(), 'Local Security Bond Allowance broken'); - require((repToken.balanceOf(address(this)) - withdrawRepAmount) * SecurityPoolUtils.PRICE_PRECISION >= securityBondAllowance * priceOracleManagerAndOperatorQueuer.lastPrice(), 'Global Security Bond Allowance broken'); + require((repToken.balanceOf(address(this)) - withdrawRepAmount) * SecurityPoolUtils.PRICE_PRECISION >= totalSecurityBondAllowance * priceOracleManagerAndOperatorQueuer.lastPrice(), 'Global Security Bond Allowance broken'); securityVaults[vault].poolOwnership -= withdrawOwnership; poolOwnershipDenominator -= withdrawOwnership; @@ -182,8 +189,6 @@ contract SecurityPool is ISecurityPool { } function depositRep(uint256 repAmount) public isOperational { - QueuedOperation memory queuedOperation = priceOracleManagerAndOperatorQueuer.getQueuedOperation(); - require(queuedOperation.amount == 0 || queuedOperation.targetVault != msg.sender, 'operation pending'); // prevents owner from saving their vault when liquidation is pending uint256 poolOwnership = repToPoolOwnership(repAmount); repToken.transferFrom(msg.sender, address(this), repAmount); securityVaults[msg.sender].poolOwnership += poolOwnership; @@ -195,14 +200,12 @@ contract SecurityPool is ISecurityPool { //////////////////////////////////////// // liquidating vault //////////////////////////////////////// - + // TODO, currently liquidator can be blocked by someone by depositing rep to vault while the deposit is pending. We dont want to block depositReps for this duration thought as we want to allow people to participate escalation game using external rep. I feel after liquidation is triggered we should store a snapshot rep balance of the vault that is then used for liquidation calculations //price = (amount1 * PRICE_PRECISION) / amount2; // price = REP * PRICE_PRECISION / ETH // liquidation moves share of debt and rep to another pool which need to remain non-liquidable // this is currently very harsh, as we steal all the rep and debt from the pool - function performLiquidation(address callerVault, address targetVaultAddress, uint256 debtAmount) public isOperational { - require(msg.sender == address(priceOracleManagerAndOperatorQueuer), 'only priceOracleManagerAndOperatorQueuer can call'); - require(priceOracleManagerAndOperatorQueuer.isPriceValid(), 'no valid price'); + function performLiquidation(address callerVault, address targetVaultAddress, uint256 debtAmount) public isOperational onlyValidOracle { updateVaultFees(targetVaultAddress); updateVaultFees(callerVault); uint256 vaultsSecurityBondAllowance = securityVaults[targetVaultAddress].securityBondAllowance; @@ -214,7 +217,7 @@ contract SecurityPool is ISecurityPool { require(debtToMove > 0, 'no debt to move'); uint256 repToMove = debtToMove * vaultsRepDeposit / securityVaults[targetVaultAddress].securityBondAllowance; uint256 ownershipToMove = repToPoolOwnership(repToMove); - require((securityVaults[callerVault].securityBondAllowance + debtToMove) * securityMultiplier * repEthPrice <= (poolOwnershipToRep(securityVaults[callerVault].poolOwnership) + repToMove) * SecurityPoolUtils.PRICE_PRECISION, 'New pool would be liquidable!'); + require((securityVaults[callerVault].securityBondAllowance + debtToMove) * securityMultiplier * repEthPrice <= poolOwnershipToRep(securityVaults[callerVault].poolOwnership + ownershipToMove) * SecurityPoolUtils.PRICE_PRECISION, 'New pool would be liquidable!'); securityVaults[targetVaultAddress].securityBondAllowance -= debtToMove; securityVaults[targetVaultAddress].poolOwnership -= ownershipToMove; securityVaults[callerVault].securityBondAllowance += debtToMove; @@ -233,19 +236,17 @@ contract SecurityPool is ISecurityPool { // set security bond allowance //////////////////////////////////////// - function performSetSecurityBondsAllowance(address callerVault, uint256 amount) public isOperational { + function performSetSecurityBondsAllowance(address callerVault, uint256 amount) public isOperational onlyValidOracle { updateVaultFees(callerVault); - require(msg.sender == address(priceOracleManagerAndOperatorQueuer), 'only priceOracleManagerAndOperatorQueuer can call'); - require(priceOracleManagerAndOperatorQueuer.isPriceValid(), 'no valid price'); uint256 oldAllowance = securityVaults[callerVault].securityBondAllowance; - securityBondAllowance += amount; - securityBondAllowance -= oldAllowance; + totalSecurityBondAllowance += amount; + totalSecurityBondAllowance -= oldAllowance; securityVaults[callerVault].securityBondAllowance = amount; require(poolOwnershipToRep(securityVaults[callerVault].poolOwnership) * SecurityPoolUtils.PRICE_PRECISION > amount * priceOracleManagerAndOperatorQueuer.lastPrice()); - require(repToken.balanceOf(address(this)) * SecurityPoolUtils.PRICE_PRECISION > securityBondAllowance * priceOracleManagerAndOperatorQueuer.lastPrice()); - require(securityBondAllowance >= completeSetCollateralAmount, 'minted too many complete sets to allow this'); + require(repToken.balanceOf(address(this)) * SecurityPoolUtils.PRICE_PRECISION > totalSecurityBondAllowance * priceOracleManagerAndOperatorQueuer.lastPrice()); + require(totalSecurityBondAllowance >= completeSetCollateralAmount, 'minted too many complete sets to allow this'); require(securityVaults[callerVault].securityBondAllowance >= SecurityPoolUtils.MIN_SECURITY_BOND_DEBT || securityVaults[callerVault].securityBondAllowance == 0, 'min deposit requirement'); emit SecurityBondAllowanceChange(callerVault, oldAllowance, amount); updateRetentionRate(); @@ -254,11 +255,10 @@ contract SecurityPool is ISecurityPool { //////////////////////////////////////// // Complete Sets //////////////////////////////////////// - function createCompleteSet() payable public isOperational { + function createCompleteSet() payable public isOperational { // todo, we want to be able to create complete sets in the children right away, figure accounting out require(msg.value > 0, 'need to send eth'); - require(systemState == SystemState.Operational, 'system is not Operational'); // todo, we want to be able to create complete sets in the children right away, figure accounting out updateCollateralAmount(); - require(securityBondAllowance >= msg.value + completeSetCollateralAmount, 'no capacity to create that many sets'); + require(totalSecurityBondAllowance >= msg.value + completeSetCollateralAmount, 'no capacity to create that many sets'); uint256 completeSetsToMint = cashToShares(msg.value); shareToken.mintCompleteSets(universeId, msg.sender, completeSetsToMint); shareTokenSupply += completeSetsToMint; @@ -267,8 +267,7 @@ contract SecurityPool is ISecurityPool { updateRetentionRate(); } - function redeemCompleteSet(uint256 completeSetAmount) public isOperational { - require(systemState == SystemState.Operational, 'system is not Operational'); // todo, we want to allow people to exit, but for accounting purposes that is difficult but maybe there's a way? + function redeemCompleteSet(uint256 completeSetAmount) public isOperational { // todo, we want to allow people to exit, but for accounting purposes that is difficult but maybe there's a way? updateCollateralAmount(); // takes in complete set and releases security bond and eth uint256 ethValue = sharesToCash(completeSetAmount); @@ -281,8 +280,8 @@ contract SecurityPool is ISecurityPool { } function redeemShares() isOperational external { - Zoltar.Outcome outcome = zoltar.finalizeQuestion(universeId, questionId); - require(outcome != Zoltar.Outcome.None, 'Question has not finalized!'); + YesNoMarkets.Outcome outcome = ISecurityPoolForker(securityPoolForker).getMarketOutcome(this); + require(outcome != YesNoMarkets.Outcome.None, 'Market has not finalized!'); uint256 tokenId = shareToken.getTokenId(universeId, outcome); uint256 amount = shareToken.burnTokenId(tokenId, msg.sender); uint256 ethValue = sharesToCash(amount); @@ -292,147 +291,98 @@ contract SecurityPool is ISecurityPool { } function redeemRep(address vault) public { - Zoltar.Outcome outcome = zoltar.finalizeQuestion(universeId, questionId); - require(outcome != Zoltar.Outcome.None, 'Question has not finalized!'); + require(ISecurityPoolForker(securityPoolForker).getMarketOutcome(this) != YesNoMarkets.Outcome.None, 'Market has not finalized!'); updateVaultFees(vault); - uint256 repAmount = poolOwnershipToRep(securityVaults[vault].poolOwnership); + uint256 repAmount = poolOwnershipToRep(securityVaults[vault].poolOwnership) - securityVaults[vault].lockedRepInEscalationGame; securityVaults[vault].poolOwnership = 0; repToken.transfer(vault, repAmount); emit RedeemRep(msg.sender, vault, repAmount); } //////////////////////////////////////// - // FORKING (migrate vault (oi+rep), truth truthAuction) + // Escalation Game (migrate vault (oi+rep), truth truthAuction) //////////////////////////////////////// - function forkSecurityPool() public { - (,, uint256 forkTime) = zoltar.universes(universeId); - require(forkTime > 0, 'Zoltar needs to have forked before Security Pool can do so'); - require(systemState == SystemState.Operational, 'System needs to be operational to trigger fork'); - require(!zoltar.isFinalized(universeId, questionId), 'question has been finalized already'); - systemState = SystemState.PoolForked; - updateCollateralAmount(); - currentRetentionRate = 0; - repAtFork = repToken.balanceOf(address(this)); - emit ForkSecurityPool(repAtFork); - repToken.approve(address(zoltar), repAtFork); - zoltar.splitRep(universeId); - // TODO: we could pay the caller basefee*2 out of Open interest we have to reward caller - } - - function createChildUniverse(QuestionOutcome outcome) public { - (,, uint256 forkTime) = zoltar.universes(universeId); - require(systemState == SystemState.PoolForked, 'Pool needs to have forked'); - require(block.timestamp <= forkTime + SecurityPoolUtils.MIGRATION_TIME, 'migration time passed'); - _createChildUniverse(outcome); - } - - function _createChildUniverse(QuestionOutcome outcome) private { - // first vault migrater creates new pool and transfers all REP to it - uint192 childUniverseId = (universeId << 2) + uint192(outcome) + 1; - uint256 retentionRate = SecurityPoolUtils.calculateRetentionRate(completeSetCollateralAmount, securityBondAllowance); - children[uint8(outcome)] = securityPoolFactory.deployChildSecurityPool(shareToken, childUniverseId, questionId, securityMultiplier, retentionRate, priceOracleManagerAndOperatorQueuer.lastPrice(), 0); - shareToken.authorize(children[uint8(outcome)]); - ReputationToken childReputationToken = children[uint8(outcome)].repToken(); - childReputationToken.transfer(address(children[uint8(outcome)]), childReputationToken.balanceOf(address(this))); - } - - // migrates vault into outcome universe after fork - function migrateVault(QuestionOutcome outcome) public { // called on parent - (,, uint256 forkTime) = zoltar.universes(universeId); - require(systemState == SystemState.PoolForked, 'Pool needs to have forked'); - require(block.timestamp <= forkTime + SecurityPoolUtils.MIGRATION_TIME , 'migration time passed'); - updateVaultFees(msg.sender); - emit MigrateVault(msg.sender, outcome, securityVaults[msg.sender].poolOwnership, securityVaults[msg.sender].securityBondAllowance); - if (address(children[uint8(outcome)]) == address(0x0)) { - _createChildUniverse(outcome); - } - children[uint256(outcome)].migrateRepFromParent(msg.sender); - // migrate open interest - if (poolOwnershipDenominator > 0 && securityVaults[msg.sender].poolOwnership > 0) { - (bool sent, ) = payable(children[uint256(outcome)]).call{ value: completeSetCollateralAmount * securityVaults[msg.sender].poolOwnership / poolOwnershipDenominator }(''); - require(sent, 'Failed to send Ether'); + function depositToEscalationGame(YesNoMarkets.Outcome outcome, uint256 maxAmount) external isOperational { + if (address(escalationGame) == address(0x0)) { + uint256 endTime = yesNoMarkets.getMarketEndDate(marketId); + require(block.timestamp > endTime, 'market has not ended'); + escalationGame = escalationGameFactory.deployEscalationGame(TODO_INITIAL_ESCALATION_GAME_DEPOSIT, repToken.getTotalTheoreticalSupply() / (FORK_TRESHOLD_DIVISOR * 2)); } - securityVaults[msg.sender].poolOwnership = 0; - securityVaults[msg.sender].securityBondAllowance = 0; + securityVaults[msg.sender].lockedRepInEscalationGame += escalationGame.depositOnOutcome(msg.sender, outcome, maxAmount); + require(poolOwnershipToRep(securityVaults[msg.sender].poolOwnership) >= securityVaults[msg.sender].lockedRepInEscalationGame, 'Not enough REP'); } - function migrateRepFromParent(address vault) public { // called on children - require(msg.sender == address(parent), 'only parent can migrate'); - updateVaultFees(vault); - parent.updateCollateralAmount(); - (uint256 parentPoolOwnership, uint256 parentSecurityBondAllowance, , ) = parent.securityVaults(vault); - emit MigrateRepFromParent(vault, parentSecurityBondAllowance, parentPoolOwnership); - securityVaults[vault].securityBondAllowance = parentSecurityBondAllowance; - securityBondAllowance += parentSecurityBondAllowance; - poolOwnershipDenominator = parent.repAtFork() * SecurityPoolUtils.PRICE_PRECISION; - if (parent.poolOwnershipDenominator() == 0 || repToken.balanceOf(address(this)) == 0) return; - - securityVaults[vault].poolOwnership = repToPoolOwnership(parentPoolOwnership * parent.repAtFork() / parent.poolOwnershipDenominator()); - migratedRep += poolOwnershipToRep(securityVaults[vault].poolOwnership); - securityVaults[vault].feeIndex = feeIndex; - // migrate completeset collateral amount incrementally as we want this portion to start paying fees right away, but stop paying fees in the parent system - } - - function startTruthAuction() public { - (,, uint256 forkTime) = zoltar.universes(universeId); - require(systemState == SystemState.ForkMigration, 'System needs to be in migration'); - require(block.timestamp > forkTime + SecurityPoolUtils.MIGRATION_TIME, 'migration time needs to pass first'); - systemState = SystemState.ForkTruthAuction; - truthAuctionStarted = block.timestamp; - parent.updateCollateralAmount(); - uint256 parentCollateral = parent.completeSetCollateralAmount(); - shareTokenSupply = parent.shareTokenSupply(); - emit TruthAuctionStarted(parentCollateral, migratedRep, parent.repAtFork()); - if (migratedRep >= parent.repAtFork()) { - // we have acquired all the ETH already, no need for truthAuction - _finalizeTruthAuction(0); - } else { - // we need to buy all the collateral that is missing (did not migrate) - uint256 ethToBuy = parentCollateral - parentCollateral * migratedRep / parent.repAtFork(); - // sell all but very small amount of REP for ETH. We cannot sell all for accounting purposes, as `poolOwnershipDenominator` cannot be infinite - // only migratedRep gets this guarrantee that some of their rep never gets sold - truthAuction.startAuction(ethToBuy, parent.repAtFork() - migratedRep / SecurityPoolUtils.MAX_AUCTION_VAULT_HAIRCUT_DIVISOR); + function withdrawFromEscalationGame(uint256[] memory depositIndexes) external isOperational { + require(address(escalationGame) != address(0x0), 'escalation game needs to be deployed'); + YesNoMarkets.Outcome outcome = ISecurityPoolForker(securityPoolForker).getMarketOutcome(this); + require(outcome != YesNoMarkets.Outcome.None, 'Market has not finalized!'); + require(!escalationGame.hasReachedNonDecision(), 'cannot withdraw, escalation game is undecisive'); + for (uint256 index = 0; index < depositIndexes.length; index++) { + (address depositor, uint256 amountToWithdraw) = escalationGame.withdrawDeposit(depositIndexes[index]); + securityVaults[depositor].poolOwnership += repToPoolOwnership(amountToWithdraw); } } - function _finalizeTruthAuction(uint256 repPurchased) private { - require(systemState == SystemState.ForkTruthAuction, 'Auction need to have started'); - truthAuction.finalizeAuction(); // this sends the eth back - systemState = SystemState.Operational; - uint256 repAvailable = parent.repAtFork(); - completeSetCollateralAmount = address(this).balance - totalFeesOvedToVaults; //todo, we might want to reduce fees if we didn't get fully funded? - if (repAvailable > 0) poolOwnershipDenominator = migratedRep * repAvailable * SecurityPoolUtils.PRICE_PRECISION / (repAvailable - repPurchased); - auctionedSecurityBondAllowance = parent.securityBondAllowance() - securityBondAllowance; - securityBondAllowance = parent.securityBondAllowance(); - if (poolOwnershipDenominator == 0) poolOwnershipDenominator = repAvailable * SecurityPoolUtils.PRICE_PRECISION; - emit FinalizeAuction(repAvailable, migratedRep, repPurchased, poolOwnershipDenominator); - updateRetentionRate(); + // todo, cleanup these only forker functions by minimizing amount and adding checks + function setSystemState(SystemState newState) external onlyForker { + systemState = newState; } - function finalizeTruthAuction() public { - require(block.timestamp > truthAuctionStarted + SecurityPoolUtils.AUCTION_TIME, 'truthAuction still ongoing'); - _finalizeTruthAuction(truthAuction.totalRepPurchased()); + function setRetentionRate(uint256 newRetention) external onlyForker { + currentRetentionRate = newRetention; } - receive() external payable { - // needed for Truth Auction to send ETH back + function setVaultOwnership(address vault, uint256 _poolOwnership, uint256 _securityBondAllowance) external onlyForker { + securityVaults[vault].poolOwnership = _poolOwnership; + securityVaults[vault].securityBondAllowance = _securityBondAllowance; + } + + function setVaultSecurityBondAllowance(address vault, uint256 _securityBondAllowance) external onlyForker { + securityVaults[vault].securityBondAllowance = _securityBondAllowance; + + } + function addToTotalSecurityBondAllowance(uint256 securityBondAllowanceDelta) external onlyForker { + totalSecurityBondAllowance += securityBondAllowanceDelta; + } + + function setPoolOwnershipDenominator(uint256 _poolOwnershipDenominator) external onlyForker { + poolOwnershipDenominator = _poolOwnershipDenominator; + } + + function setVaultPoolOwnership(address vault, uint256 poolOwnership) external onlyForker { + securityVaults[vault].poolOwnership = poolOwnership; + } + + function setVaultFeeIndex(address vault, uint256 newFeeIndex) external onlyForker { + securityVaults[vault].feeIndex = newFeeIndex; + } + + function setShareTokenSupply(uint256 newShareTokenSupply) external onlyForker { + shareTokenSupply = newShareTokenSupply; + } + + function setCompleteSetCollateralAmount(uint256 newCompleteSetCollateralAmount) external onlyForker { + completeSetCollateralAmount = newCompleteSetCollateralAmount; + } + + function setTotalSecurityBondAllowance(uint256 newTotalSecurityBondAllowance) external onlyForker { + totalSecurityBondAllowance = newTotalSecurityBondAllowance; + } + + function stealAllRep() external onlyForker() { + repToken.transfer(msg.sender, repToken.balanceOf(address(this))); } - // accounts the purchased REP from truthAuction to the vault - // we should also move a share of bad debt in the system to this vault - // anyone can call these so that we can liquidate them if needed - function claimAuctionProceeds(address vault) public { - require(claimedAuctionProceeds[vault] == false, 'Already Claimed'); - require(truthAuction.finalized(), 'Auction needs to be finalized'); - claimedAuctionProceeds[vault] = true; - uint256 amount = truthAuction.purchasedRep(vault); - require(amount > 0, 'Did not purchase anything'); // not really necessary, but good for testing - uint256 poolOwnershipAmount = repToPoolOwnership(amount); - securityVaults[vault].poolOwnership += poolOwnershipAmount; // no need to add to poolOwnershipDenominator as its already accounted - emit ClaimAuctionProceeds(vault, amount, poolOwnershipAmount, poolOwnershipDenominator); - securityVaults[vault].securityBondAllowance = auctionedSecurityBondAllowance * amount / truthAuction.totalRepPurchased(); + function migrateEth(address payable receiver, uint256 amount) external onlyForker() { + (bool sent, ) = receiver.call{ value: amount }(''); + require(sent, 'Failed to steal ETH'); + } + function authorize(ISecurityPool pool) external onlyForker() { + shareToken.authorize(pool); } - // todo, fee calculation doesn't work yet accross forks + receive() external payable { + // needed for Truth Auction to send ETH back + } } diff --git a/solidity/contracts/peripherals/SecurityPoolForker.sol b/solidity/contracts/peripherals/SecurityPoolForker.sol new file mode 100644 index 0000000..8731a70 --- /dev/null +++ b/solidity/contracts/peripherals/SecurityPoolForker.sol @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.33; + +import { ReputationToken } from '../ReputationToken.sol'; +import { Zoltar } from '../Zoltar.sol'; +import { Auction } from './Auction.sol'; +import { ISecurityPool, ISecurityPoolFactory, SystemState } from './interfaces/ISecurityPool.sol'; +import { IShareToken } from './interfaces/IShareToken.sol'; +import { EscalationGame } from './EscalationGame.sol'; +import { YesNoMarkets } from './YesNoMarkets.sol'; +import { SecurityPoolUtils } from './SecurityPoolUtils.sol'; +import { ISecurityPoolForker } from './interfaces/ISecurityPoolForker.sol'; + +struct ForkData { + uint256 repAtFork; + mapping(uint8 => ISecurityPool) children; // outcome -> children + Auction truthAuction; + mapping(address => bool) claimedAuctionProceeds; + uint256 truthAuctionStarted; + uint256 migratedRep; + uint256 auctionedSecurityBondAllowance; + + bool ownFork; + uint8 outcomeIndex; +} + +contract SecurityPoolForker is ISecurityPoolForker { + Zoltar public immutable zoltar; + YesNoMarkets public yesNoMarkets; + + mapping(ISecurityPool => ForkData) public forkData; + + event ForkSecurityPool(uint256 repAtFork); + event MigrateVault(address vault, uint8 outcome, uint256 poolOwnership, uint256 securityBondAllowance, uint256 parentLockedRepInEscalationGame); + event TruthAuctionStarted(uint256 completeSetCollateralAmount, uint256 repMigrated, uint256 repAtFork); + event TruthAuctionFinalized(); + event ClaimAuctionProceeds(address vault, uint256 amount, uint256 poolOwnershipAmount, uint256 poolOwnershipDenominator); + event MigrateRepFromParent(address vault, uint256 parentSecurityBondAllowance, uint256 parentpoolOwnership); + event FinalizeAuction(uint256 repAvailable, uint256 migratedRep, uint256 repPurchased, uint256 poolOwnershipDenominator, uint256 completeSetCollateralAmount); + event MigrateFromEscalationGame(ISecurityPool parent, address vault, YesNoMarkets.Outcome outcomeIndex, uint8[] depositIndexes, uint256 totalRep, uint256 newOwnership); + + function repToPoolOwnership(ISecurityPool securityPool, uint256 repAmount) public view returns (uint256) { + if (securityPool.poolOwnershipDenominator() == 0) return repAmount * SecurityPoolUtils.PRICE_PRECISION; + return repAmount * securityPool.poolOwnershipDenominator() / securityPool.repToken().balanceOf(address(securityPool)); + } + + function poolOwnershipToRep(ISecurityPool securityPool, uint256 poolOwnership) public view returns (uint256) { + return poolOwnership * securityPool.repToken().balanceOf(address(securityPool)) / securityPool.poolOwnershipDenominator(); + } + function getMigratedRep(ISecurityPool securityPool) public view returns (uint256) { + return forkData[securityPool].migratedRep; + } + + constructor(Zoltar _zoltar) { + zoltar = _zoltar; + } + + function forkSecurityPool(ISecurityPool securityPool) public { + uint248 universe = securityPool.universeId(); + EscalationGame escalationGame = securityPool.escalationGame(); + require(zoltar.getForkTime(universe) > 0, 'Zoltar needs to have forked before Security Pool can do so'); + require(securityPool.systemState() == SystemState.Operational, 'System is not operational'); + require(address(escalationGame) == address(0x0) || escalationGame.getMarketResolution() == YesNoMarkets.Outcome.None, 'question has been finalized already'); + securityPool.setSystemState(SystemState.PoolForked); + securityPool.updateCollateralAmount(); + securityPool.setRetentionRate(0); + ReputationToken rep = securityPool.repToken(); + securityPool.stealAllRep(); + forkData[securityPool].repAtFork = rep.balanceOf(address(this)); + + uint8[] memory outcomeIndices = new uint8[](4 + 1); + for (uint8 index = 0; index < outcomeIndices.length; index++) { + outcomeIndices[index] = index; + } + rep.approve(address(zoltar), type(uint256).max); + zoltar.splitRep(universe, outcomeIndices); + if (zoltar.getForkedBy(universe) == address(this)) { + forkData[securityPool].repAtFork += zoltar.getForkerDeposit(universe); + zoltar.forkerClaimRep(universe, outcomeIndices); + } + emit ForkSecurityPool(forkData[securityPool].repAtFork); + // TODO: we could pay the caller basefee*2 out of Open interest. We have to reward caller + } + + function createChildUniverse(ISecurityPool parent, uint8 outcomeIndex) public { + require(address(forkData[parent].children[outcomeIndex]) == address(0x0), 'child already created'); + require(parent.systemState() == SystemState.PoolForked, 'Pool needs to have forked'); + require(block.timestamp <= zoltar.getForkTime(parent.universeId()) + SecurityPoolUtils.MIGRATION_TIME , 'migration time passed'); + // first vault migrater creates new pool and transfers all REP to it + uint248 childUniverseId = uint248(uint256(keccak256(abi.encode(parent.universeId(), outcomeIndex)))); + uint256 retentionRate = SecurityPoolUtils.calculateRetentionRate(parent.completeSetCollateralAmount(), parent.totalSecurityBondAllowance()); + (ISecurityPool child, Auction truthAuction) = parent.securityPoolFactory().deployChildSecurityPool(parent, parent.shareToken(), childUniverseId, parent.marketId(), parent.securityMultiplier(), retentionRate, parent.priceOracleManagerAndOperatorQueuer().lastPrice(), 0); + forkData[child].outcomeIndex = outcomeIndex; + forkData[child].truthAuction = truthAuction; + forkData[parent].children[outcomeIndex] = child; + parent.authorize(child); + ReputationToken childReputationToken = child.repToken(); + childReputationToken.transfer(address(child), childReputationToken.balanceOf(address(this))); + + if (forkData[parent].ownFork) { + child.setPoolOwnershipDenominator(parent.poolOwnershipDenominator() * forkData[parent].repAtFork / (forkData[parent].repAtFork + parent.escalationGame().nonDecisionTreshold()*2/5) ); + } else { + child.setPoolOwnershipDenominator(parent.poolOwnershipDenominator()); + } + } + + // todo, atm this needs to be called after migratevault + function migrateFromEscalationGame(ISecurityPool parent, address vault, YesNoMarkets.Outcome outcomeIndex, uint8[] memory depositIndexes) public { + EscalationGame escalationGame = parent.escalationGame(); + if (address(forkData[parent].children[uint8(outcomeIndex)]) == address(0x0)) createChildUniverse(parent, uint8(outcomeIndex)); + ISecurityPool child = forkData[parent].children[uint8(outcomeIndex)]; + require(address(escalationGame) != address(0x0), 'escalation game needs to be deployed'); + uint256 repMigratedFromEscalationGame = 0; + for (uint256 index = 0; index < depositIndexes.length; index++) { + (address depositor, uint256 amountToWithdraw) = escalationGame.claimDepositForWinning(depositIndexes[index], outcomeIndex); + require(depositor == vault, 'deposit was not for this vault'); + repMigratedFromEscalationGame += amountToWithdraw; + } + (uint256 poolOwnership, , , , ) = child.securityVaults(vault); + uint256 ownershipDelta = repToPoolOwnership(child, repMigratedFromEscalationGame); + child.setVaultPoolOwnership(msg.sender, poolOwnership + ownershipDelta); + forkData[child].migratedRep += repMigratedFromEscalationGame; + emit MigrateFromEscalationGame(parent, vault, outcomeIndex, depositIndexes, repMigratedFromEscalationGame, ownershipDelta); + // migrate open interest + parent.migrateEth(payable(child), parent.completeSetCollateralAmount() * repMigratedFromEscalationGame / forkData[parent].repAtFork); + + } + + // migrates vault into outcome universe after fork + function migrateVault(ISecurityPool parent, uint8 outcomeIndex) public { + parent.updateVaultFees(msg.sender); + if (address(forkData[parent].children[outcomeIndex]) == address(0x0)) createChildUniverse(parent, outcomeIndex); + ISecurityPool child = forkData[parent].children[outcomeIndex]; + + child.updateVaultFees(msg.sender); + parent.updateCollateralAmount(); + (uint256 parentPoolOwnership, uint256 parentSecurityBondAllowance, , , uint256 parentLockedRepInEscalationGame) = parent.securityVaults(msg.sender); + emit MigrateRepFromParent(msg.sender, parentSecurityBondAllowance, parentPoolOwnership); + child.setVaultSecurityBondAllowance(msg.sender, parentSecurityBondAllowance); + child.addToTotalSecurityBondAllowance(parentSecurityBondAllowance); + + if (parent.poolOwnershipDenominator() != 0 && child.repToken().balanceOf(address(child)) != 0) { + uint256 ownership = parentPoolOwnership - repToPoolOwnership(child, parentLockedRepInEscalationGame); + child.setVaultPoolOwnership(msg.sender, ownership); + uint256 migratedRep = poolOwnershipToRep(child, ownership); + forkData[child].migratedRep += migratedRep; + child.setVaultFeeIndex(msg.sender, child.feeIndex()); + // migrate open interest + if (ownership > 0) { + parent.migrateEth(payable(child), parent.completeSetCollateralAmount() * migratedRep / forkData[parent].repAtFork); + } + } + + (uint256 poolOwnership, uint256 securityBondAllowance,,,) = parent.securityVaults(msg.sender); + emit MigrateVault(msg.sender, outcomeIndex, poolOwnership, securityBondAllowance, parentLockedRepInEscalationGame); + parent.setVaultOwnership(msg.sender, 0, 0); + } + + function startTruthAuction(ISecurityPool securityPool) public { + require(securityPool.systemState() == SystemState.ForkMigration, 'System needs to be in migration'); + require(block.timestamp > zoltar.getForkTime(securityPool.universeId()) + SecurityPoolUtils.MIGRATION_TIME, 'migration time needs to pass first'); + securityPool.setSystemState(SystemState.ForkTruthAuction); + forkData[securityPool].truthAuctionStarted = block.timestamp; + ISecurityPool parent = securityPool.parent(); + parent.updateCollateralAmount(); + uint256 parentCollateral = parent.completeSetCollateralAmount(); + securityPool.setShareTokenSupply(parent.shareTokenSupply()); + emit TruthAuctionStarted(parentCollateral, forkData[securityPool].migratedRep, forkData[parent].repAtFork); + if (forkData[securityPool].migratedRep >= forkData[parent].repAtFork) { + // we have acquired all the ETH already, no need for truthAuction + _finalizeTruthAuction(securityPool, 0); + } else { + // we need to buy all the collateral that is missing (did not migrate) + uint256 ethToBuy = parentCollateral - parentCollateral * forkData[securityPool].migratedRep / forkData[parent].repAtFork; + // sell all but very small amount of REP for ETH. We cannot sell all for accounting purposes, as `poolOwnershipDenominator` cannot be infinite + // only migratedRep gets this guarrantee that some of their rep never gets sold + forkData[securityPool].truthAuction.startAuction(ethToBuy, forkData[parent].repAtFork - forkData[securityPool].migratedRep / SecurityPoolUtils.MAX_AUCTION_VAULT_HAIRCUT_DIVISOR); + } + } + + function _finalizeTruthAuction(ISecurityPool securityPool, uint256 repPurchased) private { + require(securityPool.systemState() == SystemState.ForkTruthAuction, 'Auction need to have started'); + forkData[securityPool].truthAuction.finalizeAuction(address(securityPool)); // this sends the eth back + securityPool.setSystemState(SystemState.Operational); + ISecurityPool parent = securityPool.parent(); + uint256 repAvailable = forkData[parent].repAtFork; + securityPool.setCompleteSetCollateralAmount(address(securityPool).balance - securityPool.totalFeesOvedToVaults()); //todo, we might want to reduce fees if we didn't get fully funded? + uint256 parentTotalSecurityBondAllowance = parent.totalSecurityBondAllowance(); + forkData[securityPool].auctionedSecurityBondAllowance = parentTotalSecurityBondAllowance - securityPool.totalSecurityBondAllowance(); + securityPool.setTotalSecurityBondAllowance(parentTotalSecurityBondAllowance); + if (repAvailable > 0) { + securityPool.setPoolOwnershipDenominator(forkData[securityPool].migratedRep * repAvailable * SecurityPoolUtils.PRICE_PRECISION / (repAvailable - repPurchased)); + } + if (securityPool.poolOwnershipDenominator() == 0) { // wipe all rep holders in vaults + securityPool.setPoolOwnershipDenominator(repAvailable * SecurityPoolUtils.PRICE_PRECISION); + } + emit FinalizeAuction(repAvailable, forkData[securityPool].migratedRep, repPurchased, securityPool.poolOwnershipDenominator(), securityPool.completeSetCollateralAmount()); + securityPool.updateRetentionRate(); + } + + function finalizeTruthAuction(ISecurityPool securityPool) public { + require(block.timestamp > forkData[securityPool].truthAuctionStarted + SecurityPoolUtils.AUCTION_TIME, 'truthAuction still ongoing'); + _finalizeTruthAuction(securityPool, forkData[securityPool].truthAuction.totalRepPurchased()); + } + + function forkZoltarWithOwnEscalationGame(ISecurityPool securityPool) public { + EscalationGame escalationGame = securityPool.escalationGame(); + require(address(escalationGame) != address(0x0) && escalationGame.nonDecisionTimestamp() > 0, 'escalation game has not triggered fork'); + (string memory extraInfo, string[4] memory outcomes) = securityPool.yesNoMarkets().getForkingData(securityPool.marketId()); + securityPool.stealAllRep(); + forkData[securityPool].ownFork = true; + securityPool.repToken().approve(address(zoltar), type(uint256).max); + zoltar.forkUniverse(securityPool.universeId(), extraInfo, outcomes); + } + + // accounts the purchased REP from truthAuction to the vault + // we should also move a share of bad debt in the system to this vault + // anyone can call these so that we can liquidate them if needed + function claimAuctionProceeds(ISecurityPool securityPool, address vault) public { + require(forkData[securityPool].claimedAuctionProceeds[vault] == false, 'Already Claimed'); + require(forkData[securityPool].truthAuction.finalized(), 'Auction needs to be finalized'); + forkData[securityPool].claimedAuctionProceeds[vault] = true; + uint256 amount = forkData[securityPool].truthAuction.purchasedRep(vault); + require(amount > 0, 'Did not purchase anything'); // not really necessary, but good for testing + uint256 poolOwnershipAmount = repToPoolOwnership(securityPool, amount); + (uint256 poolOwnership,,,,) = securityPool.securityVaults(vault); + securityPool.setVaultOwnership(vault, poolOwnership + poolOwnershipAmount, forkData[securityPool].auctionedSecurityBondAllowance * amount / forkData[securityPool].truthAuction.totalRepPurchased()); + emit ClaimAuctionProceeds(vault, amount, poolOwnershipAmount, securityPool.poolOwnershipDenominator()); + } + + function getMarketOutcome(ISecurityPool securityPool) external returns (YesNoMarkets.Outcome outcome){ + SystemState systemState = securityPool.systemState(); + if (systemState == SystemState.PoolForked) return YesNoMarkets.Outcome.None; + ISecurityPool parent = securityPool.parent(); + if (address(parent) != address(0x0)) { + if (forkData[parent].ownFork) return YesNoMarkets.Outcome(forkData[securityPool].outcomeIndex); + } + if (systemState == SystemState.Operational) { + EscalationGame escalationGame = securityPool.escalationGame(); + uint256 forkTime = zoltar.getForkTime(securityPool.universeId()); + if (address(escalationGame) != address(0x0)) { + uint256 escalationEndDate = escalationGame.getEscalationGameEndDate(); + if (block.timestamp > escalationEndDate && (forkTime == 0 || escalationEndDate < forkTime)) return escalationGame.getMarketResolution(); + } + } + return YesNoMarkets.Outcome.None; + } +} diff --git a/solidity/contracts/peripherals/SecurityPoolUtils.sol b/solidity/contracts/peripherals/SecurityPoolUtils.sol index 2441cf8..9a19606 100644 --- a/solidity/contracts/peripherals/SecurityPoolUtils.sol +++ b/solidity/contracts/peripherals/SecurityPoolUtils.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; library SecurityPoolUtils { diff --git a/solidity/contracts/peripherals/YesNoMarkets.sol b/solidity/contracts/peripherals/YesNoMarkets.sol new file mode 100644 index 0000000..85bd728 --- /dev/null +++ b/solidity/contracts/peripherals/YesNoMarkets.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.33; + +contract YesNoMarkets { + enum Outcome { + Invalid, + Yes, + No, + None + } + + struct MarketData { + string extraInfo; + uint256 marketEndDate; + uint256 marketCreated; + } + + mapping(uint256 => MarketData) markets; + + function createMarket(string memory extraInfo, uint256 marketEndDate, bytes32 salt) external returns (uint256) { + uint256 marketId = uint256(keccak256(abi.encodePacked(msg.sender, extraInfo, marketEndDate, salt))); + markets[marketId].extraInfo = extraInfo; + markets[marketId].marketCreated = block.timestamp; + markets[marketId].marketEndDate = marketEndDate; + return marketId; + } + + function getMarketEndDate(uint256 marketId) external view returns (uint256) { + return markets[marketId].marketEndDate; + } + + function getForkingData(uint256 marketId) external view returns (string memory extraInfo, string[4] memory outcomes) { + extraInfo = markets[marketId].extraInfo; + outcomes[0] = 'Yes'; + outcomes[1] = 'No'; + } +} diff --git a/solidity/contracts/peripherals/factories/AuctionFactory.sol b/solidity/contracts/peripherals/factories/AuctionFactory.sol index 46e8f94..0b460a9 100644 --- a/solidity/contracts/peripherals/factories/AuctionFactory.sol +++ b/solidity/contracts/peripherals/factories/AuctionFactory.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; import { Auction } from '../Auction.sol'; diff --git a/solidity/contracts/peripherals/factories/PriceOracleManagerAndOperatorQueuerFactory.sol b/solidity/contracts/peripherals/factories/PriceOracleManagerAndOperatorQueuerFactory.sol index 421a572..6981cfb 100644 --- a/solidity/contracts/peripherals/factories/PriceOracleManagerAndOperatorQueuerFactory.sol +++ b/solidity/contracts/peripherals/factories/PriceOracleManagerAndOperatorQueuerFactory.sol @@ -1,6 +1,5 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; -import { IShareToken } from '../interfaces/IShareToken.sol'; import { ShareToken } from '../tokens/ShareToken.sol'; import { ISecurityPool } from '../interfaces/ISecurityPool.sol'; import { Zoltar } from '../../Zoltar.sol'; diff --git a/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol b/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol index aba9ba3..feebd37 100644 --- a/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol +++ b/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol @@ -1,6 +1,5 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; -import { IShareToken } from '../interfaces/IShareToken.sol'; import { SecurityPool } from '../SecurityPool.sol'; import { ISecurityPool, ISecurityPoolFactory } from '../interfaces/ISecurityPool.sol'; import { OpenOracle } from '../openOracle/OpenOracle.sol'; @@ -8,10 +7,13 @@ import { Zoltar } from '../../Zoltar.sol'; import { ShareTokenFactory } from './ShareTokenFactory.sol'; import { AuctionFactory } from './AuctionFactory.sol'; import { Auction } from '../Auction.sol'; -import { ShareToken } from '../tokens/ShareToken.sol'; +import { IShareToken } from '../interfaces/IShareToken.sol'; import { PriceOracleManagerAndOperatorQueuerFactory } from './PriceOracleManagerAndOperatorQueuerFactory.sol'; import { PriceOracleManagerAndOperatorQueuer } from '../PriceOracleManagerAndOperatorQueuer.sol'; import { ReputationToken } from '../../ReputationToken.sol'; +import { EscalationGameFactory } from './EscalationGameFactory.sol'; +import { YesNoMarkets } from '../YesNoMarkets.sol'; +import { ISecurityPoolForker } from '../interfaces/ISecurityPoolForker.sol'; contract SecurityPoolFactory is ISecurityPoolFactory { ShareTokenFactory shareTokenFactory; @@ -19,50 +21,56 @@ contract SecurityPoolFactory is ISecurityPoolFactory { PriceOracleManagerAndOperatorQueuerFactory priceOracleManagerAndOperatorQueuerFactory; Zoltar zoltar; OpenOracle openOracle; + EscalationGameFactory escalationGameFactory; + YesNoMarkets yesNoMarkets; + ISecurityPoolForker securityPoolForker; - event DeploySecurityPool(ISecurityPool securityPool, Auction truthAuction, PriceOracleManagerAndOperatorQueuer priceOracleManagerAndOperatorQueuer, IShareToken shareToken, ISecurityPool parent, uint192 universeId, uint56 questionId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount); + event DeploySecurityPool(ISecurityPool securityPool, Auction truthAuction, PriceOracleManagerAndOperatorQueuer priceOracleManagerAndOperatorQueuer, IShareToken shareToken, ISecurityPool parent, uint248 universeId, uint256 marketId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount); - constructor(OpenOracle _openOracle, Zoltar _zoltar, ShareTokenFactory _shareTokenFactory, AuctionFactory _auctionFactory, PriceOracleManagerAndOperatorQueuerFactory _priceOracleManagerAndOperatorQueuerFactory) { + constructor(ISecurityPoolForker _securityPoolForker, YesNoMarkets _yesNoMarkets, EscalationGameFactory _escalationGameFactory, OpenOracle _openOracle, Zoltar _zoltar, ShareTokenFactory _shareTokenFactory, AuctionFactory _auctionFactory, PriceOracleManagerAndOperatorQueuerFactory _priceOracleManagerAndOperatorQueuerFactory) { + securityPoolForker = _securityPoolForker; shareTokenFactory = _shareTokenFactory; auctionFactory = _auctionFactory; priceOracleManagerAndOperatorQueuerFactory = _priceOracleManagerAndOperatorQueuerFactory; zoltar = _zoltar; openOracle = _openOracle; + escalationGameFactory = _escalationGameFactory; + yesNoMarkets = _yesNoMarkets; } - function deployChildSecurityPool(IShareToken shareToken, uint192 universeId, uint56 questionId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount) external returns (ISecurityPool securityPool) { - ISecurityPool parent = ISecurityPool(payable(msg.sender)); - bytes32 securityPoolSalt = keccak256(abi.encodePacked(parent, universeId, questionId, securityMultiplier)); - (ReputationToken reputationToken,,) = zoltar.universes(universeId); + function deployChildSecurityPool(ISecurityPool parent, IShareToken shareToken, uint248 universeId, uint256 marketId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount) external returns (ISecurityPool securityPool, Auction truthAuction) { + bytes32 securityPoolSalt = keccak256(abi.encodePacked(parent, universeId, marketId, securityMultiplier)); + ReputationToken reputationToken = zoltar.getRepToken(universeId); PriceOracleManagerAndOperatorQueuer priceOracleManagerAndOperatorQueuer = priceOracleManagerAndOperatorQueuerFactory.deployPriceOracleManagerAndOperatorQueuer(openOracle, reputationToken, securityPoolSalt); - Auction truthAuction = auctionFactory.deployAuction(securityPoolSalt); + truthAuction = auctionFactory.deployAuction(securityPoolSalt); - securityPool = new SecurityPool{ salt: bytes32(uint256(0x0)) }(this, truthAuction, priceOracleManagerAndOperatorQueuer, shareToken, openOracle, parent, zoltar, universeId, questionId, securityMultiplier); + securityPool = new SecurityPool{ salt: bytes32(uint256(0x0)) }(address(securityPoolForker), this, yesNoMarkets, escalationGameFactory, priceOracleManagerAndOperatorQueuer, shareToken, openOracle, parent, zoltar, universeId, marketId, securityMultiplier); priceOracleManagerAndOperatorQueuer.setSecurityPool(securityPool); securityPool.setStartingParams(currentRetentionRate, startingRepEthPrice, completeSetCollateralAmount); - truthAuction.setOwner(address(securityPool)); - emit DeploySecurityPool(securityPool, truthAuction, priceOracleManagerAndOperatorQueuer, shareToken, parent, universeId, questionId, securityMultiplier, currentRetentionRate, startingRepEthPrice, completeSetCollateralAmount); + truthAuction.setOwner(address(securityPoolForker)); + emit DeploySecurityPool(securityPool, truthAuction, priceOracleManagerAndOperatorQueuer, shareToken, parent, universeId, marketId, securityMultiplier, currentRetentionRate, startingRepEthPrice, completeSetCollateralAmount); } - function deployOriginSecurityPool(uint192 universeId, uint56 questionId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount) external returns (ISecurityPool securityPool) { - bytes32 securityPoolSalt = keccak256(abi.encodePacked(address(0x0), universeId, questionId, securityMultiplier)); - (ReputationToken reputationToken,,) = zoltar.universes(universeId); + function deployOriginSecurityPool(uint248 universeId, string memory extraInfo, uint256 marketEndDate, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice) external returns (ISecurityPool securityPool) { + uint256 marketId = yesNoMarkets.createMarket(extraInfo, marketEndDate, keccak256(abi.encodePacked(address(this), universeId, securityMultiplier, extraInfo, marketEndDate))); + ReputationToken reputationToken = zoltar.getRepToken(universeId); + bytes32 securityPoolSalt = keccak256(abi.encodePacked(address(0x0), universeId, marketId, securityMultiplier)); PriceOracleManagerAndOperatorQueuer priceOracleManagerAndOperatorQueuer = priceOracleManagerAndOperatorQueuerFactory.deployPriceOracleManagerAndOperatorQueuer(openOracle, reputationToken, securityPoolSalt); // sharetoken has different salt as sharetoken address does not change in forks - bytes32 shareTokenSalt = keccak256(abi.encodePacked(securityMultiplier)); - IShareToken shareToken = shareTokenFactory.deployShareToken(questionId, shareTokenSalt); + bytes32 shareTokenSalt = keccak256(abi.encodePacked(securityMultiplier, marketId)); + IShareToken shareToken = shareTokenFactory.deployShareToken(shareTokenSalt); - securityPool = new SecurityPool{ salt: bytes32(uint256(0x0)) }(this, Auction(address(0x0)), priceOracleManagerAndOperatorQueuer, shareToken, openOracle, ISecurityPool(payable(0x0)), zoltar, universeId, questionId, securityMultiplier); + securityPool = new SecurityPool{ salt: bytes32(uint256(0x0)) }(address(securityPoolForker), this, yesNoMarkets, escalationGameFactory, priceOracleManagerAndOperatorQueuer, shareToken, openOracle, ISecurityPool(payable(0x0)), zoltar, universeId, marketId, securityMultiplier); priceOracleManagerAndOperatorQueuer.setSecurityPool(securityPool); - securityPool.setStartingParams(currentRetentionRate, startingRepEthPrice, completeSetCollateralAmount); + securityPool.setStartingParams(currentRetentionRate, startingRepEthPrice, 0); shareToken.authorize(securityPool); - emit DeploySecurityPool(securityPool, Auction(address(0x0)), priceOracleManagerAndOperatorQueuer, shareToken, ISecurityPool(payable(0x0)), universeId, questionId, securityMultiplier, currentRetentionRate, startingRepEthPrice, completeSetCollateralAmount); + emit DeploySecurityPool(securityPool, Auction(address(0x0)), priceOracleManagerAndOperatorQueuer, shareToken, ISecurityPool(payable(0x0)), universeId, marketId, securityMultiplier, currentRetentionRate, startingRepEthPrice, 0); } } diff --git a/solidity/contracts/peripherals/factories/ShareTokenFactory.sol b/solidity/contracts/peripherals/factories/ShareTokenFactory.sol index 33b5483..3a4ce68 100644 --- a/solidity/contracts/peripherals/factories/ShareTokenFactory.sol +++ b/solidity/contracts/peripherals/factories/ShareTokenFactory.sol @@ -1,6 +1,5 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; -import { IShareToken } from '../interfaces/IShareToken.sol'; import { ShareToken } from '../tokens/ShareToken.sol'; import { ISecurityPool } from '../interfaces/ISecurityPool.sol'; import { Zoltar } from '../../Zoltar.sol'; @@ -12,7 +11,7 @@ contract ShareTokenFactory { zoltar = _zoltar; } - function deployShareToken(uint56 questionId, bytes32 salt) external returns (IShareToken shareToken) { - return new ShareToken{ salt: salt }(msg.sender, zoltar, questionId); + function deployShareToken(bytes32 salt) external returns (ShareToken shareToken) { + return new ShareToken{ salt: salt }(msg.sender, zoltar); } } diff --git a/solidity/contracts/peripherals/interfaces/IAugur.sol b/solidity/contracts/peripherals/interfaces/IAugur.sol index b367b73..db30c9e 100644 --- a/solidity/contracts/peripherals/interfaces/IAugur.sol +++ b/solidity/contracts/peripherals/interfaces/IAugur.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; enum TokenType { diff --git a/solidity/contracts/peripherals/interfaces/ISecurityPool.sol b/solidity/contracts/peripherals/interfaces/ISecurityPool.sol index fa9a90b..2f2aca1 100644 --- a/solidity/contracts/peripherals/interfaces/ISecurityPool.sol +++ b/solidity/contracts/peripherals/interfaces/ISecurityPool.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; import { Zoltar } from '../../Zoltar.sol'; @@ -7,12 +7,15 @@ import { Auction } from "../Auction.sol"; import { IShareToken } from "./IShareToken.sol"; import { ReputationToken } from "../../ReputationToken.sol"; import { PriceOracleManagerAndOperatorQueuer } from "../PriceOracleManagerAndOperatorQueuer.sol"; +import { EscalationGame } from '../EscalationGame.sol'; +import { YesNoMarkets } from '../YesNoMarkets.sol'; struct SecurityVault { uint256 poolOwnership; uint256 securityBondAllowance; uint256 unpaidEthFees; uint256 feeIndex; + uint256 lockedRepInEscalationGame; } enum SystemState { @@ -31,26 +34,20 @@ enum QuestionOutcome { interface ISecurityPool { // -------- View Functions -------- - function questionId() external view returns (uint56); - function universeId() external view returns (uint192); + function marketId() external view returns (uint256); + function universeId() external view returns (uint248); function zoltar() external view returns (Zoltar); - function securityBondAllowance() external view returns (uint256); + function totalSecurityBondAllowance() external view returns (uint256); function completeSetCollateralAmount() external view returns (uint256); function poolOwnershipDenominator() external view returns (uint256); - function repAtFork() external view returns (uint256); - function migratedRep() external view returns (uint256); function securityMultiplier() external view returns (uint256); function totalFeesOvedToVaults() external view returns (uint256); function lastUpdatedFeeAccumulator() external view returns (uint256); function currentRetentionRate() external view returns (uint256); - function securityVaults(address vault) external view returns (uint256 poolOwnership, uint256 securityBondAllowance, uint256 unpaidEthFees, uint256 feeIndex); - function claimedAuctionProceeds(address vault) external view returns (bool); - function children(uint256 index) external view returns (ISecurityPool); + function securityVaults(address vault) external view returns (uint256 poolOwnership, uint256 securityBondAllowance, uint256 unpaidEthFees, uint256 feeIndex, uint256 lockedRepInEscalationGame); function parent() external view returns (ISecurityPool); - function truthAuctionStarted() external view returns (uint256); function systemState() external view returns (SystemState); function shareToken() external view returns (IShareToken); - function truthAuction() external view returns (Auction); function repToken() external view returns (ReputationToken); function securityPoolFactory() external view returns (ISecurityPoolFactory); function priceOracleManagerAndOperatorQueuer() external view returns (PriceOracleManagerAndOperatorQueuer); @@ -80,20 +77,31 @@ interface ISecurityPool { function createCompleteSet() external payable; function redeemCompleteSet(uint256 amount) external; - function forkSecurityPool() external; - function migrateVault(QuestionOutcome outcome) external; - function migrateRepFromParent(address vault) external; - function startTruthAuction() external; - function finalizeTruthAuction() external; - function claimAuctionProceeds(address vault) external; - function createChildUniverse(QuestionOutcome outcome) external; - - function redeemShares() external; + function escalationGame() external returns (EscalationGame); + function setRetentionRate(uint256 newRetention) external; + function setSystemState(SystemState newState) external; + function setVaultOwnership(address vault, uint256 _poolOwnership, uint256 _securityBondAllowance) external; + + function setVaultSecurityBondAllowance(address vault, uint256 _securityBondAllowance) external; + function addToTotalSecurityBondAllowance(uint256 securityBondAllowanceDelta) external; + function setPoolOwnershipDenominator(uint256 _poolOwnershipDenominator) external; + function setVaultPoolOwnership(address vault, uint256 poolOwnership) external; + function setVaultFeeIndex(address vault, uint256 newFeeIndex) external; + function feeIndex() external view returns (uint256); + function setShareTokenSupply(uint256 newShareTokenSupply) external; + function setCompleteSetCollateralAmount(uint256 newCompleteSetCollateralAmount) external; + function setTotalSecurityBondAllowance(uint256 newTotalSecurityBondAllowance) external; + function authorize(ISecurityPool pool) external; + function yesNoMarkets() external view returns (YesNoMarkets); + function stealAllRep() external; + function migrateEth(address payable child, uint256 amount) external; + + function securityPoolForker() external returns (address); receive() external payable; } interface ISecurityPoolFactory { - function deployChildSecurityPool(IShareToken shareToken, uint192 universeId, uint56 questionId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount) external returns (ISecurityPool securityPool); - function deployOriginSecurityPool(uint192 universeId, uint56 questionId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount) external returns (ISecurityPool securityPool); + function deployChildSecurityPool(ISecurityPool parent, IShareToken shareToken, uint248 universeId, uint256 marketId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount) external returns (ISecurityPool securityPool, Auction truthAuction); + function deployOriginSecurityPool(uint248 universeId, string memory extraInfo, uint256 marketEndDate, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice) external returns (ISecurityPool securityPool); } diff --git a/solidity/contracts/peripherals/interfaces/ISecurityPoolForker.sol b/solidity/contracts/peripherals/interfaces/ISecurityPoolForker.sol new file mode 100644 index 0000000..7a8365b --- /dev/null +++ b/solidity/contracts/peripherals/interfaces/ISecurityPoolForker.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.33; + +import { ISecurityPool } from './ISecurityPool.sol'; +import { YesNoMarkets } from '../YesNoMarkets.sol'; + +interface ISecurityPoolForker { + function forkSecurityPool(ISecurityPool securityPool) external; + function createChildUniverse(ISecurityPool securityPool, uint8 outcomeIndex) external; + function migrateVault(ISecurityPool securityPool, uint8 outcomeIndex) external; + function startTruthAuction(ISecurityPool securityPool) external; + function finalizeTruthAuction(ISecurityPool securityPool) external; + function forkZoltarWithOwnEscalationGame(ISecurityPool securityPool) external; + function claimAuctionProceeds(ISecurityPool securityPool, address vault) external; + function getMarketOutcome(ISecurityPool securityPool) external returns (YesNoMarkets.Outcome outcome); +} diff --git a/solidity/contracts/peripherals/interfaces/IShareToken.sol b/solidity/contracts/peripherals/interfaces/IShareToken.sol index 398df80..8e833af 100644 --- a/solidity/contracts/peripherals/interfaces/IShareToken.sol +++ b/solidity/contracts/peripherals/interfaces/IShareToken.sol @@ -1,39 +1,21 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; -import '../interfaces/ISecurityPool.sol'; import '../../Zoltar.sol'; +import '../interfaces/ISecurityPool.sol'; +import '../tokens/TokenId.sol'; -/** -* @title IShareToken -* @notice Interface for the ShareToken contract -*/ interface IShareToken { - - // Read-only metadata - function name() external view returns (string memory); - function symbol() external view returns (string memory); - function zoltar() external view returns (Zoltar); - - // Security pool registration function authorize(ISecurityPool _securityPoolCandidate) external; - - // Question operations - function mintCompleteSets(uint192 _universeId, address _account, uint256 _cashAmount) external payable; - function burnCompleteSets(uint192 _universeId, address _owner, uint256 _amount) external; - function burnTokenId(uint256 _tokenId, address _owner) external returns (uint256); - - // TokenId information helpers - function getUniverse(uint256 _tokenId) external pure returns (uint256); - function getOutcome(uint256 _tokenId) external pure returns (Zoltar.Outcome); - - // Balance and supply queries - function totalSupplyForOutcome(uint192 _universeId, Zoltar.Outcome _outcome) external view returns (uint256); - function balanceOfOutcome(uint192 _universeId, Zoltar.Outcome _outcome, address _account) external view returns (uint256); - function balanceOfShares(uint192 _universeId, address _account) external view returns (uint256[3] memory balances); - - // Token ID encoding/decoding - function getTokenId(uint192 _universeId, Zoltar.Outcome _outcome) external pure returns (uint256 _tokenId); - function getTokenIds(uint192 _universeId, Zoltar.Outcome[] memory _outcomes) external pure returns (uint256[] memory _tokenIds); - function unpackTokenId(uint256 _tokenId) external pure returns (uint256 _universe, Zoltar.Outcome _outcome); + function mintCompleteSets(uint248 _universeId, address _account, uint256 _cashAmount) external payable; + function burnCompleteSets(uint248 _universeId, address _owner, uint256 _amount) external; + function burnTokenId(uint256 _tokenId, address _owner) external returns (uint256 balance); + function getUniverse(uint256 _tokenId) external pure returns(uint256); + function getOutcome(uint256 _tokenId) external pure returns(YesNoMarkets.Outcome); + function totalSupplyForOutcome(uint248 _universeId, YesNoMarkets.Outcome _outcome) external view returns (uint256); + function balanceOfOutcome(uint248 _universeId, YesNoMarkets.Outcome _outcome, address _account) external view returns (uint256); + function balanceOfShares(uint248 _universeId, address _account) external view returns (uint256[3] memory balances); + function getTokenId(uint248 _universeId, YesNoMarkets.Outcome _outcome) external pure returns (uint256 _tokenId); + function getTokenIds(uint248 _universeId, YesNoMarkets.Outcome[] memory _outcomes) external pure returns (uint256[] memory _tokenIds); + function unpackTokenId(uint248 _tokenId) external pure returns (uint248 _universe, YesNoMarkets.Outcome _outcome); } diff --git a/solidity/contracts/peripherals/interfaces/IWeth9.sol b/solidity/contracts/peripherals/interfaces/IWeth9.sol index feedd1b..4e78312 100644 --- a/solidity/contracts/peripherals/interfaces/IWeth9.sol +++ b/solidity/contracts/peripherals/interfaces/IWeth9.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; interface IWeth9 { diff --git a/solidity/contracts/peripherals/tokens/ERC1155.sol b/solidity/contracts/peripherals/tokens/ERC1155.sol index 85685dd..c2ed1c3 100644 --- a/solidity/contracts/peripherals/tokens/ERC1155.sol +++ b/solidity/contracts/peripherals/tokens/ERC1155.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; import { IERC1155 } from '../interfaces/IERC1155.sol'; diff --git a/solidity/contracts/peripherals/tokens/ForkedERC1155.sol b/solidity/contracts/peripherals/tokens/ForkedERC1155.sol index 1bfba57..d0e672e 100644 --- a/solidity/contracts/peripherals/tokens/ForkedERC1155.sol +++ b/solidity/contracts/peripherals/tokens/ForkedERC1155.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; import './ERC1155.sol'; @@ -9,26 +9,25 @@ abstract contract ForkedERC1155 is ERC1155 { event Migrate(address migrator, uint256 fromId, uint256 toId, uint256 fromIdBalance); constructor() {} - function universeHasForked(uint192 universeId) internal virtual view returns (bool); + function universeHasForked(uint248 universeId) internal virtual view returns (bool); - function getUniverseId(uint256 id) internal virtual pure returns (uint192); + function getUniverseId(uint256 id) internal virtual pure returns (uint248); - function getChildId(uint256 originalId, uint192 newUniverse) internal virtual pure returns (uint256); + function getChildId(uint256 originalId, uint248 newUniverse) internal virtual pure returns (uint256); + function getChildUniverseId(uint248 universeId, uint8 outcomeIndex) public virtual pure returns (uint248); - // Note: In the event there is a chain of forks 64+ deep where no balance has carried further down this will make the original value innaccesible - // This would take several years and likely a malicious actor very openly burning a large amount of money to do this and a user that has ignored every previous fork so the risk is considered low enough for this to be acceptable - function migrate(uint256 fromId) external { - uint192 universeId = getUniverseId(fromId); - require(universeHasForked(universeId), "Universe has not forked"); + function migrate(uint256 fromId, uint8[] memory outcomes) external { + uint248 universeId = getUniverseId(fromId); + require(universeHasForked(universeId), 'Universe has not forked'); uint256 fromIdBalance = _balances[fromId][msg.sender]; _balances[fromId][msg.sender] = 0; _supplys[fromId] -= fromIdBalance; - // For each outcome universe - for (uint8 i = 1; i < Constants.NUM_OUTCOMES + 1; i++) { - uint192 childUniverseId = (universeId << 2) + i; - uint256 toId = getChildId(fromId, childUniverseId); + // TODO, check that outcomes is unique + // TODO, do we allow people to migrate later to different universes? + for (uint8 i = 0; i < outcomes.length; i++) { + uint256 toId = getChildId(fromId, getChildUniverseId(universeId, outcomes[i])); _balances[toId][msg.sender] += fromIdBalance; _supplys[toId] += fromIdBalance; emit Migrate(msg.sender, fromId, toId, fromIdBalance); diff --git a/solidity/contracts/peripherals/tokens/ShareToken.sol b/solidity/contracts/peripherals/tokens/ShareToken.sol index e417183..cfe34e6 100644 --- a/solidity/contracts/peripherals/tokens/ShareToken.sol +++ b/solidity/contracts/peripherals/tokens/ShareToken.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; import '../../Constants.sol'; @@ -6,6 +6,7 @@ import './ForkedERC1155.sol'; import './TokenId.sol'; import '../../Zoltar.sol'; import '../interfaces/ISecurityPool.sol'; +import '../interfaces/IShareToken.sol'; /** * @title Share Token @@ -13,20 +14,18 @@ import '../interfaces/ISecurityPool.sol'; */ contract ShareToken is ForkedERC1155, IShareToken { - string constant public name = "Shares"; - string constant public symbol = "SHARE"; + // TODO, rename based on the market they represent + string constant public name = 'Shares'; + string constant public symbol = 'SHARE'; Zoltar public immutable zoltar; - uint56 public immutable questionId; mapping(address => bool) authorized; - function universeHasForked(uint192 universeId) internal override view returns (bool) { - (,, uint256 forkTime) = zoltar.universes(universeId); - return forkTime > 0; + function universeHasForked(uint248 universeId) internal override view returns (bool) { + return zoltar.getForkTime(universeId) > 0; } - constructor(address owner, Zoltar _zoltar, uint56 _questionId) { + constructor(address owner, Zoltar _zoltar) { zoltar = _zoltar; - questionId = _questionId; authorized[owner] = true; } @@ -35,38 +34,38 @@ contract ShareToken is ForkedERC1155, IShareToken { authorized[address(_securityPoolCandidate)] = true; } - function getUniverseId(uint256 id) internal override pure returns (uint192 universeId) { + function getUniverseId(uint256 id) internal override pure returns (uint248 universeId) { assembly { - universeId := shr(64, and(id, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000)) + universeId := shr(8, and(id, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00)) } } - function getChildId(uint256 originalId, uint192 newUniverse) internal override pure returns (uint256 newId) { + function getChildId(uint256 originalId, uint248 newUniverse) internal override pure returns (uint256 newId) { assembly { - newId := or(shr(192, shl(192, originalId)), shl(64, newUniverse)) + newId := or(and(originalId, 0xFF), shl(8, and(newUniverse, 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) } } - function mintCompleteSets(uint192 _universeId, address _account, uint256 _cashAmount) external payable { + function mintCompleteSets(uint248 _universeId, address _account, uint256 _cashAmount) external payable { require(authorized[msg.sender] == true, 'not authorized'); uint256[] memory _tokenIds = new uint256[](Constants.NUM_OUTCOMES); uint256[] memory _values = new uint256[](Constants.NUM_OUTCOMES); for (uint8 i = 0; i < Constants.NUM_OUTCOMES; i++) { - _tokenIds[i] = TokenId.getTokenId(_universeId, Zoltar.Outcome(i)); + _tokenIds[i] = TokenId.getTokenId(_universeId, YesNoMarkets.Outcome(i)); _values[i] = _cashAmount; } _mintBatch(_account, _tokenIds, _values); } - function burnCompleteSets(uint192 _universeId, address _owner, uint256 _amount) external { + function burnCompleteSets(uint248 _universeId, address _owner, uint256 _amount) external { require(authorized[msg.sender] == true, 'not authorized'); uint256[] memory _tokenIds = new uint256[](Constants.NUM_OUTCOMES); uint256[] memory _values = new uint256[](Constants.NUM_OUTCOMES); for (uint8 i = 0; i < Constants.NUM_OUTCOMES; i++) { - _tokenIds[i] = TokenId.getTokenId(_universeId, Zoltar.Outcome(i)); + _tokenIds[i] = TokenId.getTokenId(_universeId, YesNoMarkets.Outcome(i)); _values[i] = _amount; } @@ -79,41 +78,45 @@ contract ShareToken is ForkedERC1155, IShareToken { _burn(_owner, _tokenId, balance); } + function getChildUniverseId(uint248 universeId, uint8 outcomeIndex) public override pure returns (uint248) { + return uint248(uint256(keccak256(abi.encode(universeId, outcomeIndex)))); + } + function getUniverse(uint256 _tokenId) external pure returns(uint256) { - (uint192 _universe, ) = TokenId.unpackTokenId(_tokenId); + (uint248 _universe, ) = TokenId.unpackTokenId(_tokenId); return _universe; } - function getOutcome(uint256 _tokenId) external pure returns(Zoltar.Outcome) { - (, Zoltar.Outcome _outcome) = TokenId.unpackTokenId(_tokenId); + function getOutcome(uint256 _tokenId) external pure returns(YesNoMarkets.Outcome) { + (, YesNoMarkets.Outcome _outcome) = TokenId.unpackTokenId(_tokenId); return _outcome; } - function totalSupplyForOutcome(uint192 _universeId, Zoltar.Outcome _outcome) public view returns (uint256) { + function totalSupplyForOutcome(uint248 _universeId, YesNoMarkets.Outcome _outcome) public view returns (uint256) { uint256 _tokenId = getTokenId(_universeId, _outcome); return totalSupply(_tokenId); } - function balanceOfOutcome(uint192 _universeId, Zoltar.Outcome _outcome, address _account) public view returns (uint256) { + function balanceOfOutcome(uint248 _universeId, YesNoMarkets.Outcome _outcome, address _account) public view returns (uint256) { uint256 _tokenId = getTokenId(_universeId, _outcome); return balanceOf(_account, _tokenId); } - function balanceOfShares(uint192 _universeId, address _account) public view returns (uint256[3] memory balances) { - balances[0] = balanceOf(_account, getTokenId(_universeId, Zoltar.Outcome.Invalid)); - balances[1] = balanceOf(_account, getTokenId(_universeId, Zoltar.Outcome.Yes)); - balances[2] = balanceOf(_account, getTokenId(_universeId, Zoltar.Outcome.No)); + function balanceOfShares(uint248 _universeId, address _account) public view returns (uint256[3] memory balances) { + balances[0] = balanceOf(_account, getTokenId(_universeId, YesNoMarkets.Outcome.Invalid)); + balances[1] = balanceOf(_account, getTokenId(_universeId, YesNoMarkets.Outcome.Yes)); + balances[2] = balanceOf(_account, getTokenId(_universeId, YesNoMarkets.Outcome.No)); } - function getTokenId(uint192 _universeId, Zoltar.Outcome _outcome) public pure returns (uint256 _tokenId) { + function getTokenId(uint248 _universeId, YesNoMarkets.Outcome _outcome) public pure returns (uint256 _tokenId) { return TokenId.getTokenId(_universeId, _outcome); } - function getTokenIds(uint192 _universeId, Zoltar.Outcome[] memory _outcomes) public pure returns (uint256[] memory _tokenIds) { + function getTokenIds(uint248 _universeId, YesNoMarkets.Outcome[] memory _outcomes) public pure returns (uint256[] memory _tokenIds) { return TokenId.getTokenIds(_universeId, _outcomes); } - function unpackTokenId(uint256 _tokenId) public pure returns (uint256 _universe, Zoltar.Outcome _outcome) { + function unpackTokenId(uint248 _tokenId) public pure returns (uint248 _universe, YesNoMarkets.Outcome _outcome) { return TokenId.unpackTokenId(_tokenId); } } diff --git a/solidity/contracts/peripherals/tokens/TokenId.sol b/solidity/contracts/peripherals/tokens/TokenId.sol index 874898f..7dc43ae 100644 --- a/solidity/contracts/peripherals/tokens/TokenId.sol +++ b/solidity/contracts/peripherals/tokens/TokenId.sol @@ -1,28 +1,28 @@ -// SPDX-License-Identifier: UNICENSE +// SPDX-License-Identifier: Unlicense pragma solidity 0.8.33; import '../../Zoltar.sol'; +import { YesNoMarkets } from '../YesNoMarkets.sol'; library TokenId { - function getTokenId(uint192 _universeId, Zoltar.Outcome _outcome) internal pure returns (uint256 _tokenId) { - bytes memory _tokenIdBytes = abi.encodePacked(_universeId, uint56(0), _outcome); + function getTokenId(uint248 _universeId, YesNoMarkets.Outcome _outcome) internal pure returns (uint256 _tokenId) { assembly { - _tokenId := mload(add(_tokenIdBytes, add(0x20, 0))) + _tokenId := or(shl(8, and(_universeId, 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), and(_outcome, 0xFF)) } } - function getTokenIds(uint192 _universeId, Zoltar.Outcome[] memory _outcomes) internal pure returns (uint256[] memory _tokenIds) { + function getTokenIds(uint248 _universeId, YesNoMarkets.Outcome[] memory _outcomes) internal pure returns (uint256[] memory _tokenIds) { _tokenIds = new uint256[](_outcomes.length); for (uint256 _i = 0; _i < _outcomes.length; _i++) { _tokenIds[_i] = getTokenId(_universeId, _outcomes[_i]); } } - function unpackTokenId(uint256 _tokenId) internal pure returns (uint192 _universe, Zoltar.Outcome _outcome) { + function unpackTokenId(uint256 _tokenId) internal pure returns (uint248 _universe, YesNoMarkets.Outcome _outcome) { assembly { - _universe := shr(64, and(_tokenId, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000)) - _outcome := and(_tokenId, 0x00000000000000000000000000000000000000000000000000000000000000FF) + _universe := shr(8, and(_tokenId, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00)) + _outcome := and(_tokenId, 0xFF) } } } diff --git a/solidity/ts/tests/testPeripherals.ts b/solidity/ts/tests/testPeripherals.ts index a509a8f..f9c6af8 100644 --- a/solidity/ts/tests/testPeripherals.ts +++ b/solidity/ts/tests/testPeripherals.ts @@ -1,289 +1,363 @@ -import { describe, beforeEach, test } from 'node:test' +import test, { describe, beforeEach } from 'node:test' +import assert from 'node:assert' import { getMockedEthSimulateWindowEthereum, MockWindowEthereum } from '../testsuite/simulator/MockWindowEthereum.js' import { createWriteClient, WriteClient } from '../testsuite/simulator/utils/viem.js' import { DAY, GENESIS_REPUTATION_TOKEN, TEST_ADDRESSES } from '../testsuite/simulator/utils/constants.js' -import { approximatelyEqual, contractExists, finalizeQuestion, getChildUniverseId, getERC20Balance, getETHBalance, getQuestionData, getReportBond, getRepTokenAddress, getWinningOutcome, isFinalized, reportOutcome, setupTestAccounts } from '../testsuite/simulator/utils/utilities.js' +import { approveToken, contractExists, getChildUniverseId, getERC20Balance, getETHBalance, setupTestAccounts } from '../testsuite/simulator/utils/utilities.js' import { addressString, dateToBigintSeconds, rpow } from '../testsuite/simulator/utils/bigint.js' -import assert from 'node:assert' -import { SystemState } from '../testsuite/simulator/types/peripheralTypes.js' -import { getDeployments } from '../testsuite/simulator/utils/deployments.js' +import { getDeployments } from '../testsuite/simulator/utils/contracts/deployments.js' import { createTransactionExplainer } from '../testsuite/simulator/utils/transactionExplainer.js' -import { approveAndDepositRep, canLiquidate, deployPeripherals, deployZoltarAndCreateMarket, genesisUniverse, MAX_RETENTION_RATE, questionId, manipulatePriceOracleAndPerformOperation, securityMultiplier, triggerFork, manipulatePriceOracle, handleOracleReporting } from '../testsuite/simulator/utils/peripheralsTestUtils.js' -import { getSecurityPoolAddresses } from '../testsuite/simulator/utils/deployPeripherals.js' -import { balanceOfShares, balanceOfSharesInCash, claimAuctionProceeds, createChildUniverse, createCompleteSet, finalizeTruthAuction, forkSecurityPool, getCompleteSetCollateralAmount, getCurrentRetentionRate, getEthAmountToBuy, getTotalFeesOvedToVaults, getLastPrice, getMigratedRep, getPoolOwnershipDenominator, getSecurityBondAllowance, getSecurityVault, getSystemState, migrateShares, migrateVault, OperationType, participateAuction, poolOwnershipToRep, redeemCompleteSet, redeemFees, redeemShares, sharesToCash, startTruthAuction, updateVaultFees, redeemRep, requestPriceIfNeededAndQueueOperation, depositRep } from '../testsuite/simulator/utils/peripherals.js' +import { approveAndDepositRep, canLiquidate, handleOracleReporting, manipulatePriceOracle, manipulatePriceOracleAndPerformOperation, triggerOwnGameFork } from '../testsuite/simulator/utils/contracts/peripheralsTestUtils.js' +import { deployOriginSecurityPool, ensureInfraDeployed, getInfraContractAddresses, getMarketId, getSecurityPoolAddresses } from '../testsuite/simulator/utils/contracts/deployPeripherals.js' +import { balanceOfShares, balanceOfSharesInCash, getEthAmountToBuy, getLastPrice, getMarketEndDate, migrateShares, OperationType, participateAuction, requestPriceIfNeededAndQueueOperation } from '../testsuite/simulator/utils/contracts/peripherals.js' import { QuestionOutcome } from '../testsuite/simulator/types/types.js' +import { approximatelyEqual, strictEqual18Decimal, strictEqualTypeSafe } from '../testsuite/simulator/utils/testUtils.js' +import { claimAuctionProceeds, createChildUniverse, finalizeTruthAuction, forkSecurityPool, getMarketOutcome, getMigratedRep, getSecurityPoolForkerForkData, migrateFromEscalationGame, migrateVault, startTruthAuction } from '../testsuite/simulator/utils/contracts/securityPoolForker.js' +import { SystemState } from '../testsuite/simulator/types/peripheralTypes.js' +import { getEscalationGameDeposits, getMarketResolution, getNonDecisionTreshold, getStartBond } from '../testsuite/simulator/utils/contracts/escalationGame.js' +import { ensureZoltarDeployed, forkUniverse, getRepTokenAddress, getTotalTheoreticalSupply, getUniverseForkData, getZoltarAddress, getZoltarForkTreshold } from '../testsuite/simulator/utils/contracts/zoltar.js' +import { createCompleteSet, depositRep, depositToEscalationGame, getCompleteSetCollateralAmount, getCurrentRetentionRate, getPoolOwnershipDenominator, getRepToken, getSecurityPoolsEscalationGame, getSecurityVault, getSystemState, getTotalFeesOvedToVaults, getTotalSecurityBondAllowance, poolOwnershipToRep, redeemCompleteSet, redeemFees, redeemRep, redeemShares, sharesToCash, updateVaultFees, withdrawFromEscalationGame } from '../testsuite/simulator/utils/contracts/securityPool.js' describe('Peripherals Contract Test Suite', () => { let mockWindow: MockWindowEthereum let client: WriteClient let startBalance: bigint - let reportBond: bigint + const reportBond = 1n * 10n ** 18n const PRICE_PRECISION = 1n * 10n ** 18n const repDeposit = 1000n * 10n ** 18n const currentTimestamp = dateToBigintSeconds(new Date()) + const marketEndDate = currentTimestamp + 365n * DAY let securityPoolAddresses: { securityPool: `0x${ string }`, priceOracleManagerAndOperatorQueuer: `0x${ string }`, shareToken: `0x${ string }`, - truthAuction: `0x${ string }` + truthAuction: `0x${ string }`, + escalationGame: `0x${ string }`, } + const genesisUniverse = 0n + const securityMultiplier = 2n + const startingRepEthPrice = 10n + const MAX_RETENTION_RATE = 999_999_996_848_000_000n // ≈90% yearly + const EXTRA_INFO = 'test market!' + const marketId = getMarketId(genesisUniverse, securityMultiplier, EXTRA_INFO, marketEndDate) + + const marketText = 'test market' + const outcomes = ['Outcome 1', 'Outcome 2', 'Outcome 3', 'Outcome 4'] as const beforeEach(async () => { mockWindow = getMockedEthSimulateWindowEthereum() - mockWindow.setAfterTransactionSendCallBack(createTransactionExplainer(getDeployments(genesisUniverse, questionId, securityMultiplier))) + mockWindow.setAfterTransactionSendCallBack(createTransactionExplainer(getDeployments(genesisUniverse, marketId, securityMultiplier))) client = createWriteClient(mockWindow, TEST_ADDRESSES[0], 0) //await mockWindow.setStartBLock(mockWindow.getTime) await setupTestAccounts(mockWindow) startBalance = await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), client.account.address) - await deployZoltarAndCreateMarket(client, currentTimestamp + 365n * DAY) - await deployPeripherals(client) - await approveAndDepositRep(client, repDeposit) - securityPoolAddresses = getSecurityPoolAddresses(addressString(0x0n), genesisUniverse, questionId, securityMultiplier) - reportBond = await getReportBond(client) + await ensureZoltarDeployed(client) + await ensureInfraDeployed(client) + await deployOriginSecurityPool(client, genesisUniverse, EXTRA_INFO, marketEndDate, securityMultiplier, MAX_RETENTION_RATE, startingRepEthPrice) + + await approveAndDepositRep(client, repDeposit, marketId) + securityPoolAddresses = getSecurityPoolAddresses(addressString(0x0n), genesisUniverse, marketId, securityMultiplier) }) test('can deposit rep and withdraw it', async () => { await manipulatePriceOracleAndPerformOperation(client, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.WithdrawRep, client.account.address, repDeposit) - assert.strictEqual(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') + strictEqualTypeSafe(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') approximatelyEqual(await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddresses.securityPool), 0n, 100n, 'Did not empty security pool of rep') - approximatelyEqual(await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), client.account.address), startBalance - reportBond, 100n, 'Did not get rep back') + approximatelyEqual(await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), client.account.address), startBalance, 100n, 'Did not get rep back') }) test('can deposit rep and redeem it back after market has ended', async () => { await manipulatePriceOracle(client, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer) - assert.strictEqual(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') + strictEqualTypeSafe(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') + const poolOwnershipDenominator = await getPoolOwnershipDenominator(client, securityPoolAddresses.securityPool) + assert.ok(poolOwnershipDenominator > 0n, 'poolOwnershipDenominator was zero') + const endTime = await getMarketEndDate(client, marketId) + await mockWindow.setTime(endTime + 10000n) + await depositToEscalationGame(client, securityPoolAddresses.securityPool, QuestionOutcome.Yes, reportBond) + const escalationGameAddress = await getSecurityPoolsEscalationGame(client, securityPoolAddresses.securityPool) + strictEqualTypeSafe(escalationGameAddress, securityPoolAddresses.escalationGame, 'escalation game addresses do not match') + + assert.ok(await getNonDecisionTreshold(client, securityPoolAddresses.escalationGame) > 10n * reportBond, 'fork treshold need to be big enough') + await mockWindow.advanceTime(10n * DAY) + const yesDeposits = await getEscalationGameDeposits(client, securityPoolAddresses.escalationGame, QuestionOutcome.Yes) + strictEqualTypeSafe(yesDeposits.length, 1, 'the should have been one deposit') + strictEqualTypeSafe(yesDeposits[0].depositIndex, 0n, 'index should be zero') + strictEqualTypeSafe(yesDeposits[0].depositor, client.account.address, 'wrong depositor') + strictEqualTypeSafe(yesDeposits[0].cumulativeAmount, reportBond, 'cumulator should be report bond') + strictEqualTypeSafe(yesDeposits[0].amount, reportBond, 'amount should be report bond') + strictEqualTypeSafe(await getStartBond(client, securityPoolAddresses.escalationGame), reportBond, 'report bond matches') + + const ourDeposits = yesDeposits.filter((deposit) => BigInt(deposit.depositor) === BigInt(client.account.address)) + strictEqualTypeSafe(await getMarketResolution(client, securityPoolAddresses.escalationGame), QuestionOutcome.Yes, 'market has resolved') + await withdrawFromEscalationGame(client, securityPoolAddresses.securityPool, ourDeposits.map((deposit) => deposit.depositIndex)) - const questionData = await getQuestionData(client, questionId) - await mockWindow.setTime(questionData.endTime + 10000n) - await reportOutcome(client, genesisUniverse, questionId, QuestionOutcome.Yes) - await mockWindow.advanceTime(2n * DAY) - await finalizeQuestion(client, genesisUniverse, questionId) const repBefore = await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), client.account.address) await redeemRep(client, securityPoolAddresses.securityPool, client.account.address) const repAfter = await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), client.account.address) - assert.strictEqual(repAfter-repBefore, repDeposit, 'did not get rep back') - assert.strictEqual(await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddresses.securityPool), 0n, 'Did not empty security pool of rep') + strictEqualTypeSafe(repAfter-repBefore, repDeposit, 'did not get rep back') + strictEqualTypeSafe(await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddresses.securityPool), 0n, 'Did not empty security pool of rep') }) test('Can Liquidate', async () => { - const questionData = await getQuestionData(client, questionId) - await mockWindow.setTime(questionData.endTime + 10000n) + const endTime = await getMarketEndDate(client, marketId) + await mockWindow.setTime(endTime + 10000n) const securityPoolAllowance = repDeposit / 4n - assert.strictEqual(await getCurrentRetentionRate(client, securityPoolAddresses.securityPool), MAX_RETENTION_RATE, 'retention rate was not at max'); + strictEqualTypeSafe(await getCurrentRetentionRate(client, securityPoolAddresses.securityPool), MAX_RETENTION_RATE, 'retention rate was not at max'); await manipulatePriceOracleAndPerformOperation(client, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.SetSecurityBondsAllowance, client.account.address, securityPoolAllowance) const initialPrice = await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer) - assert.strictEqual(initialPrice, 1n * PRICE_PRECISION, 'Price was not set!') - assert.strictEqual(await getSecurityBondAllowance(client, securityPoolAddresses.securityPool), securityPoolAllowance, 'Security pool allowance was not set correctly') + strictEqualTypeSafe(initialPrice, 1n * PRICE_PRECISION, 'Price was not set!') + strictEqualTypeSafe(await getTotalSecurityBondAllowance(client, securityPoolAddresses.securityPool), securityPoolAllowance, 'Security pool allowance was not set correctly') + const liquidatorClient = createWriteClient(mockWindow, TEST_ADDRESSES[1], 0) + await approveToken(liquidatorClient, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddresses.securityPool) + await depositRep(liquidatorClient, securityPoolAddresses.securityPool, repDeposit * 10n) const openInterestAmount = 100n * 10n ** 18n await createCompleteSet(client, securityPoolAddresses.securityPool, openInterestAmount) await mockWindow.advanceTime(100000n) - const liquidatorClient = createWriteClient(mockWindow, TEST_ADDRESSES[1], 0) - - assert.strictEqual(canLiquidate(initialPrice, securityPoolAllowance, repDeposit, 2n), false, 'Should not be able to liquidate yet') + strictEqualTypeSafe(canLiquidate(initialPrice, securityPoolAllowance, repDeposit, 2n), false, 'Should not be able to liquidate yet') // REP/ETH increases to 10x, 10 REP = 1 ETH (rep drops in value) const forcedPrice = PRICE_PRECISION * 10n await requestPriceIfNeededAndQueueOperation(liquidatorClient, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.Liquidation, client.account.address, securityPoolAllowance) - assert.rejects(depositRep(client, securityPoolAddresses.securityPool, repDeposit * 10n), 'operation pending') + + // TODO: this should reject as we should not allow user to block liquidation like this + //assert.rejects(depositRep(client, securityPoolAddresses.securityPool, repDeposit * 10n), 'operation pending') await handleOracleReporting(liquidatorClient, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, forcedPrice) const currentPrice = await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer) - assert.strictEqual(currentPrice, PRICE_PRECISION * 10n, 'Price did not increase!') + strictEqualTypeSafe(currentPrice, PRICE_PRECISION * 10n, 'Price did not increase!') - assert.strictEqual(canLiquidate(currentPrice, securityPoolAllowance, repDeposit, 2n), true, 'Should be able to liquidate now') + strictEqualTypeSafe(canLiquidate(currentPrice, securityPoolAllowance, repDeposit, 2n), true, 'Should be able to liquidate now') // liquidator should have all the assets now const originalVault = await getSecurityVault(client, securityPoolAddresses.securityPool, client.account.address) const liquidatorVault = await getSecurityVault(client, securityPoolAddresses.securityPool, liquidatorClient.account.address) - assert.strictEqual(originalVault.securityBondAllowance, 0n, 'original vault should not have any security bonds') - assert.strictEqual(originalVault.repDepositShare, 0n, 'original vault should not have any rep') - assert.strictEqual(liquidatorVault.securityBondAllowance, securityPoolAllowance, 'liquidator doesnt have all the security pool allowances') - assert.strictEqual(liquidatorVault.repDepositShare / PRICE_PRECISION, repDeposit+(repDeposit * 10n), 'liquidator should have all the rep in the pool') + strictEqualTypeSafe(originalVault.securityBondAllowance, 0n, 'original vault should not have any security bonds') + strictEqualTypeSafe(originalVault.repDepositShare, 0n, 'original vault should not have any rep') + strictEqualTypeSafe(liquidatorVault.securityBondAllowance, securityPoolAllowance, 'liquidator doesnt have all the security pool allowances') + strictEqualTypeSafe(liquidatorVault.repDepositShare / PRICE_PRECISION, repDeposit+(repDeposit * 10n), 'liquidator should have all the rep in the pool') }) test('Open Interest Fees (non forking)', async () => { - const questionData = await getQuestionData(client, questionId) - assert.strictEqual(questionData.endTime > dateToBigintSeconds(new Date), true, 'market has already ended') + const endTime = await getMarketEndDate(client, marketId) + strictEqualTypeSafe(endTime > dateToBigintSeconds(new Date), true, 'market has already ended') const securityPoolAllowance = repDeposit / 4n const aMonthFromNow = currentTimestamp + 2628000n - assert.strictEqual(await getCurrentRetentionRate(client, securityPoolAddresses.securityPool), MAX_RETENTION_RATE, 'retention rate was not at max') + strictEqualTypeSafe(await getCurrentRetentionRate(client, securityPoolAddresses.securityPool), MAX_RETENTION_RATE, 'retention rate was not at max') await manipulatePriceOracleAndPerformOperation(client, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.SetSecurityBondsAllowance, client.account.address, securityPoolAllowance) - assert.strictEqual(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') - assert.strictEqual(await getSecurityBondAllowance(client, securityPoolAddresses.securityPool), securityPoolAllowance, 'Security pool allowance was not set correctly') + strictEqualTypeSafe(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') + strictEqualTypeSafe(await getTotalSecurityBondAllowance(client, securityPoolAddresses.securityPool), securityPoolAllowance, 'Security pool allowance was not set correctly') const openInterestAmount = 100n * 10n ** 18n await mockWindow.setTime(aMonthFromNow) await createCompleteSet(client, securityPoolAddresses.securityPool, openInterestAmount) const retentionRate = await getCurrentRetentionRate(client, securityPoolAddresses.securityPool) - await mockWindow.setTime(questionData.endTime + 10000n) + await mockWindow.setTime(endTime + 10000n) await updateVaultFees(client, securityPoolAddresses.securityPool, client.account.address) const feesAccrued = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) const ethBalanceBefore = await getETHBalance(client, client.account.address) const securityVault = await getSecurityVault(client, securityPoolAddresses.securityPool, client.account.address) await redeemFees(client, securityPoolAddresses.securityPool, client.account.address) - assert.strictEqual(securityVault.securityBondAllowance, securityPoolAllowance, 'securityPoolAllowance is all ours') + strictEqualTypeSafe(securityVault.securityBondAllowance, securityPoolAllowance, 'securityPoolAllowance is all ours') const ethBalanceAfter = await getETHBalance(client, client.account.address) - assert.strictEqual(ethBalanceAfter - ethBalanceBefore, securityVault.unpaidEthFees, 'eth gained should be fees accrued') - assert.strictEqual(feesAccrued / 1000n, securityVault.unpaidEthFees / 1000n, 'eth gained should be fees accrued (minus rounding issues') + strictEqualTypeSafe(ethBalanceAfter - ethBalanceBefore, securityVault.unpaidEthFees, 'eth gained should be fees accrued') + strictEqualTypeSafe(feesAccrued / 1000n, securityVault.unpaidEthFees / 1000n, 'eth gained should be fees accrued (minus rounding issues') const completeSetCollateralAmount = await getCompleteSetCollateralAmount(client, securityPoolAddresses.securityPool) - assert.strictEqual(feesAccrued + completeSetCollateralAmount, openInterestAmount, 'no eth lost') - const timePassed = questionData.endTime - aMonthFromNow - assert.strictEqual(timePassed / 8640n, 3345n, 'not enough time passed') - assert.strictEqual(retentionRate, 999999987364000000n, 'retention rate did not match') + strictEqualTypeSafe(feesAccrued + completeSetCollateralAmount, openInterestAmount, 'no eth lost') + const timePassed = endTime - aMonthFromNow + strictEqualTypeSafe(timePassed / 8640n, 3345n, 'not enough time passed') + strictEqualTypeSafe(retentionRate, 999999987364000000n, 'retention rate did not match') const completeSetCollateralAmountPercentage = Number(completeSetCollateralAmount * 1000n / openInterestAmount) / 10 const expected = Number(1000n * rpow(retentionRate, timePassed, PRICE_PRECISION) / PRICE_PRECISION) / 10 - assert.strictEqual(completeSetCollateralAmountPercentage, expected, 'return amount did not match') + strictEqualTypeSafe(completeSetCollateralAmountPercentage, expected, 'return amount did not match') const contractBalance = await getETHBalance(client, securityPoolAddresses.securityPool) - assert.strictEqual(contractBalance + ethBalanceAfter - ethBalanceBefore, openInterestAmount, 'contract balance+ fees should equal initial open interest') + strictEqualTypeSafe(contractBalance + ethBalanceAfter - ethBalanceBefore, openInterestAmount, 'contract balance+ fees should equal initial open interest') }) test('can set security bonds allowance, mint complete sets and fork happily' , async () => { - const questionData = await getQuestionData(client, questionId) - await mockWindow.setTime(questionData.endTime + 10000n) + const endTime = await getMarketEndDate(client, marketId) + await mockWindow.setTime(endTime + 10000n) const securityPoolAllowance = repDeposit / 4n - assert.strictEqual(await getCurrentRetentionRate(client, securityPoolAddresses.securityPool), MAX_RETENTION_RATE, 'retention rate was not at max'); + strictEqualTypeSafe(await getCurrentRetentionRate(client, securityPoolAddresses.securityPool), MAX_RETENTION_RATE, 'retention rate was not at max'); await manipulatePriceOracleAndPerformOperation(client, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.SetSecurityBondsAllowance, client.account.address, securityPoolAllowance) - assert.strictEqual(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') - assert.strictEqual(await getSecurityBondAllowance(client, securityPoolAddresses.securityPool), securityPoolAllowance, 'Security pool allowance was not set correctly') + strictEqualTypeSafe(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') + strictEqualTypeSafe(await getTotalSecurityBondAllowance(client, securityPoolAddresses.securityPool), securityPoolAllowance, 'Security pool allowance was not set correctly') + + const forkTreshold = (await getTotalTheoreticalSupply(client, await getRepToken(client, securityPoolAddresses.securityPool))) / 20n + await depositRep(client, securityPoolAddresses.securityPool, forkTreshold * 2n) const openInterestAmount = 100n * 10n ** 18n - const maxGasFees = openInterestAmount /4n + const maxGasFees = openInterestAmount / 4n const ethBalance = await getETHBalance(client, client.account.address) await createCompleteSet(client, securityPoolAddresses.securityPool, openInterestAmount) assert.ok(await getCurrentRetentionRate(client, securityPoolAddresses.securityPool) < MAX_RETENTION_RATE, 'retention rate did not decrease after minting complete sets'); const completeSetBalances = await balanceOfShares(client, securityPoolAddresses.shareToken, genesisUniverse, client.account.address) - assert.strictEqual(completeSetBalances[0], completeSetBalances[1], 'yes no and invalid share counts need to match') - assert.strictEqual(completeSetBalances[1], completeSetBalances[2], 'yes no and invalid share counts need to match') - assert.strictEqual(openInterestAmount, await sharesToCash(client, securityPoolAddresses.securityPool, completeSetBalances[0]), 'Did not create enough complete sets') + strictEqualTypeSafe(completeSetBalances[0], completeSetBalances[1], 'yes no and invalid share counts need to match') + strictEqualTypeSafe(completeSetBalances[1], completeSetBalances[2], 'yes no and invalid share counts need to match') + strictEqualTypeSafe(await sharesToCash(client, securityPoolAddresses.securityPool, completeSetBalances[0]), openInterestAmount, 'Did not create enough complete sets') assert.ok(ethBalance - await getETHBalance(client, client.account.address) > maxGasFees, 'Did not lose eth to create complete sets') - assert.strictEqual(await getCompleteSetCollateralAmount(client, securityPoolAddresses.securityPool), openInterestAmount, 'contract did not record the amount correctly') + strictEqualTypeSafe(await getCompleteSetCollateralAmount(client, securityPoolAddresses.securityPool), openInterestAmount, 'contract did not record the amount correctly') await redeemCompleteSet(client, securityPoolAddresses.securityPool, completeSetBalances[0]) assert.ok(ethBalance - await getETHBalance(client, client.account.address) < maxGasFees, 'Did not get ETH back from complete sets') const newCompleteSetBalances = await balanceOfShares(client, securityPoolAddresses.shareToken, genesisUniverse, client.account.address) - assert.strictEqual(newCompleteSetBalances[0], 0n, 'Did not lose complete sets') - assert.strictEqual(newCompleteSetBalances[1], 0n, 'Did not lose complete sets') - assert.strictEqual(newCompleteSetBalances[2], 0n, 'Did not lose complete sets') - assert.strictEqual(await getCurrentRetentionRate(client, securityPoolAddresses.securityPool), MAX_RETENTION_RATE, 'retention rate was not at max after zero complete sets'); - // forking + strictEqualTypeSafe(newCompleteSetBalances[0], 0n, 'Did not lose complete sets') + strictEqualTypeSafe(newCompleteSetBalances[1], 0n, 'Did not lose complete sets') + strictEqualTypeSafe(newCompleteSetBalances[2], 0n, 'Did not lose complete sets') + strictEqualTypeSafe(await getCurrentRetentionRate(client, securityPoolAddresses.securityPool), MAX_RETENTION_RATE, 'retention rate was not at max after zero complete sets'); + await createCompleteSet(client, securityPoolAddresses.securityPool, openInterestAmount) const repBalance = await getERC20Balance(client, getRepTokenAddress(genesisUniverse), securityPoolAddresses.securityPool) - await triggerFork(client, mockWindow, questionId) + // forking + const zoltarForkTreshold = await getZoltarForkTreshold(client, genesisUniverse) + const burnAmount = zoltarForkTreshold / 5n + await triggerOwnGameFork(client, securityPoolAddresses.securityPool) + const forkerRepBalance = await getERC20Balance(client, getRepTokenAddress(genesisUniverse), getInfraContractAddresses().securityPoolForker) + const zoltarForkData = await getUniverseForkData(client, genesisUniverse) + strictEqualTypeSafe(zoltarForkData.forkerRepDeposit + forkerRepBalance + burnAmount, repBalance, 'forkerRepDeposit + forkerRepBalance+burnAmount should equal deposit') + await forkSecurityPool(client, securityPoolAddresses.securityPool) + + const forkData = await getSecurityPoolForkerForkData(client, securityPoolAddresses.securityPool) + strictEqualTypeSafe(forkData.repAtFork, repBalance - burnAmount, 'rep at fork does not match deposit rep') + strictEqualTypeSafe(forkData.migratedRep, 0n, 'migrated rep should be 0 so far') + strictEqualTypeSafe(forkData.outcomeIndex, 0, 'there should be no outcome') + strictEqualTypeSafe(forkData.ownFork, true, 'should be own fork') const totalFeesOvedToVaultsRightAfterFork = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) - assert.strictEqual(await getSystemState(client, securityPoolAddresses.securityPool), SystemState.PoolForked, 'Parent is forked') - assert.strictEqual(0n, await getERC20Balance(client, getRepTokenAddress(genesisUniverse), securityPoolAddresses.securityPool), 'Parents original rep is gone') + strictEqualTypeSafe(await getSystemState(client, securityPoolAddresses.securityPool), SystemState.PoolForked, 'Parent is forked') + strictEqualTypeSafe(0n, await getERC20Balance(client, getRepTokenAddress(genesisUniverse), securityPoolAddresses.securityPool), 'Parents original rep is gone') await migrateVault(client, securityPoolAddresses.securityPool, QuestionOutcome.Yes) + await migrateFromEscalationGame(client, securityPoolAddresses.securityPool, client.account.address, QuestionOutcome.Yes, [0n]) const yesUniverse = getChildUniverseId(genesisUniverse, QuestionOutcome.Yes) - const yesSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, yesUniverse, questionId, securityMultiplier) + const yesSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, yesUniverse, marketId, securityMultiplier) - assert.strictEqual(await getSystemState(client, yesSecurityPool.securityPool), SystemState.ForkMigration, 'Fork Migration need to start') + strictEqualTypeSafe(await getSystemState(client, yesSecurityPool.securityPool), SystemState.ForkMigration, 'Fork Migration need to start') const migratedRep = await getMigratedRep(client, yesSecurityPool.securityPool) - approximatelyEqual(migratedRep, repBalance, 10n, 'correct amount rep migrated') + approximatelyEqual(migratedRep, repBalance - burnAmount, 10n, 'correct amount rep migrated') assert.ok(await contractExists(client, yesSecurityPool.securityPool), 'Did not create YES security pool') await mockWindow.advanceTime(8n * 7n * DAY + DAY) await startTruthAuction(client, yesSecurityPool.securityPool) - assert.strictEqual(await getSystemState(client, yesSecurityPool.securityPool), SystemState.Operational, 'yes System should be operational right away') - assert.strictEqual(await getCompleteSetCollateralAmount(client, yesSecurityPool.securityPool), openInterestAmount, 'child contract did not record the amount correctly') + strictEqualTypeSafe(await getSystemState(client, yesSecurityPool.securityPool), SystemState.Operational, 'yes System should be operational right away') + + const totalFees = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOvedToVaults(client, yesSecurityPool.securityPool) + approximatelyEqual(await getCompleteSetCollateralAmount(client, yesSecurityPool.securityPool), openInterestAmount - totalFees, 10n, 'child contract did not record the amount correctly') const totalFeesOvedToVaultsAfterFork = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) - assert.strictEqual(totalFeesOvedToVaultsRightAfterFork, totalFeesOvedToVaultsAfterFork, 'parents fees should be frozen') + strictEqualTypeSafe(totalFeesOvedToVaultsRightAfterFork, totalFeesOvedToVaultsAfterFork, 'parents fees should be frozen') }) test('two security pools with disagreement', async () => { - const questionData = await getQuestionData(client, questionId) - await mockWindow.setTime(questionData.endTime + 10000n) + const endTime = await getMarketEndDate(client, marketId) + await mockWindow.setTime(endTime + 10000n) const openInterestAmount = 10n * 10n ** 18n const openInterestArray = [openInterestAmount, openInterestAmount, openInterestAmount] const securityPoolAllowance = repDeposit / 4n await manipulatePriceOracleAndPerformOperation(client, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.SetSecurityBondsAllowance, client.account.address, securityPoolAllowance) const attackerClient = createWriteClient(mockWindow, TEST_ADDRESSES[1], 0) - await approveAndDepositRep(attackerClient, repDeposit) + await approveAndDepositRep(attackerClient, repDeposit, marketId) await manipulatePriceOracleAndPerformOperation(attackerClient, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.SetSecurityBondsAllowance, client.account.address, securityPoolAllowance) + const forkTreshold = (await getTotalTheoreticalSupply(client, await getRepToken(client, securityPoolAddresses.securityPool))) / 20n + + const zoltarForkTreshold = await getZoltarForkTreshold(client, genesisUniverse) + const burnAmount = zoltarForkTreshold / 5n + await depositRep(client, securityPoolAddresses.securityPool, 2n * forkTreshold) const repBalanceInGenesisPool = await getERC20Balance(client, getRepTokenAddress(genesisUniverse), securityPoolAddresses.securityPool) - assert.strictEqual(repBalanceInGenesisPool, 2n * repDeposit, 'After two deposits, the system should have 2 x repDeposit worth of REP') - assert.strictEqual(await getSecurityBondAllowance(client, securityPoolAddresses.securityPool), 2n * securityPoolAllowance, 'Security bond allowance should be 2x') - assert.strictEqual(await getPoolOwnershipDenominator(client, securityPoolAddresses.securityPool), repBalanceInGenesisPool * PRICE_PRECISION, 'Pool ownership denominator should equal `pool balance * PRICE_PRECISION` prior fork') + strictEqual18Decimal(repBalanceInGenesisPool, 2n * repDeposit + 2n * forkTreshold, 'After two deposits, the system should have 2 x repDeposit worth of REP + 2x fork') + strictEqual18Decimal(await getTotalSecurityBondAllowance(client, securityPoolAddresses.securityPool), 2n * securityPoolAllowance, 'Security bond allowance should be 2x') + strictEqual18Decimal(await getPoolOwnershipDenominator(client, securityPoolAddresses.securityPool), repBalanceInGenesisPool * PRICE_PRECISION, 'Pool ownership denominator should equal `pool balance * PRICE_PRECISION` prior fork') const openInterestHolder = createWriteClient(mockWindow, TEST_ADDRESSES[2], 0) await createCompleteSet(openInterestHolder, securityPoolAddresses.securityPool, openInterestAmount) assert.deepStrictEqual(await balanceOfSharesInCash(client, securityPoolAddresses.securityPool, securityPoolAddresses.shareToken, genesisUniverse, addressString(TEST_ADDRESSES[2])), openInterestArray, 'Did not create enough complete sets') - - await triggerFork(client, mockWindow, questionId) + await triggerOwnGameFork(client, securityPoolAddresses.securityPool) await forkSecurityPool(client, securityPoolAddresses.securityPool) - assert.deepStrictEqual(await balanceOfSharesInCash(client, securityPoolAddresses.securityPool, securityPoolAddresses.shareToken, genesisUniverse, addressString(TEST_ADDRESSES[2])), openInterestArray, 'Shares exist after fork') - await migrateShares(openInterestHolder, securityPoolAddresses.shareToken, genesisUniverse, QuestionOutcome.Yes) - await migrateShares(openInterestHolder, securityPoolAddresses.shareToken, genesisUniverse, QuestionOutcome.No) - await migrateShares(openInterestHolder, securityPoolAddresses.shareToken, genesisUniverse, QuestionOutcome.Invalid) + const yesUniverse = getChildUniverseId(genesisUniverse, QuestionOutcome.Yes) + const yesSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, yesUniverse, marketId, securityMultiplier) // we migrate to yes - const yesUniverse = getChildUniverseId(genesisUniverse, QuestionOutcome.Yes) - assert.ok(await isFinalized(client, yesUniverse, questionId), 'yes is finalized') - assert.strictEqual(await getWinningOutcome(client, yesUniverse, questionId), QuestionOutcome.Yes, 'finalized as yes') - const yesSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, yesUniverse, questionId, securityMultiplier) await migrateVault(client, securityPoolAddresses.securityPool, QuestionOutcome.Yes) + await migrateFromEscalationGame(client, securityPoolAddresses.securityPool, client.account.address, QuestionOutcome.Yes, [0n]) + const yesVault = await getSecurityVault(client, yesSecurityPool.securityPool, client.account.address) + const yesPoolBalance = await getERC20Balance(client, await getRepToken(client, yesSecurityPool.securityPool), yesSecurityPool.securityPool) + strictEqual18Decimal(await poolOwnershipToRep(client, yesSecurityPool.securityPool, yesVault.repDepositShare), yesPoolBalance-repDeposit, 'we should account for all the rep in yes pool (except attackers rep)') const migratedRepInYes = await getMigratedRep(client, yesSecurityPool.securityPool) - approximatelyEqual(repBalanceInGenesisPool / 2n, migratedRepInYes, 10n, 'half migrated to yes') - assert.strictEqual(await getERC20Balance(client, getRepTokenAddress(yesUniverse), yesSecurityPool.securityPool), repBalanceInGenesisPool, 'yes has all the rep') + strictEqual18Decimal(yesPoolBalance-repDeposit, migratedRepInYes, 'yes pool has the same rep as migrated rep') + strictEqualTypeSafe(await getMarketOutcome(client, yesSecurityPool.securityPool), QuestionOutcome.Yes, 'yes is finalized') + strictEqualTypeSafe(await getERC20Balance(client, getRepTokenAddress(yesUniverse), yesSecurityPool.securityPool), repBalanceInGenesisPool - burnAmount, 'yes has all the rep') + + assert.ok(await contractExists(client, yesSecurityPool.securityPool), 'yes security pool exist') + const feesOved = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOvedToVaults(client, yesSecurityPool.securityPool) // attacker migrated to No const noUniverse = getChildUniverseId(genesisUniverse, QuestionOutcome.No) - assert.ok(await isFinalized(client, noUniverse, questionId), 'no is finalized') - assert.strictEqual(await getWinningOutcome(client, noUniverse, questionId), QuestionOutcome.No, 'finalized as yes') - const noSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, noUniverse, questionId, securityMultiplier) + const noSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, noUniverse, marketId, securityMultiplier) await migrateVault(attackerClient, securityPoolAddresses.securityPool, QuestionOutcome.No) + strictEqualTypeSafe(await getMarketOutcome(client, noSecurityPool.securityPool), QuestionOutcome.No, 'finalized as yes') const migratedRepInNo = await getMigratedRep(client, noSecurityPool.securityPool) - approximatelyEqual(repBalanceInGenesisPool / 2n, migratedRepInNo, 10n, 'half migrated to no') - assert.strictEqual(await getERC20Balance(client, getRepTokenAddress(noUniverse), noSecurityPool.securityPool), repBalanceInGenesisPool, 'no has all the rep') + approximatelyEqual(migratedRepInNo, repDeposit, 10n, 'other side migrated to no') + strictEqualTypeSafe(await getERC20Balance(client, getRepTokenAddress(noUniverse), noSecurityPool.securityPool), repBalanceInGenesisPool - burnAmount, 'no has all the rep') + + approximatelyEqual(await getETHBalance(client, securityPoolAddresses.securityPool), await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool), 10n, 'there should be only fees left in old security pool') // invalid, no one migrated here await createChildUniverse(client, securityPoolAddresses.securityPool, QuestionOutcome.Invalid) // no one migrated, we need to create the universe as rep holders did not const invalidUniverse = getChildUniverseId(genesisUniverse, QuestionOutcome.Invalid) - const invalidSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, invalidUniverse, questionId, securityMultiplier) - assert.ok(await isFinalized(client, invalidUniverse, questionId), 'invalid is finalized') - assert.strictEqual(await getWinningOutcome(client, invalidUniverse, questionId), QuestionOutcome.Invalid, 'finalized as invalid') + const invalidSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, invalidUniverse, marketId, securityMultiplier) await mockWindow.advanceTime(8n * 7n * DAY + DAY) + const getCurrentOpenInterestArray = async () => { + const currentFees = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOvedToVaults(client, yesSecurityPool.securityPool) + return openInterestArray.map((x) => x - currentFees) + } + // auction yes + const repAtFork = (await getSecurityPoolForkerForkData(client, securityPoolAddresses.securityPool)).repAtFork + const completeSetAmount = await getCompleteSetCollateralAmount(client, securityPoolAddresses.securityPool) + const auctionedEthInYes = completeSetAmount-completeSetAmount * migratedRepInYes / repAtFork await startTruthAuction(client, yesSecurityPool.securityPool) - assert.strictEqual(await getSystemState(client, yesSecurityPool.securityPool), SystemState.ForkTruthAuction, 'Auction started') - approximatelyEqual(await getEthAmountToBuy(client, yesSecurityPool.truthAuction), openInterestAmount / 2n, 10n, 'Need to buy half of open interest') + strictEqualTypeSafe(await getSystemState(client, yesSecurityPool.securityPool), SystemState.ForkTruthAuction, 'Auction started') + approximatelyEqual(await getEthAmountToBuy(client, yesSecurityPool.truthAuction), auctionedEthInYes, 10n, 'Need to buy half of open interest on yes') // participate yes auction by buying quarter of all REP (this is a open interest and rep holder happy case where REP holders win 50%) const yesAuctionParticipant = createWriteClient(mockWindow, TEST_ADDRESSES[3], 0) - await participateAuction(yesAuctionParticipant, yesSecurityPool.truthAuction, repBalanceInGenesisPool / 4n, openInterestAmount / 2n) + await participateAuction(yesAuctionParticipant, yesSecurityPool.truthAuction, repBalanceInGenesisPool / 4n, auctionedEthInYes) // auction no + const auctionedEthInNo = completeSetAmount-completeSetAmount * migratedRepInNo / repAtFork await startTruthAuction(client, noSecurityPool.securityPool) - assert.strictEqual(await getSystemState(client, noSecurityPool.securityPool), SystemState.ForkTruthAuction, 'Auction started') - approximatelyEqual(await getEthAmountToBuy(client, noSecurityPool.truthAuction), openInterestAmount / 2n, 10n, 'Need to buy half of open interest') + strictEqualTypeSafe(await getSystemState(client, noSecurityPool.securityPool), SystemState.ForkTruthAuction, 'Auction started') + approximatelyEqual(await getEthAmountToBuy(client, noSecurityPool.truthAuction), auctionedEthInNo, 10n, 'Need to buy half of open interest on no') // participate no auction by buying 3/4 of all REP (this is a open interest happy case where REP holders lose 50%) const noAuctionParticipant = createWriteClient(mockWindow, TEST_ADDRESSES[4], 0) - await participateAuction(noAuctionParticipant, noSecurityPool.truthAuction, repBalanceInGenesisPool * 3n / 4n, openInterestAmount / 2n) + await participateAuction(noAuctionParticipant, noSecurityPool.truthAuction, repBalanceInGenesisPool * 3n / 4n, auctionedEthInNo) // auction invalid await startTruthAuction(client, invalidSecurityPool.securityPool) - assert.strictEqual(await getSystemState(client, invalidSecurityPool.securityPool), SystemState.ForkTruthAuction, 'Auction started') - approximatelyEqual(await getEthAmountToBuy(client, invalidSecurityPool.truthAuction), openInterestAmount, 10n, 'Need to buy all of open interest') - // participate no auction by buying 3/4 of all REP (this is a open interest happy case where REP holders lose 50%) + strictEqualTypeSafe(await getSystemState(client, invalidSecurityPool.securityPool), SystemState.ForkTruthAuction, 'Auction started') + approximatelyEqual(await getEthAmountToBuy(client, invalidSecurityPool.truthAuction), completeSetAmount, 10n, 'Need to buy all of open interest on invalid') const invalidAuctionParticipant = createWriteClient(mockWindow, TEST_ADDRESSES[5], 0) // buy half of the open interest for 3/4 of everything - await participateAuction(invalidAuctionParticipant, invalidSecurityPool.truthAuction, repBalanceInGenesisPool - repBalanceInGenesisPool / 1_000_000n, openInterestAmount / 2n) + await participateAuction(invalidAuctionParticipant, invalidSecurityPool.truthAuction, repBalanceInGenesisPool - burnAmount - repBalanceInGenesisPool / 1_000_000n, completeSetAmount / 2n) await mockWindow.advanceTime(7n * DAY + DAY) // yes status: auction fully funds, 1/4 of rep balance is sold for eth await finalizeTruthAuction(client, yesSecurityPool.securityPool) - assert.deepStrictEqual(await balanceOfSharesInCash(client, yesSecurityPool.securityPool, yesSecurityPool.shareToken, yesUniverse, addressString(TEST_ADDRESSES[2])), openInterestArray, 'Not enough shares in yes') - approximatelyEqual(await getCompleteSetCollateralAmount(client, yesSecurityPool.securityPool), openInterestAmount, 10n, 'yes child contract did not record the amount correctly') - assert.strictEqual(await getSystemState(client, yesSecurityPool.securityPool), SystemState.Operational, 'Yes System should be operational again') + assert.deepStrictEqual(await balanceOfSharesInCash(client, securityPoolAddresses.securityPool, securityPoolAddresses.shareToken, genesisUniverse, addressString(TEST_ADDRESSES[2])), openInterestArray.map((x) => x - feesOved), 'Shares exist after fork') + await migrateShares(openInterestHolder, securityPoolAddresses.shareToken, genesisUniverse, QuestionOutcome.Yes, [0n, 1n, 2n]) + await migrateShares(openInterestHolder, securityPoolAddresses.shareToken, genesisUniverse, QuestionOutcome.No, [0n, 1n, 2n]) + await migrateShares(openInterestHolder, securityPoolAddresses.shareToken, genesisUniverse, QuestionOutcome.Invalid, [0n, 1n, 2n]) + + assert.deepStrictEqual(await balanceOfSharesInCash(client, yesSecurityPool.securityPool, yesSecurityPool.shareToken, yesUniverse, addressString(TEST_ADDRESSES[2])), [completeSetAmount, completeSetAmount, completeSetAmount], 'Not enough shares in yes') + + approximatelyEqual(await getCompleteSetCollateralAmount(client, yesSecurityPool.securityPool), (await getCurrentOpenInterestArray())[0], 10n, 'yes child contract did not record the amount correctly') + strictEqualTypeSafe(await getSystemState(client, yesSecurityPool.securityPool), SystemState.Operational, 'Yes System should be operational again') await claimAuctionProceeds(client, yesSecurityPool.securityPool, yesAuctionParticipant.account.address) const yesAuctionParticipantVault = await getSecurityVault(client, yesSecurityPool.securityPool, yesAuctionParticipant.account.address) @@ -292,21 +366,22 @@ describe('Peripherals Contract Test Suite', () => { const originalYesVault = await getSecurityVault(client, yesSecurityPool.securityPool, client.account.address) const originalYesVaultRep = await poolOwnershipToRep(client, yesSecurityPool.securityPool, originalYesVault.repDepositShare) - approximatelyEqual(originalYesVaultRep, repBalanceInGenesisPool * 3n / 4n, 10000n, 'original yes vault holder should hold rest 3/4 of rep') - assert.strictEqual((await getSecurityVault(client, yesSecurityPool.securityPool, attackerClient.account.address)).repDepositShare, 0n, 'attacker should have zero as they did not migrate to yes') + approximatelyEqual(originalYesVaultRep, repBalanceInGenesisPool * 3n / 4n-burnAmount, 10000n, 'original yes vault holder should hold rest 3/4 of rep') + strictEqualTypeSafe((await getSecurityVault(client, yesSecurityPool.securityPool, attackerClient.account.address)).repDepositShare, 0n, 'attacker should have zero as they did not migrate to yes') const balancePriorYesRedeemal = await getETHBalance(client, addressString(TEST_ADDRESSES[2])) await redeemShares(openInterestHolder, yesSecurityPool.securityPool) - assert.deepStrictEqual(await balanceOfSharesInCash(client, yesSecurityPool.securityPool, securityPoolAddresses.shareToken, yesUniverse, addressString(TEST_ADDRESSES[2])), [openInterestAmount, 0n, openInterestAmount], 'Not enough shares') + const currentShares = await getCurrentOpenInterestArray() + assert.deepStrictEqual(await balanceOfSharesInCash(client, yesSecurityPool.securityPool, securityPoolAddresses.shareToken, yesUniverse, addressString(TEST_ADDRESSES[2])), [currentShares[0], 0n, currentShares[2]], 'Not enough shares 1') const fees = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOvedToVaults(client, yesSecurityPool.securityPool) approximatelyEqual(await getETHBalance(client, addressString(TEST_ADDRESSES[2])), balancePriorYesRedeemal + openInterestAmount - fees, 10n ** 15n, 'did not gain eth after redeeming yes shares') // no status: auction fully funds, 3/4 of rep balance is sold for eth await finalizeTruthAuction(client, noSecurityPool.securityPool) - assert.deepStrictEqual(await balanceOfSharesInCash(client, noSecurityPool.securityPool, noSecurityPool.shareToken, noUniverse, addressString(TEST_ADDRESSES[2])), openInterestArray, 'Not enough shares in no') + assert.deepStrictEqual(await balanceOfSharesInCash(client, noSecurityPool.securityPool, noSecurityPool.shareToken, noUniverse, addressString(TEST_ADDRESSES[2])), currentShares, 'Not enough shares in no') - assert.strictEqual(await getSystemState(client, noSecurityPool.securityPool), SystemState.Operational, 'No System should be operational again') - assert.strictEqual(await getCompleteSetCollateralAmount(client, noSecurityPool.securityPool), openInterestAmount, 'no child contract did not record the amount correctly') + strictEqualTypeSafe(await getSystemState(client, noSecurityPool.securityPool), SystemState.Operational, 'No System should be operational again') + strictEqualTypeSafe(await getCompleteSetCollateralAmount(client, noSecurityPool.securityPool), currentShares[0], 'no child contract did not record the amount correctly') await claimAuctionProceeds(client, noSecurityPool.securityPool, noAuctionParticipant.account.address) const noAuctionParticipantVault = await getSecurityVault(client, noSecurityPool.securityPool, noAuctionParticipant.account.address) @@ -315,62 +390,66 @@ describe('Peripherals Contract Test Suite', () => { const originalNoVault = await getSecurityVault(client, noSecurityPool.securityPool, attackerClient.account.address) const originalNoVaultRep = await poolOwnershipToRep(client, noSecurityPool.securityPool, originalNoVault.repDepositShare) - approximatelyEqual(originalNoVaultRep, repBalanceInGenesisPool * 1n / 4n, 10000n, 'original no vault holder should hold rest 1/4 of rep') - assert.strictEqual((await getSecurityVault(client, noSecurityPool.securityPool, client.account.address)).repDepositShare, 0n, 'client should have zero as they did not migrate to no') + approximatelyEqual(originalNoVaultRep, repBalanceInGenesisPool * 1n / 4n - burnAmount, 10000n, 'original no vault holder should hold rest 1/4 of rep') + strictEqualTypeSafe((await getSecurityVault(client, noSecurityPool.securityPool, client.account.address)).repDepositShare, 0n, 'client should have zero as they did not migrate to no') const balancePriorNoRedeemal = await getETHBalance(client, addressString(TEST_ADDRESSES[2])) await redeemShares(openInterestHolder, noSecurityPool.securityPool) - assert.deepStrictEqual(await balanceOfSharesInCash(client, noSecurityPool.securityPool, securityPoolAddresses.shareToken, noUniverse, addressString(TEST_ADDRESSES[2])), [openInterestAmount, openInterestAmount, 0n], 'Not enough shares') + assert.deepStrictEqual(await balanceOfSharesInCash(client, noSecurityPool.securityPool, noSecurityPool.shareToken, noUniverse, addressString(TEST_ADDRESSES[2])), [currentShares[0], currentShares[1], 0n], 'Not enough shares 2') approximatelyEqual(await getETHBalance(client, addressString(TEST_ADDRESSES[2])), balancePriorNoRedeemal + openInterestAmount-fees, 10n ** 15n, 'did not gain eth after redeeming no shares') // invalid status: auction 3/4 funds for all REP (minus 1/100 000). Open interest holders lose 50% await finalizeTruthAuction(client, invalidSecurityPool.securityPool) - assert.deepStrictEqual(await balanceOfSharesInCash(client, invalidSecurityPool.securityPool, securityPoolAddresses.shareToken, invalidUniverse, addressString(TEST_ADDRESSES[2])), openInterestArray.map((x) => x / 2n), 'Not enough shares in invalid') - assert.strictEqual(await getSystemState(client, invalidSecurityPool.securityPool), SystemState.Operational, 'Invalid System should be operational again') - assert.strictEqual(await getCompleteSetCollateralAmount(client, invalidSecurityPool.securityPool), openInterestAmount / 2n, 'Invalid child contract did not record the amount correctly') + assert.deepStrictEqual(await balanceOfSharesInCash(client, invalidSecurityPool.securityPool, invalidSecurityPool.shareToken, invalidUniverse, addressString(TEST_ADDRESSES[2])), currentShares.map((x) => x / 2n), 'Not enough shares in invalid') + strictEqualTypeSafe(await getSystemState(client, invalidSecurityPool.securityPool), SystemState.Operational, 'Invalid System should be operational again') + approximatelyEqual(await getCompleteSetCollateralAmount(client, invalidSecurityPool.securityPool), currentShares[0] / 2n, 10n, 'Invalid child contract did not record the amount correctly') await claimAuctionProceeds(client, invalidSecurityPool.securityPool, invalidAuctionParticipant.account.address) const invalidAuctionParticipantVault = await getSecurityVault(client, invalidSecurityPool.securityPool, invalidAuctionParticipant.account.address) const invalidAuctionParticipantRep = await poolOwnershipToRep(client, invalidSecurityPool.securityPool, invalidAuctionParticipantVault.repDepositShare) - approximatelyEqual(invalidAuctionParticipantRep, repBalanceInGenesisPool - repBalanceInGenesisPool / 1_000_000n, 10000n, 'Invalid auction participant did not get ownership of rep they bought') + approximatelyEqual(invalidAuctionParticipantRep, repBalanceInGenesisPool - repBalanceInGenesisPool / 1_000_000n - burnAmount, 10000n, 'Invalid auction participant did not get ownership of rep they bought') + // try creating new complete sets const openInterestHolder2 = createWriteClient(mockWindow, TEST_ADDRESSES[4], 0) - await createCompleteSet(openInterestHolder2, invalidSecurityPool.securityPool, openInterestAmount) + await createCompleteSet(openInterestHolder2, invalidSecurityPool.securityPool, currentShares[0]) const balancePriorInvalidRedeemal = await getETHBalance(client, addressString(TEST_ADDRESSES[2])) await redeemShares(openInterestHolder, invalidSecurityPool.securityPool) - assert.deepStrictEqual(await balanceOfSharesInCash(client, invalidSecurityPool.securityPool, invalidSecurityPool.shareToken, invalidUniverse, addressString(TEST_ADDRESSES[2])), [0n, openInterestAmount, openInterestAmount].map((x) => x / 2n), 'Not enough shares after redeeming invalid 1') + assert.deepStrictEqual(await balanceOfSharesInCash(client, invalidSecurityPool.securityPool, invalidSecurityPool.shareToken, invalidUniverse, addressString(TEST_ADDRESSES[2])), [0n, currentShares[1], currentShares[2]].map((x) => x / 2n), 'Not enough shares after redeeming invalid 1') approximatelyEqual(await getETHBalance(client, addressString(TEST_ADDRESSES[2])), balancePriorInvalidRedeemal + (openInterestAmount - fees) / 2n, 10n ** 15n, 'did not gain eth after redeeming invalid shares') const balancePriorInvalidRedeemal2 = await getETHBalance(client, addressString(TEST_ADDRESSES[4])) await redeemShares(openInterestHolder2, invalidSecurityPool.securityPool) - assert.deepStrictEqual(await balanceOfSharesInCash(client, invalidSecurityPool.securityPool, invalidSecurityPool.shareToken, invalidUniverse, addressString(TEST_ADDRESSES[4])), [0n, openInterestAmount, openInterestAmount], 'Not enough shares after redeeming invalid 2') - approximatelyEqual(await getETHBalance(client, addressString(TEST_ADDRESSES[4])), balancePriorInvalidRedeemal2 + openInterestAmount, 10n ** 15n, 'did not gain eth after redeeming invalid shares') + assert.deepStrictEqual(await balanceOfSharesInCash(client, invalidSecurityPool.securityPool, invalidSecurityPool.shareToken, invalidUniverse, addressString(TEST_ADDRESSES[4])), [0n, currentShares[1], currentShares[2]], 'Not enough shares after redeeming invalid 2') + approximatelyEqual(await getETHBalance(client, addressString(TEST_ADDRESSES[4])), balancePriorInvalidRedeemal2 + currentShares[0], 10n ** 15n, 'did not gain eth after redeeming invalid shares') }) test('can fork zero rep pools', async () => { - const questionData = await getQuestionData(client, questionId) - await mockWindow.setTime(questionData.endTime + 10000n) + const endTime = await getMarketEndDate(client, marketId) + await mockWindow.setTime(endTime + 10000n) await manipulatePriceOracleAndPerformOperation(client, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.WithdrawRep, client.account.address, repDeposit) - assert.strictEqual(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') + strictEqualTypeSafe(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') approximatelyEqual(await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddresses.securityPool), 0n, 100n, 'Did not empty security pool of rep') - approximatelyEqual(await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), client.account.address), startBalance - reportBond, 100n, 'Did not get rep back') - await triggerFork(client, mockWindow, questionId) + approximatelyEqual(await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), client.account.address), startBalance, 100n, 'Did not get rep back') + + await approveToken(client, addressString(GENESIS_REPUTATION_TOKEN), getZoltarAddress()) + await forkUniverse(client, genesisUniverse, marketText, outcomes) await forkSecurityPool(client, securityPoolAddresses.securityPool) - assert.strictEqual(await getSystemState(client, securityPoolAddresses.securityPool), SystemState.PoolForked, 'Parent is forked') + strictEqualTypeSafe(await getSystemState(client, securityPoolAddresses.securityPool), SystemState.PoolForked, 'Parent is forked') await migrateVault(client, securityPoolAddresses.securityPool, QuestionOutcome.Yes) const yesUniverse = getChildUniverseId(genesisUniverse, QuestionOutcome.Yes) - const yesSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, yesUniverse, questionId, securityMultiplier) + const yesSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, yesUniverse, marketId, securityMultiplier) - assert.strictEqual(await getSystemState(client, yesSecurityPool.securityPool), SystemState.ForkMigration, 'Fork Migration need to start') + strictEqualTypeSafe(await getSystemState(client, yesSecurityPool.securityPool), SystemState.ForkMigration, 'Fork Migration need to start') const migratedRep = await getMigratedRep(client, yesSecurityPool.securityPool) - assert.strictEqual(migratedRep, 0n, 'correct amount rep migrated') + strictEqualTypeSafe(migratedRep, 0n, 'correct amount rep migrated') assert.ok(await contractExists(client, yesSecurityPool.securityPool), 'Did not create YES security pool') await mockWindow.advanceTime(8n * 7n * DAY + DAY) await startTruthAuction(client, yesSecurityPool.securityPool) - assert.strictEqual(await getSystemState(client, yesSecurityPool.securityPool), SystemState.Operational, 'yes System should be operational right away') - assert.strictEqual(await getCompleteSetCollateralAmount(client, yesSecurityPool.securityPool), 0n, 'child contract did not record the amount correctly') + strictEqualTypeSafe(await getSystemState(client, yesSecurityPool.securityPool), SystemState.Operational, 'yes System should be operational right away') + strictEqualTypeSafe(await getCompleteSetCollateralAmount(client, yesSecurityPool.securityPool), 0n, 'child contract did not record the amount correctly') }) // - todo test that users can claim their stuff (shares+rep) even if zoltar forks after market ends }) + diff --git a/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts new file mode 100644 index 0000000..6e59d23 --- /dev/null +++ b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts @@ -0,0 +1,203 @@ +import 'viem/window' +import { encodeDeployData, getCreate2Address, keccak256, numberToBytes, toHex, encodePacked, zeroAddress } from 'viem' +import { WriteClient } from '../viem.js' +import { PROXY_DEPLOYER_ADDRESS } from '../constants.js' +import { addressString } from '../bigint.js' +import { contractExists } from '../utilities.js' +import { mainnet } from 'viem/chains' +import { peripherals_Auction_Auction, peripherals_EscalationGame_EscalationGame, peripherals_factories_AuctionFactory_AuctionFactory, peripherals_factories_EscalationGameFactory_EscalationGameFactory, peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory, peripherals_factories_SecurityPoolFactory_SecurityPoolFactory, peripherals_factories_ShareTokenFactory_ShareTokenFactory, peripherals_openOracle_OpenOracle_OpenOracle, peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer, peripherals_SecurityPool_SecurityPool, peripherals_SecurityPoolForker_SecurityPoolForker, peripherals_SecurityPoolUtils_SecurityPoolUtils, peripherals_tokens_ShareToken_ShareToken, peripherals_YesNoMarkets_YesNoMarkets, Zoltar_Zoltar } from '../../../../types/contractArtifact.js' +import { objectEntries } from '../typescript.js' +import { getRepTokenAddress, getZoltarAddress } from './zoltar.js' + +export const getSecurityPoolUtilsAddress = () => getCreate2Address({ bytecode: `0x${ peripherals_SecurityPoolUtils_SecurityPoolUtils.evm.bytecode.object }`, from: addressString(PROXY_DEPLOYER_ADDRESS), salt: numberToBytes(0) }) + +export const applyLibraries = (bytecode: string): `0x${ string }` => { + const securityPoolUtils = keccak256(toHex('contracts/peripherals/SecurityPoolUtils.sol:SecurityPoolUtils')).slice(2, 36) + const replaceLib = (bytecode: string, hash: string, replaceWithAddress: `0x${ string }`) => bytecode.replaceAll(`__$${ hash }$__`, replaceWithAddress.slice(2).toLocaleLowerCase()) + return `0x${ replaceLib(bytecode, securityPoolUtils, getSecurityPoolUtilsAddress()) }` +} + +export const getSecurityPoolForkerByteCode = (zoltar: `0x${ string }`) => { + return encodeDeployData({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + bytecode: applyLibraries(peripherals_SecurityPoolForker_SecurityPoolForker.evm.bytecode.object), + args: [ zoltar ] + }) +} + +export const getSecurityPoolFactoryByteCode = (securityPoolForker: `0x${ string }`, yesNoMarkets: `0x${ string }`, escalationGameFactory: `0x${ string }`, openOracle: `0x${ string }`, zoltar: `0x${ string }`, shareTokenFactory: `0x${ string }`, auctionFactory: `0x${ string }`, priceOracleManagerAndOperatorQueuerFactory: `0x${ string }`) => { + return encodeDeployData({ + abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi, + bytecode: applyLibraries(peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.evm.bytecode.object), + args: [ securityPoolForker, yesNoMarkets, escalationGameFactory, openOracle, zoltar, shareTokenFactory, auctionFactory, priceOracleManagerAndOperatorQueuerFactory ] + }) +} + +export const getSecurityPoolFactoryAddress = (securityPoolForker: `0x${ string }`, yesNoMarkets: `0x${ string }`, escalationGameFactory: `0x${ string }`, openOracle: `0x${ string }`, zoltar: `0x${ string }`, shareTokenFactory: `0x${ string }`, auctionFactory: `0x${ string }`, priceOracleManagerAndOperatorQueuerFactory: `0x${ string }`) => { + return getCreate2Address({ + from: addressString(PROXY_DEPLOYER_ADDRESS), + salt: numberToBytes(0), + bytecode: getSecurityPoolFactoryByteCode(securityPoolForker, yesNoMarkets, escalationGameFactory, openOracle, zoltar, shareTokenFactory, auctionFactory, priceOracleManagerAndOperatorQueuerFactory) + }) +} + +export const getShareTokenFactoryByteCode = (zoltar: `0x${ string }`) => { + return encodeDeployData({ + abi: peripherals_factories_ShareTokenFactory_ShareTokenFactory.abi, + bytecode: `0x${ peripherals_factories_ShareTokenFactory_ShareTokenFactory.evm.bytecode.object }`, + args: [ zoltar ] + }) +} + +export const getYesNoMarketsByteCode = () => { + return encodeDeployData({ + abi: peripherals_YesNoMarkets_YesNoMarkets.abi, + bytecode: `0x${ peripherals_YesNoMarkets_YesNoMarkets.evm.bytecode.object }` + }) +} +export const getEscalationGameFactoryByteCode = () => { + return encodeDeployData({ + abi: peripherals_factories_EscalationGameFactory_EscalationGameFactory.abi, + bytecode: `0x${ peripherals_factories_EscalationGameFactory_EscalationGameFactory.evm.bytecode.object }` + }) +} + +export function getInfraContractAddresses() { + const getAddress = (bytecode: `0x${ string }`) => getCreate2Address({ bytecode, from: addressString(PROXY_DEPLOYER_ADDRESS), salt: numberToBytes(0) }) + + const contracts = { + securityPoolUtils: getAddress(`0x${ peripherals_SecurityPoolUtils_SecurityPoolUtils.evm.bytecode.object }`), + openOracle: getAddress(`0x${ peripherals_openOracle_OpenOracle_OpenOracle.evm.bytecode.object }`), + zoltar: getZoltarAddress(), + shareTokenFactory: getAddress(getShareTokenFactoryByteCode(getZoltarAddress())), + auctionFactory: getAddress(`0x${ peripherals_factories_AuctionFactory_AuctionFactory.evm.bytecode.object }`), + priceOracleManagerAndOperatorQueuerFactory: getAddress(`0x${ peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory.evm.bytecode.object }`), + securityPoolForker: getAddress(getSecurityPoolForkerByteCode(getZoltarAddress())), + yesNoMarkets: getAddress(getYesNoMarketsByteCode()), + escalationGameFactory: getAddress(getEscalationGameFactoryByteCode()), + } + const securityPoolFactory = getSecurityPoolFactoryAddress(contracts.securityPoolForker, contracts.yesNoMarkets, contracts.escalationGameFactory, contracts.openOracle, contracts.zoltar, contracts.shareTokenFactory, contracts.auctionFactory, contracts.priceOracleManagerAndOperatorQueuerFactory) + return { ...contracts, securityPoolFactory } +} + +export async function getInfraDeployedInformation(client: WriteClient): Promise<{ [key in keyof ReturnType]: boolean }> { + const contractAddresses = getInfraContractAddresses() + type ContractKeys = keyof typeof contractAddresses + + const contractKeys = Object.keys(contractAddresses) as ContractKeys[] + + const contractExistencePairs = await Promise.all( + contractKeys.map(async key => { + const doesExist = await contractExists(client, contractAddresses[key]) + return [key, doesExist] as const + }) + ) + + const contractExistenceObject: { [key in ContractKeys]: boolean } = {} as { [key in ContractKeys]: boolean } + contractExistencePairs.forEach(([key, doesExist]) => { + contractExistenceObject[key] = doesExist + }) + + return contractExistenceObject +} +export async function ensureInfraDeployed(client: WriteClient): Promise { + const contractAddresses = getInfraContractAddresses() + const existence = await getInfraDeployedInformation(client) + + const deployBytecode = async (bytecode: `0x${ string }`) => await client.sendTransaction({ to: addressString(PROXY_DEPLOYER_ADDRESS), data: bytecode }) + + if (!existence.securityPoolUtils) await deployBytecode(`0x${ peripherals_SecurityPoolUtils_SecurityPoolUtils.evm.bytecode.object }`) + if (!existence.openOracle) await deployBytecode(`0x${ peripherals_openOracle_OpenOracle_OpenOracle.evm.bytecode.object }`) + if (!existence.zoltar) await deployBytecode(`0x${ Zoltar_Zoltar.evm.bytecode.object }`) + if (!existence.shareTokenFactory) await deployBytecode(getShareTokenFactoryByteCode(getZoltarAddress())) + if (!existence.auctionFactory) await deployBytecode(`0x${ peripherals_factories_AuctionFactory_AuctionFactory.evm.bytecode.object }`) + if (!existence.priceOracleManagerAndOperatorQueuerFactory) await deployBytecode(`0x${ peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory.evm.bytecode.object }`) + if (!existence.securityPoolFactory) await deployBytecode(getSecurityPoolFactoryByteCode(contractAddresses.securityPoolForker, contractAddresses.yesNoMarkets, contractAddresses.escalationGameFactory, contractAddresses.openOracle, contractAddresses.zoltar, contractAddresses.shareTokenFactory, contractAddresses.auctionFactory, contractAddresses.priceOracleManagerAndOperatorQueuerFactory)) + if (!existence.yesNoMarkets) await deployBytecode(getYesNoMarketsByteCode()) + if (!existence.escalationGameFactory) await deployBytecode(getEscalationGameFactoryByteCode()) + if (!existence.securityPoolForker) await deployBytecode(getSecurityPoolForkerByteCode(contractAddresses.zoltar)) + + for (const [name, contractAddress] of objectEntries(contractAddresses)) { + if (!(await contractExists(client, contractAddress))) throw new Error(`${ name } does not exist eventhought we deployed it`) + } +} + +const computeSecurityPoolSalt = (parent: `0x${ string }`, universeId: bigint, marketId: bigint, securityMultiplier: bigint) => { + const types = ['address', 'uint248', 'uint256', 'uint256'] as const + const values = [parent, universeId, marketId, securityMultiplier] as const + return keccak256(encodePacked(types, values)) +} + +const computeShareTokenSalt = (securityMultiplier: bigint, marketId: bigint) => { + const types = ['uint256', 'uint256'] as const + const values = [securityMultiplier, marketId] as const + return keccak256(encodePacked(types, values)) +} + +export const getMarketId = (universeId: bigint, securityMultiplier: bigint, extraInfo: string, marketEndDate: bigint) => { + const securityPoolfactory = getInfraContractAddresses().securityPoolFactory + const salt = keccak256(encodePacked(['address', 'uint248', 'uint256', 'string', 'uint256'], [securityPoolfactory, universeId, securityMultiplier, extraInfo, marketEndDate])) + return BigInt(keccak256(encodePacked(['address', 'string', 'uint256', 'bytes32'], [securityPoolfactory, extraInfo, marketEndDate, salt]))); +} + +export const getSecurityPoolAddresses = (parent: `0x${ string }`, universeId: bigint, marketId: bigint, securityMultiplier: bigint) => { + const securityPoolSalt = computeSecurityPoolSalt(parent, universeId, marketId, securityMultiplier) + const infraContracts = getInfraContractAddresses() + const securityPoolSaltWithMsgSender = keccak256(encodePacked(['address', 'bytes32'] as const, [infraContracts.securityPoolFactory, securityPoolSalt])) + + const contracts = { + priceOracleManagerAndOperatorQueuer: getCreate2Address({ + bytecode: encodeDeployData({ + abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, + bytecode: `0x${ peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.evm.bytecode.object }`, + args: [ infraContracts.openOracle, getRepTokenAddress(universeId) ] + }), + from: infraContracts.priceOracleManagerAndOperatorQueuerFactory, + salt: securityPoolSaltWithMsgSender + }), + shareToken: getCreate2Address({ + bytecode: encodeDeployData({ + abi: peripherals_tokens_ShareToken_ShareToken.abi, + bytecode: `0x${ peripherals_tokens_ShareToken_ShareToken.evm.bytecode.object }`, + args: [ infraContracts.securityPoolFactory, infraContracts.zoltar ] + }), + from: infraContracts.shareTokenFactory, + salt: computeShareTokenSalt(securityMultiplier, marketId) + }), + truthAuction: BigInt(parent) == 0n ? zeroAddress : getCreate2Address({ + bytecode: `0x${ peripherals_Auction_Auction.evm.bytecode.object }`, + from: infraContracts.auctionFactory, + salt: securityPoolSaltWithMsgSender + }), + } + const securityPool = getCreate2Address({ + bytecode: encodeDeployData({ + abi: peripherals_SecurityPool_SecurityPool.abi, + bytecode: applyLibraries(peripherals_SecurityPool_SecurityPool.evm.bytecode.object), + args: [ infraContracts.securityPoolForker, infraContracts.securityPoolFactory, infraContracts.yesNoMarkets, infraContracts.escalationGameFactory, contracts.priceOracleManagerAndOperatorQueuer, contracts.shareToken, infraContracts.openOracle, parent, infraContracts.zoltar, universeId, marketId, securityMultiplier] as const + }), + from: infraContracts.securityPoolFactory, + salt: numberToBytes(0) + }) + const escalationGame = getCreate2Address({ + bytecode: encodeDeployData({ + abi: peripherals_EscalationGame_EscalationGame.abi, + bytecode: `0x${ peripherals_EscalationGame_EscalationGame.evm.bytecode.object }`, + args: [ securityPool ] + }), + from: infraContracts.escalationGameFactory, + salt: numberToBytes(0) + }) + return { ...contracts, securityPool, escalationGame } +} + +export const deployOriginSecurityPool = async (client: WriteClient, universeId: bigint, extraInfo: string, marketEndDate: bigint, securityMultiplier: bigint, startingRetentionRate: bigint, startingRepEthPrice: bigint) => { + const infraAddresses = getInfraContractAddresses() + return await client.writeContract({ + chain: mainnet, + abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi, + functionName: 'deployOriginSecurityPool', + address: infraAddresses.securityPoolFactory, + args: [universeId, extraInfo, marketEndDate, securityMultiplier, startingRetentionRate, startingRepEthPrice] + }) +} diff --git a/solidity/ts/testsuite/simulator/utils/contracts/deployments.ts b/solidity/ts/testsuite/simulator/utils/contracts/deployments.ts new file mode 100644 index 0000000..0b2dae5 --- /dev/null +++ b/solidity/ts/testsuite/simulator/utils/contracts/deployments.ts @@ -0,0 +1,141 @@ +import { peripherals_interfaces_IAugur_IAugur, IERC20_IERC20, peripherals_interfaces_IWeth9_IWeth9, peripherals_Auction_Auction, peripherals_openOracle_OpenOracle_OpenOracle, peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer, peripherals_SecurityPool_SecurityPool, peripherals_factories_AuctionFactory_AuctionFactory, ReputationToken_ReputationToken, Zoltar_Zoltar, peripherals_SecurityPoolUtils_SecurityPoolUtils, peripherals_tokens_ShareToken_ShareToken, peripherals_factories_SecurityPoolFactory_SecurityPoolFactory, peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory, peripherals_factories_ShareTokenFactory_ShareTokenFactory, peripherals_factories_EscalationGameFactory_EscalationGameFactory, peripherals_YesNoMarkets_YesNoMarkets, peripherals_SecurityPoolForker_SecurityPoolForker, peripherals_EscalationGame_EscalationGame } from '../../../../types/contractArtifact.js' +import { QuestionOutcome } from '../../types/types.js' +import { addressString } from '../bigint.js' +import { ETHEREUM_LOGS_LOGGER_ADDRESS, TEST_ADDRESSES, WETH_ADDRESS } from '../constants.js' +import { Deployment } from '../logExplaining.js' +import { getInfraContractAddresses, getSecurityPoolAddresses } from './deployPeripherals.js' +import { zeroAddress } from 'viem' +import { getChildUniverseId } from '../utilities.js' +import { getRepTokenAddress } from './zoltar.js' + +const getUniverseName = (universeId: bigint): string => { + if (universeId === 0n) return 'Genesis' + return `Universe-${ universeId }` + /* + const path: string[] = [] + let currentUniverseId = universeId + while (currentUniverseId > 0n) { + const branchIndex = Number(currentUniverseId & 0b11n) - 1 + const outcomeName = QuestionOutcome[branchIndex] as keyof typeof QuestionOutcome + path.push(outcomeName) + currentUniverseId = currentUniverseId >> 2n + } + if (path.length === 0) return 'U-Genesis' + return `U-Genesis-${ path.join('-') }` + */ +} + +const getDeploymentsForUniverse = (universeId: bigint, securityPoolAddress: `0x${ string }`, repTokenAddress: `0x${ string }`, priceOracleManagerAndOperatorQueuerAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, auction: `0x${ string }`, escalationGame: `0x${ string }` ): Deployment[] => [ + { + abi: ReputationToken_ReputationToken.abi, + deploymentName: `RepV2 ${ getUniverseName(universeId) }`, + address: repTokenAddress + }, { + abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, + deploymentName: `PriceOracleManagerAndOperatorQueuer ${ getUniverseName(universeId) }`, + address: priceOracleManagerAndOperatorQueuerAddress + }, { + abi: peripherals_SecurityPool_SecurityPool.abi, + deploymentName: `ETH SecurityPool ${ getUniverseName(universeId) }`, + address: securityPoolAddress + }, { + abi: peripherals_EscalationGame_EscalationGame.abi, + deploymentName: `Escalation Game`, + address: escalationGame + }, { + abi: peripherals_tokens_ShareToken_ShareToken.abi, + deploymentName: `ShareToken ${ getUniverseName(universeId) }`, + address: shareTokenAddress + }, { + abi: peripherals_Auction_Auction.abi, + deploymentName: `Truth Auction ${ getUniverseName(universeId) }`, + address: auction + } +] as const + +export const getDeployments = (genesisUniverse: bigint, questionId: bigint, securityMultiplier: bigint): Deployment[] => { + const infraAddresses = getInfraContractAddresses() + const originAddresses = getSecurityPoolAddresses(zeroAddress, genesisUniverse, questionId, securityMultiplier) + + const oucomes = [QuestionOutcome.Invalid, QuestionOutcome.No, QuestionOutcome.Yes] + + const getChildAddresses = (parentSecurityPoolAddress: `0x${ string }`, parentUniverseId: bigint): Deployment[] => { + return oucomes.flatMap((outcome) => { + const universeId = getChildUniverseId(parentUniverseId, BigInt(outcome)) + const childAddresses = getSecurityPoolAddresses(parentSecurityPoolAddress, universeId, questionId, securityMultiplier) + return getDeploymentsForUniverse(universeId, childAddresses.securityPool, getRepTokenAddress(universeId), childAddresses.priceOracleManagerAndOperatorQueuer, childAddresses.shareToken, childAddresses.truthAuction, childAddresses.escalationGame) + }) + } + + return ([ + ...getDeploymentsForUniverse(genesisUniverse, originAddresses.securityPool, getRepTokenAddress(genesisUniverse), originAddresses.priceOracleManagerAndOperatorQueuer, originAddresses.shareToken, originAddresses.truthAuction, originAddresses.escalationGame), + ...getChildAddresses(originAddresses.securityPool, genesisUniverse), // children + ...oucomes.flatMap((outcome) => getChildAddresses(getSecurityPoolAddresses(originAddresses.securityPool, genesisUniverse, questionId, securityMultiplier).securityPool, getChildUniverseId(genesisUniverse, BigInt(outcome)))), // grand children + { + abi: Zoltar_Zoltar.abi, + deploymentName: 'Zoltar', + address: infraAddresses.zoltar, + }, { + abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi, + deploymentName: 'Security Pool Factory', + address: infraAddresses.securityPoolFactory + }, { + abi: peripherals_factories_AuctionFactory_AuctionFactory.abi, + deploymentName: 'Auction Factory', + address: infraAddresses.auctionFactory + }, { + abi: peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory.abi, + deploymentName: 'Price Oracle Manager And Operator Queuer Factory', + address: infraAddresses.priceOracleManagerAndOperatorQueuerFactory + }, { + abi: peripherals_factories_ShareTokenFactory_ShareTokenFactory.abi, + deploymentName: 'Share Token Factory', + address: infraAddresses.shareTokenFactory + }, { + abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, + deploymentName: 'Open Oracle', + address: infraAddresses.openOracle + }, { + abi: peripherals_interfaces_IWeth9_IWeth9.abi, + deploymentName: 'WETH', + address: WETH_ADDRESS + }, { + abi: peripherals_interfaces_IAugur_IAugur.abi, + deploymentName: 'Augur', + address: '0x23916a8f5c3846e3100e5f587ff14f3098722f5d' + }, { + abi: IERC20_IERC20.abi, + deploymentName: 'ETH', + address: addressString(ETHEREUM_LOGS_LOGGER_ADDRESS) + }, { + abi: undefined, + deploymentName: 'Micah Deployer', + address: `0x7a0d94f55792c434d74a40883c6ed8545e406d12` + }, { + abi: peripherals_SecurityPoolUtils_SecurityPoolUtils.abi, + deploymentName: 'Security Pool Utils', + address: infraAddresses.securityPoolUtils + }, { + abi: peripherals_factories_EscalationGameFactory_EscalationGameFactory.abi, + deploymentName: 'Escalation Game Factory', + address: infraAddresses.escalationGameFactory + }, { + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + deploymentName: 'Security Pool Forker', + address: infraAddresses.securityPoolForker + }, { + abi: peripherals_YesNoMarkets_YesNoMarkets.abi, + deploymentName: 'Yes No Markets', + address: infraAddresses.yesNoMarkets + }, { + abi: undefined, + deploymentName: 'Augur V2 Genesis', + address: '0x49244BD018Ca9fd1f06ecC07B9E9De773246e5AA' + }, + ...TEST_ADDRESSES.map((testAddress, index) => ({ + abi: undefined, + deploymentName: `TEST_ADDRESSES[${ index }]`, + address: addressString(testAddress) + } as const)) + ] as const).filter((entry) => BigInt(entry.address) !== 0n) +} diff --git a/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts b/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts new file mode 100644 index 0000000..6f56125 --- /dev/null +++ b/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts @@ -0,0 +1,277 @@ +import 'viem/window' +import { ReadContractReturnType } from 'viem' +import { ReadClient, WriteClient } from '../viem.js' +import { WETH_ADDRESS } from '../constants.js' +import { peripherals_Auction_Auction, peripherals_openOracle_OpenOracle_OpenOracle, peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer, peripherals_tokens_ShareToken_ShareToken, peripherals_YesNoMarkets_YesNoMarkets } from '../../../../types/contractArtifact.js' +import { QuestionOutcome } from '../../types/types.js' +import { getInfraContractAddresses } from './deployPeripherals.js' +import { shareArrayToCash } from './securityPool.js' + +export enum OperationType { + Liquidation = 0, + WithdrawRep = 1, + SetSecurityBondsAllowance = 2 +} + +export const requestPriceIfNeededAndQueueOperation = async (client: WriteClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`, operation: OperationType, targetVault: `0x${ string }`, amount: bigint) => { + const ethCost = await getRequestPriceEthCost(client, priceOracleManagerAndOperatorQueuer) * 2n; + return await client.writeContract({ + abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, + functionName: 'requestPriceIfNeededAndQueueOperation', + address: priceOracleManagerAndOperatorQueuer, + args: [operation, targetVault, amount], + value: ethCost, + }) +} + +export const requestPrice = async (client: WriteClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`) => { + const ethCost = await getRequestPriceEthCost(client, priceOracleManagerAndOperatorQueuer) * 2n; + return await client.writeContract({ + abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, + functionName: 'requestPrice', + address: priceOracleManagerAndOperatorQueuer, + args: [], + value: ethCost, + }) +} + +export const getPendingReportId = async (client: ReadClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, + functionName: 'pendingReportId', + address: priceOracleManagerAndOperatorQueuer, + args: [] + }) as bigint +} + +interface ExtraReportData { + stateHash: `0x${ string }` + callbackContract: `0x${ string }` + numReports: number + callbackGasLimit: number + callbackSelector: `0x${ string }` + protocolFeeRecipient: `0x${ string }` + trackDisputes: boolean + keepFee: boolean + feeToken: boolean +} + +export const getOpenOracleExtraData = async (client: ReadClient, extraDataId: bigint): Promise => { + const result = await client.readContract({ + abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, + functionName: 'extraData', + address: getInfraContractAddresses().openOracle, + args: [extraDataId] + }) as ReadContractReturnType + + const [ + stateHash, + callbackContract, + numReports, + callbackGasLimit, + callbackSelector, + protocolFeeRecipient, + trackDisputes, + keepFee, + feeToken + ] = result as [ + `0x${ string }`, + `0x${ string }`, + bigint, + bigint, + `0x${ string }`, + `0x${ string }`, + boolean, + boolean, + boolean + ] + + return { + stateHash, + callbackContract, + numReports: Number(numReports), + callbackGasLimit: Number(callbackGasLimit), + callbackSelector, + protocolFeeRecipient, + trackDisputes, + keepFee, + feeToken + } +} + +export const openOracleSubmitInitialReport = async (client: WriteClient, reportId: bigint, amount1: bigint, amount2: bigint, stateHash: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, + functionName: 'submitInitialReport', + address: getInfraContractAddresses().openOracle, + args: [reportId, amount1, amount2, stateHash] + }) +} + +export const openOracleSettle = async (client: WriteClient, reportId: bigint) => { + return await client.writeContract({ + abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, + functionName: 'settle', + address: getInfraContractAddresses().openOracle, + gas: 5_000_000n, //needed because of gas() opcode being used + args: [reportId] + }) +} + +export const getRequestPriceEthCost = async (client: ReadClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, + functionName: 'getRequestPriceEthCost', + address: priceOracleManagerAndOperatorQueuer, + args: [] + }) as bigint +} + +export const wrapWeth = async (client: WriteClient, amount: bigint) => { + const wethAbi = [{ + type: 'function', + name: 'deposit', + stateMutability: 'payable', + inputs: [], + outputs: [] + }] + return await client.writeContract({ + abi: wethAbi, + address: WETH_ADDRESS, + functionName: 'deposit', + value: amount + }) +} + +export interface ReportMeta { + exactToken1Report: bigint + escalationHalt: bigint + fee: bigint + settlerReward: bigint + token1: `0x${ string }` + settlementTime: number + token2: `0x${ string }` + timeType: boolean + feePercentage: number + protocolFee: number + multiplier: number + disputeDelay: number +} + +export const getOpenOracleReportMeta = async (client: ReadClient, reportId: bigint): Promise => { + const reportMetaData = await client.readContract({ + abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, + functionName: 'reportMeta', + address: getInfraContractAddresses().openOracle, + args: [reportId] + }) + + const [ + exactToken1Report, + escalationHalt, + fee, + settlerReward, + token1, + settlementTime, + token2, + timeType, + feePercentage, + protocolFee, + multiplier, + disputeDelay + ] = reportMetaData + + return { + exactToken1Report, + escalationHalt, + fee, + settlerReward, + token1, + settlementTime, + token2, + timeType, + feePercentage, + protocolFee, + multiplier, + disputeDelay + } +} + +export const getLastPrice = async (client: ReadClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, + functionName: 'lastPrice', + address: priceOracleManagerAndOperatorQueuer, + args: [] + }) as bigint +} + +export const participateAuction = async (client: WriteClient, auctionAddress: `0x${ string }`, repToBuy: bigint, ethToInvest: bigint) => { + return await client.writeContract({ + abi: peripherals_Auction_Auction.abi, + functionName: 'participate', + address: auctionAddress, + args: [repToBuy], + value: ethToInvest + }) +} +export const getEthAmountToBuy = async (client: ReadClient, auctionAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_Auction_Auction.abi, + functionName: 'ethAmountToBuy', + address: auctionAddress, + args: [], + }) +} + +export const balanceOfOutcome = async (client: ReadClient, shareTokenAddress: `0x${ string }`, universeId: bigint, outcome: QuestionOutcome, account: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_tokens_ShareToken_ShareToken.abi, + functionName: 'balanceOfOutcome', + address: shareTokenAddress, + args: [universeId, outcome, account], + }) +} + +export const balanceOfShares = async (client: ReadClient, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_tokens_ShareToken_ShareToken.abi, + functionName: 'balanceOfShares', + address: shareTokenAddress, + args: [universeId, account], + }) +} + +export const balanceOfSharesInCash = async (client: ReadClient, seucurityPoolAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { + const array = await client.readContract({ + abi: peripherals_tokens_ShareToken_ShareToken.abi, + functionName: 'balanceOfShares', + address: shareTokenAddress, + args: [universeId, account], + }) + return await shareArrayToCash(client, seucurityPoolAddress, array) +} + +export const getTokenId = (universeId: bigint, outcome: QuestionOutcome) => { + const universeMask = (1n << 248n) - 1n + return ((universeId & universeMask) << 8n) | (BigInt(outcome) & 255n) +} +export const unpackTokenId = (tokenId: bigint): { universe: bigint, outcome: QuestionOutcome } => ({ universe: tokenId >> 8n, outcome: Number(tokenId & 0xFFn) }) + +export const migrateShares = async (client: WriteClient, shareTokenAddress: `0x${ string }`, fromUniverseId: bigint, outcome: QuestionOutcome, outcomes: bigint[]) => { + return await client.writeContract({ + abi: peripherals_tokens_ShareToken_ShareToken.abi, + functionName: 'migrate', + address: shareTokenAddress, + args: [getTokenId(fromUniverseId, outcome), outcomes.map((x) => Number(x))], + }) +} + +export const getMarketEndDate = async(client: ReadClient, marketId: bigint) => { + return await client.readContract({ + abi: peripherals_YesNoMarkets_YesNoMarkets.abi, + functionName: 'getMarketEndDate', + address: getInfraContractAddresses().yesNoMarkets, + args: [marketId], + }) +} diff --git a/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts b/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts new file mode 100644 index 0000000..f7c8d52 --- /dev/null +++ b/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts @@ -0,0 +1,86 @@ +import { zeroAddress } from 'viem' +import { MockWindowEthereum } from '../../MockWindowEthereum.js' +import { addressString } from '../bigint.js' +import { DAY, GENESIS_REPUTATION_TOKEN, WETH_ADDRESS } from '../constants.js' +import { getInfraContractAddresses, getSecurityPoolAddresses } from './deployPeripherals.js' +import { approveToken, contractExists, getERC20Balance } from '../utilities.js' +import { WriteClient } from '../viem.js' +import assert from 'node:assert' +import { getOpenOracleExtraData, getOpenOracleReportMeta, getPendingReportId, openOracleSettle, openOracleSubmitInitialReport, OperationType, requestPrice, requestPriceIfNeededAndQueueOperation, wrapWeth } from './peripherals.js' +import { QuestionOutcome } from '../../types/types.js' +import { forkZoltarWithOwnEscalationGame } from './securityPoolForker.js' +import { getTotalTheoreticalSupply } from './zoltar.js' +import { depositRep, depositToEscalationGame, getRepToken, getSecurityVault, poolOwnershipToRep } from './securityPool.js' + +export const genesisUniverse = 0n +export const securityMultiplier = 2n +export const startingRepEthPrice = 1n +export const completeSetCollateralAmount = 0n +export const PRICE_PRECISION = 10n ** 18n +export const MAX_RETENTION_RATE = 999_999_996_848_000_000n // ≈90% yearly +export const EXTRA_INFO = 'test market!' + +export const approveAndDepositRep = async (client: WriteClient, repDeposit: bigint, marketId: bigint) => { + const securityPoolAddress = getSecurityPoolAddresses(zeroAddress, genesisUniverse, marketId, securityMultiplier).securityPool + assert.ok(await contractExists(client, securityPoolAddress), 'security pool not deployed') + + const startBalance = await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddress) + await approveToken(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddress) + await depositRep(client, securityPoolAddress, repDeposit) + + const newBalance = await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddress) + assert.strictEqual(newBalance, startBalance + repDeposit, 'Did not deposit rep') +} + +export const triggerOwnGameFork = async(client: WriteClient, securityPoolAddress: `0x${ string }`) => { + const repToken = await getRepToken(client, securityPoolAddress) + const forkTreshold = (await getTotalTheoreticalSupply(client, repToken)) / 20n /2n + const vault = await getSecurityVault(client, securityPoolAddress, client.account.address) + const repAmount = await poolOwnershipToRep(client, securityPoolAddress, vault.repDepositShare) + assert.ok(repAmount >= 2n * forkTreshold, 'not enough rep in vault to fork') + await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.Yes, forkTreshold) + await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.No, forkTreshold) + await forkZoltarWithOwnEscalationGame(client, securityPoolAddress) +} + +export const handleOracleReporting = async(client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint) => { + const pendingReportId = await getPendingReportId(client, priceOracleManagerAndOperatorQueuer) + if (pendingReportId === 0n) { + // operation already executed + return + } + assert.ok(pendingReportId > 0, 'Operation is not queued') + + const reportMeta = await getOpenOracleReportMeta(client, pendingReportId) + + // initial report + const amount1 = reportMeta.exactToken1Report + const amount2 = amount1 * PRICE_PRECISION / forceRepEthPriceTo + + const openOracle = getInfraContractAddresses().openOracle + await approveToken(client, addressString(GENESIS_REPUTATION_TOKEN), openOracle) + await approveToken(client, WETH_ADDRESS, openOracle) + await wrapWeth(client, amount2) + const wethBalance = await getERC20Balance(client, WETH_ADDRESS, client.account.address) + assert.strictEqual(wethBalance, amount2, 'Did not wrap weth') + + const stateHash = (await getOpenOracleExtraData(client, pendingReportId)).stateHash + await openOracleSubmitInitialReport(client, pendingReportId, amount1, amount2, stateHash) + + await mockWindow.advanceTime(DAY) + + await openOracleSettle(client, pendingReportId) +} + +export const manipulatePriceOracleAndPerformOperation = async(client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, operation: OperationType, targetVault: `0x${ string }`, amount: bigint, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { + await requestPriceIfNeededAndQueueOperation(client, priceOracleManagerAndOperatorQueuer, operation, targetVault, amount) + await handleOracleReporting(client, mockWindow, priceOracleManagerAndOperatorQueuer, forceRepEthPriceTo) +} + +export const manipulatePriceOracle = async(client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { + await requestPrice(client, priceOracleManagerAndOperatorQueuer) + await handleOracleReporting(client, mockWindow, priceOracleManagerAndOperatorQueuer, forceRepEthPriceTo) +} + +export const canLiquidate = (lastPrice: bigint, securityBondAllowance: bigint, stakedRep: bigint, securityMultiplier: bigint) => securityBondAllowance * lastPrice * securityMultiplier > stakedRep * PRICE_PRECISION + diff --git a/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts b/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts new file mode 100644 index 0000000..ebf8df5 --- /dev/null +++ b/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts @@ -0,0 +1,228 @@ +import { peripherals_SecurityPool_SecurityPool } from '../../../../types/contractArtifact.js' +import { SystemState } from '../../types/peripheralTypes.js' +import { QuestionOutcome } from '../../types/types.js' +import { ReadClient, WriteClient } from '../viem.js' + +export const depositToEscalationGame = async (client: WriteClient, securityPoolAddress: `0x${ string }`, outcome: QuestionOutcome, amount: bigint) => { + return await client.writeContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'depositToEscalationGame', + address: securityPoolAddress, + args: [outcome, amount], + }) +} + +export const withdrawFromEscalationGame = async (client: WriteClient, securityPoolAddress: `0x${ string }`, depositIndexes: bigint[]) => { + return await client.writeContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'withdrawFromEscalationGame', + address: securityPoolAddress, + args: [depositIndexes], + }) +} + +export const depositRep = async (client: WriteClient, securityPoolAddress: `0x${ string }`, amount: bigint) => { + return await client.writeContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'depositRep', + address: securityPoolAddress, + args: [amount] + }) +} + +export const createCompleteSet = async (client: WriteClient, securityPoolAddress: `0x${ string }`, completeSetsToCreate: bigint) => { + return await client.writeContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'createCompleteSet', + address: securityPoolAddress, + args: [], + value: completeSetsToCreate, + }) +} + +export const redeemCompleteSet = async (client: WriteClient, securityPoolAddress: `0x${ string }`, completeSetsToRedeem: bigint) => { + return await client.writeContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'redeemCompleteSet', + address: securityPoolAddress, + args: [completeSetsToRedeem], + }) +} + +export const getTotalSecurityBondAllowance = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'totalSecurityBondAllowance', + address: securityPoolAddress, + args: [] + }) as bigint +} + +export const getCompleteSetCollateralAmount = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'completeSetCollateralAmount', + address: securityPoolAddress, + args: [] + }) as bigint +} + +export const getSystemState = async (client: ReadClient, securityPoolAddress: `0x${ string }`): Promise => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'systemState', + address: securityPoolAddress, + args: [], + }) +} + +export const getCurrentRetentionRate = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'currentRetentionRate', + address: securityPoolAddress, + args: [], + }) +} + +export const getSecurityVault = async (client: ReadClient, securityPoolAddress: `0x${ string }`, securityVault: `0x${ string }`) => { + const vault = await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'securityVaults', + address: securityPoolAddress, + args: [securityVault], + }) + const [repDepositShare, securityBondAllowance, unpaidEthFees, feeIndex ] = vault + return { repDepositShare, securityBondAllowance, unpaidEthFees, feeIndex } +} + +export const getSecurityPoolsEscalationGame = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'escalationGame', + address: securityPoolAddress, + args: [], + }) +} + +export const getPoolOwnershipDenominator = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'poolOwnershipDenominator', + address: securityPoolAddress, + args: [], + }) +} + +export const poolOwnershipToRep = async (client: ReadClient, securityPoolAddress: `0x${ string }`, poolOwnership: bigint) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'poolOwnershipToRep', + address: securityPoolAddress, + args: [poolOwnership], + }) +} + +export const repToPoolOwnership = async (client: ReadClient, securityPoolAddress: `0x${ string }`, repAmount: bigint) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'repToPoolOwnership', + address: securityPoolAddress, + args: [repAmount], + }) +} + +export const redeemShares = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'redeemShares', + address: securityPoolAddress, + args: [], + }) +} + + +export const getTotalFeesOvedToVaults = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'totalFeesOvedToVaults', + address: securityPoolAddress, + args: [], + }) +} + +export const sharesToCash = async (client: ReadClient, securityPoolAddress: `0x${ string }`, completeSetAmount: bigint) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'sharesToCash', + address: securityPoolAddress, + args: [completeSetAmount], + }) +} + +export const cashToShares = async (client: ReadClient, securityPoolAddress: `0x${ string }`, eth: bigint) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'cashToShares', + address: securityPoolAddress, + args: [eth], + }) +} + +export const getShareTokenSupply = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'shareTokenSupply', + address: securityPoolAddress, + args: [], + }) +} + +export const shareArrayToCash = async (client: ReadClient, securityPoolAddress: `0x${ string }`, shares: readonly bigint[]) => { + return await Promise.all(shares.map((shares) => sharesToCash(client, securityPoolAddress, shares))) +} + +export const updateVaultFees = async (client: WriteClient, securityPoolAddress: `0x${ string }`, vault: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'updateVaultFees', + address: securityPoolAddress, + args: [vault], + }) +} + +export const redeemFees = async (client: WriteClient, securityPoolAddress: `0x${ string }`, vault: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'redeemFees', + address: securityPoolAddress, + args: [vault], + }) +} + +export const redeemRep = async (client: WriteClient, securityPoolAddress: `0x${ string }`, vault: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'redeemRep', + address: securityPoolAddress, + args: [vault], + }) +} + +export const getRepToken = async(client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'repToken', + address: securityPoolAddress, + args: [], + }) +} + +export const isFinalized = async(client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'repToken', + address: securityPoolAddress, + args: [], + }) +} diff --git a/solidity/ts/testsuite/simulator/utils/contracts/securityPoolForker.ts b/solidity/ts/testsuite/simulator/utils/contracts/securityPoolForker.ts new file mode 100644 index 0000000..e93cab4 --- /dev/null +++ b/solidity/ts/testsuite/simulator/utils/contracts/securityPoolForker.ts @@ -0,0 +1,134 @@ +import { peripherals_SecurityPool_SecurityPool, peripherals_SecurityPoolForker_SecurityPoolForker } from '../../../../types/contractArtifact.js' +import { QuestionOutcome } from '../../types/types.js' +import { getInfraContractAddresses } from './deployPeripherals.js' +import { contractExists } from '../utilities.js' +import { ReadClient, WriteClient } from '../viem.js' + +export const forkSecurityPool = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'forkSecurityPool', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress], + }) +} + +export const migrateVault = async (client: WriteClient, securityPoolAddress: `0x${ string }`, outcome: bigint | QuestionOutcome) => { + return await client.writeContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'migrateVault', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress, Number(outcome)], + }) +} + +export const startTruthAuction = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'startTruthAuction', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress], + }) +} + +export const finalizeTruthAuction = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'finalizeTruthAuction', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress], + }) +} + +export const claimAuctionProceeds = async (client: WriteClient, securityPoolAddress: `0x${ string }`, vault: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'claimAuctionProceeds', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress, vault], + }) +} + +export const forkZoltarWithOwnEscalationGame = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { + return await client.writeContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'forkZoltarWithOwnEscalationGame', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress], + }) +} + +export const getMigratedRep = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'getMigratedRep', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress], + }) +} + +export const getMarketOutcome = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + if(!(await contractExists(client, securityPoolAddress))) return QuestionOutcome.None + return await client.readContract({ + abi: [{ + "inputs": [ + { + "internalType": "contract ISecurityPool", + "name": "securityPool", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "getMarketOutcome", + "outputs": [ + { + "internalType": "enum YesNoMarkets.Outcome", + "name": "outcome", + "type": "uint8" + } + ] + }] as const, // typescript limitation on types... + functionName: 'getMarketOutcome', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress], + }) +} + +export const createChildUniverse = async (client: WriteClient, securityPoolAddress: `0x${ string }`, outcome: QuestionOutcome) => { + return await client.writeContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'createChildUniverse', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress, Number(outcome)], + }) +} + +export const getSecurityPoolForkerForkData = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + const data = await client.readContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'forkData', + address: getInfraContractAddresses().securityPoolForker, + args: [securityPoolAddress], + }) + const [ repAtFork, truthAuction, truthAuctionStarted, migratedRep, auctionedSecurityBondAllowance, ownFork, outcomeIndex] = data + return { repAtFork, truthAuction, truthAuctionStarted, migratedRep, auctionedSecurityBondAllowance, ownFork, outcomeIndex } +} + +export const migrateFromEscalationGame = async (client: WriteClient, parentSecurityPool: `0x${ string }`, vault: `0x${ string }`, outcomeIndex: QuestionOutcome, depositIndexes: bigint[]) => { + return await client.writeContract({ + abi: peripherals_SecurityPoolForker_SecurityPoolForker.abi, + functionName: 'migrateFromEscalationGame', + address: getInfraContractAddresses().securityPoolForker, + args: [parentSecurityPool, vault, outcomeIndex, depositIndexes.map((x) => Number(x))], + }) +} + +export const getCompleteSetCollateralAmount = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { + return await client.readContract({ + abi: peripherals_SecurityPool_SecurityPool.abi, + functionName: 'completeSetCollateralAmount', + address: securityPoolAddress, + args: [], + }) +} diff --git a/solidity/ts/testsuite/simulator/utils/testUtils.ts b/solidity/ts/testsuite/simulator/utils/testUtils.ts new file mode 100644 index 0000000..9221a18 --- /dev/null +++ b/solidity/ts/testsuite/simulator/utils/testUtils.ts @@ -0,0 +1,10 @@ +import assert from 'node:assert' +import { abs, bigintToDecimalString } from './bigint.js' + +export const strictEqualTypeSafe = (actual: Type, expected: Type, errorMessage?: string | Error | undefined) => assert.strictEqual(actual, expected, errorMessage) + +export const strictEqual18Decimal = (actual: bigint, expected: bigint, errorMessage?: string | Error | undefined) => assert.strictEqual(bigintToDecimalString(actual, 18n), bigintToDecimalString(expected, 18n), errorMessage) + +export const approximatelyEqual = (actual: bigint, expected: bigint, errorDelta: bigint, message?: string | Error | undefined) => { + if (abs(actual - expected) > errorDelta) strictEqualTypeSafe(actual, expected, message) +} diff --git a/solidity/ts/testsuite/simulator/utils/typescript.ts b/solidity/ts/testsuite/simulator/utils/typescript.ts index eb093a3..779adc2 100644 --- a/solidity/ts/testsuite/simulator/utils/typescript.ts +++ b/solidity/ts/testsuite/simulator/utils/typescript.ts @@ -1,36 +1,38 @@ -export type UnionToIntersection = (T extends unknown ? (k: T) => void : never) extends (k: infer I) => void ? I : never - -export function assertNever(value: never): never { - throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`) -} - -export type DistributedOmit> = T extends unknown ? Pick> : never - -export type DistributiveOmit = T extends unknown ? Omit : never - -export function assertUnreachable(value: never): never { - throw new Error(`Unreachable! (${ value })`) -} - -function isObject(maybe: unknown): maybe is Object { - return typeof maybe === 'object' && maybe !== null && !Array.isArray(maybe) -} - -export function assertIsObject(maybe: unknown): asserts maybe is Object { - if (!isObject(maybe)) throw new Error(`Expected object but got ${ typeof maybe }`) -} - -export function createGuard(check: (maybe: T) => U | undefined): (maybe: T) => maybe is U { - return (maybe: T): maybe is U => check(maybe) !== undefined -} - -export function getWithDefault(map: Map, key: Key, defaultValue: Value) { - const previousValue = map.get(key) - if (previousValue === undefined) return defaultValue - return previousValue -} - -type Split = { [K in keyof T]: { [P in K]: T[P] } }[keyof T] | Record -export function modifyObject(original: T, subObject: NoInfer>): T { - return {...original, ...subObject } -} +export type UnionToIntersection = (T extends unknown ? (k: T) => void : never) extends (k: infer I) => void ? I : never + +export function assertNever(value: never): never { + throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`) +} + +export type DistributedOmit> = T extends unknown ? Pick> : never + +export type DistributiveOmit = T extends unknown ? Omit : never + +export function assertUnreachable(value: never): never { + throw new Error(`Unreachable! (${ value })`) +} + +function isObject(maybe: unknown): maybe is Object { + return typeof maybe === 'object' && maybe !== null && !Array.isArray(maybe) +} + +export function assertIsObject(maybe: unknown): asserts maybe is Object { + if (!isObject(maybe)) throw new Error(`Expected object but got ${ typeof maybe }`) +} + +export function createGuard(check: (maybe: T) => U | undefined): (maybe: T) => maybe is U { + return (maybe: T): maybe is U => check(maybe) !== undefined +} + +export function getWithDefault(map: Map, key: Key, defaultValue: Value) { + const previousValue = map.get(key) + if (previousValue === undefined) return defaultValue + return previousValue +} + +type Split = { [K in keyof T]: { [P in K]: T[P] } }[keyof T] | Record +export function modifyObject(original: T, subObject: NoInfer>): T { + return {...original, ...subObject } +} + +export const objectEntries = (obj: T) => Object.entries(obj) as { [K in keyof T]-?: [K, T[K]] }[keyof T][] diff --git a/solidity/ts/testsuite/simulator/utils/utilities.ts b/solidity/ts/testsuite/simulator/utils/utilities.ts index e359afc..4a36702 100644 --- a/solidity/ts/testsuite/simulator/utils/utilities.ts +++ b/solidity/ts/testsuite/simulator/utils/utilities.ts @@ -1,17 +1,15 @@ import 'viem/window' -import { getContractAddress, numberToBytes, encodeAbiParameters, keccak256, encodeDeployData, getCreate2Address } from 'viem' +import { encodeAbiParameters, keccak256 } from 'viem' import { mainnet } from 'viem/chains' import { ReadClient, WriteClient } from './viem.js' import { GENESIS_REPUTATION_TOKEN, PROXY_DEPLOYER_ADDRESS, TEST_ADDRESSES } from './constants.js' -import { abs, addressString, bytes32String } from './bigint.js' +import { addressString } from './bigint.js' import { Address } from 'viem' import { ABIS } from '../../../abi/abis.js' import { MockWindowEthereum } from '../MockWindowEthereum.js' -import { ReputationToken_ReputationToken, Zoltar_Zoltar } from '../../../types/contractArtifact.js' import { QuestionOutcome } from '../types/types.js' -import assert from 'node:assert' -export const initialTokenBalance = 1000000n * 10n**18n +export const TOKEN_AMOUNT_TO_MINT = 100000000n * 10n ** 18n export async function sleep(milliseconds: number) { await new Promise(resolve => setTimeout(resolve, milliseconds)) @@ -210,9 +208,7 @@ export const getETHBalance = async (client: ReadClient, address: Address) => { } export const setupTestAccounts = async (mockWindowEthereum: MockWindowEthereum) => { - const accountValues = TEST_ADDRESSES.map((address) => { - return { address: addressString(address), amount: initialTokenBalance} - }) + const accountValues = TEST_ADDRESSES.map((address) => ({ address: addressString(address), amount: TOKEN_AMOUNT_TO_MINT})) await mintETH(mockWindowEthereum, accountValues) await mintERC20(mockWindowEthereum, addressString(GENESIS_REPUTATION_TOKEN), accountValues, 1n) } @@ -226,163 +222,11 @@ export async function ensureProxyDeployerDeployed(client: WriteClient): Promise< await client.waitForTransactionReceipt({ hash: deployHash }) } -export function getZoltarAddress() { - const bytecode: `0x${ string }` = `0x${ Zoltar_Zoltar.evm.bytecode.object }` - return getContractAddress({ bytecode, from: addressString(PROXY_DEPLOYER_ADDRESS), opcode: 'CREATE2', salt: numberToBytes(0) }) -} - -export const isZoltarDeployed = async (client: ReadClient) => { - const expectedDeployedBytecode: `0x${ string }` = `0x${ Zoltar_Zoltar.evm.deployedBytecode.object }` - const address = getZoltarAddress() - const deployedBytecode = await client.getCode({ address }) - return deployedBytecode === expectedDeployedBytecode -} - -export const deployZoltarTransaction = () => { - const bytecode: `0x${ string }` = `0x${ Zoltar_Zoltar.evm.bytecode.object }` - return { to: addressString(PROXY_DEPLOYER_ADDRESS), data: bytecode } as const -} - -export const ensureZoltarDeployed = async (client: WriteClient) => { - await ensureProxyDeployerDeployed(client) - if (await isZoltarDeployed(client)) return - const hash = await client.sendTransaction(deployZoltarTransaction()) - await client.waitForTransactionReceipt({ hash }) -} - -export const getUniverseData = async (client: ReadClient, universeId: bigint) => { - const universeData = await client.readContract({ - abi: Zoltar_Zoltar.abi, - functionName: 'universes', - address: getZoltarAddress(), - args: [universeId] - }) - const [reputationToken, forkingQuestion, forkTime] = universeData - return { reputationToken, forkingQuestion, forkTime } -} - -export const createQuestion = async (client: WriteClient, universe: bigint, endTime: bigint, extraInfo: string) => { - return await client.writeContract({ - chain: mainnet, - abi: Zoltar_Zoltar.abi, - functionName: 'createQuestion', - address: getZoltarAddress(), - args: [universe, endTime, client.account.address, extraInfo] - }) -} - -export const getQuestionData = async (client: ReadClient, questionId: bigint) => { - const questionData = await client.readContract({ - abi: Zoltar_Zoltar.abi, - functionName: 'questions', - address: getZoltarAddress(), - args: [questionId] - }) - const [endTime, originUniverse, designatedReporter, extraInfo] = questionData - - return { - endTime, - originUniverse, - designatedReporter, - extraInfo - } -} - -export const reportOutcome = async (client: WriteClient, universe: bigint, question: bigint, outcome: QuestionOutcome) => { - return await client.writeContract({ - chain: mainnet, - abi: Zoltar_Zoltar.abi, - functionName: 'reportOutcome', - address: getZoltarAddress(), - args: [universe, question, Number(outcome)] - }) -} - -export const finalizeQuestion = async (client: WriteClient, universe: bigint, question: bigint) => { - return await client.writeContract({ - chain: mainnet, - abi: Zoltar_Zoltar.abi, - functionName: 'finalizeQuestion', - address: getZoltarAddress(), - args: [universe, question] - }) -} - -export const dispute = async (client: WriteClient, universe: bigint, question: bigint, outcome: QuestionOutcome) => { - return await client.writeContract({ - chain: mainnet, - abi: Zoltar_Zoltar.abi, - functionName: 'dispute', - address: getZoltarAddress(), - args: [universe, question, Number(outcome)] - }) -} - -export const splitRep = async (client: WriteClient, universe: bigint) => { - return await client.writeContract({ - chain: mainnet, - abi: Zoltar_Zoltar.abi, - functionName: 'splitRep', - address: getZoltarAddress(), - args: [universe] - }) -} - -export const splitStakedRep = async (client: WriteClient, universe: bigint, question: bigint) => { - return await client.writeContract({ - chain: mainnet, - abi: Zoltar_Zoltar.abi, - functionName: 'splitStakedRep', - address: getZoltarAddress(), - args: [universe, question] - }) -} - -export const isFinalized = async (client: ReadClient, universe: bigint, questionId: bigint) => { - return await client.readContract({ - abi: Zoltar_Zoltar.abi, - functionName: 'isFinalized', - address: getZoltarAddress(), - args: [universe, questionId] - }) -} - -export const getWinningOutcome = async (client: ReadClient, universe: bigint, questionId: bigint): Promise => { - return await client.readContract({ - abi: Zoltar_Zoltar.abi, - functionName: 'getWinningOutcome', - address: getZoltarAddress(), - args: [universe, questionId] - }) -} - -export const getReportBond = async (client: ReadClient) => { - return await client.readContract({ - abi: Zoltar_Zoltar.abi, - functionName: 'REP_BOND', - address: getZoltarAddress(), - args: [] - }) -} - -export function getChildUniverseId(parentUniverseId: bigint, outcome: QuestionOutcome): bigint { - return (parentUniverseId << 2n) + BigInt(outcome) + 1n -} - -export function getRepTokenAddress(universeId: bigint): `0x${ string }` { - if (universeId === 0n) return addressString(GENESIS_REPUTATION_TOKEN) - const initCode = encodeDeployData({ - abi: ReputationToken_ReputationToken.abi, - bytecode: `0x${ ReputationToken_ReputationToken.evm.bytecode.object }`, - args: [getZoltarAddress()] - }) - return getCreate2Address({ from: getZoltarAddress(), salt: bytes32String(universeId), bytecodeHash: keccak256(initCode) }) -} - export const contractExists = async (client: ReadClient, contract: `0x${ string }`) => await client.getCode({ address: contract }) !== undefined -export const approximatelyEqual = (actual: bigint, expected: bigint, errorDelta: bigint, message?: string | Error | undefined) => { - if (abs(actual - expected) > errorDelta) assert.strictEqual(actual, expected, message) -} - export const isUnknownAnAddress = (maybeAddress: unknown): maybeAddress is `0x${ string }` => typeof maybeAddress === 'string' && /^0x[a-fA-F0-9]{40}$/.test(maybeAddress) + +const uint248BitMask = (1n << 248n) - 1n +export function getChildUniverseId(parentUniverseId: bigint, outcome: bigint | QuestionOutcome): bigint { + return BigInt(keccak256(encodeAbiParameters([{ type: 'uint248' }, { type: 'uint8' }], [parentUniverseId, Number(outcome)]))) & uint248BitMask +} From 0556ccd06640af2161d3c729bf96d801a82278b5 Mon Sep 17 00:00:00 2001 From: KillariDev Date: Fri, 13 Feb 2026 15:35:02 +0200 Subject: [PATCH 2/8] fix according to comments --- .../PriceOracleManagerAndOperatorQueuer.sol | 14 +++---- .../contracts/peripherals/SecurityPool.sol | 10 ++--- .../peripherals/SecurityPoolForker.sol | 6 +-- .../peripherals/SecurityPoolUtils.sol | 2 +- .../peripherals/factories/AuctionFactory.sol | 2 +- ...eOracleManagerAndOperatorQueuerFactory.sol | 2 +- .../factories/SecurityPoolFactory.sol | 9 +++-- .../peripherals/interfaces/IAugur.sol | 4 +- .../peripherals/interfaces/ISecurityPool.sol | 6 +-- .../interfaces/ISecurityPoolForker.sol | 2 +- solidity/ts/tests/testPeripherals.ts | 24 ++++++------ .../utils/contracts/deployPeripherals.ts | 39 +++++++++++++++---- .../simulator/utils/deployPeripherals.ts | 16 ++++---- .../testsuite/simulator/utils/peripherals.ts | 4 +- .../ts/testsuite/simulator/utils/testUtils.ts | 11 +++++- 15 files changed, 92 insertions(+), 59 deletions(-) diff --git a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol index 02db81c..7d2ec9d 100644 --- a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol +++ b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol @@ -37,7 +37,7 @@ contract PriceOracleManagerAndOperatorQueuer { OpenOracle public immutable openOracle; event PriceReported(uint256 reportId, uint256 price); - event ExecutetedQueuedOperation(uint256 operationId, OperationType operation, bool success, string errorMessage); + event ExecutedQueuedOperation(uint256 operationId, OperationType operation, bool success, string errorMessage); // operation queuing uint256 public previousQueuedOperationId; @@ -133,21 +133,21 @@ contract PriceOracleManagerAndOperatorQueuer { // todo, we should allow these operations here to fail, but solidity try catch doesnt work inside the same contract if (queuedOperations[operationId].operation == OperationType.Liquidation) { try securityPool.performLiquidation(queuedOperations[operationId].initiatorVault, queuedOperations[operationId].targetVault, queuedOperations[operationId].amount) { - emit ExecutetedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); + emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); } catch Error(string memory reason) { - emit ExecutetedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); + emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); } } else if(queuedOperations[operationId].operation == OperationType.WithdrawRep) { try securityPool.performWithdrawRep(queuedOperations[operationId].initiatorVault, queuedOperations[operationId].amount) { - emit ExecutetedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); + emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); } catch Error(string memory reason) { - emit ExecutetedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); + emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); } } else { try securityPool.performSetSecurityBondsAllowance(queuedOperations[operationId].initiatorVault, queuedOperations[operationId].amount) { - emit ExecutetedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); + emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); } catch Error(string memory reason) { - emit ExecutetedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); + emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); } } queuedOperations[operationId].amount = 0; diff --git a/solidity/contracts/peripherals/SecurityPool.sol b/solidity/contracts/peripherals/SecurityPool.sol index 219a2a4..f5cabb0 100644 --- a/solidity/contracts/peripherals/SecurityPool.sol +++ b/solidity/contracts/peripherals/SecurityPool.sol @@ -40,7 +40,7 @@ contract SecurityPool is ISecurityPool { uint256 public securityMultiplier; uint256 public shareTokenSupply; - uint256 public totalFeesOvedToVaults; + uint256 public totalFeesOwedToVaults; uint256 public lastUpdatedFeeAccumulator; uint256 public feeIndex; uint256 public currentRetentionRate; @@ -56,7 +56,7 @@ contract SecurityPool is ISecurityPool { event RedeemShares(address redeemer, uint256 sharesAmount, uint256 ethValue); event UpdateVaultFees(address vault, uint256 feeIndex, uint256 unpaidEthFees); event RedeemFees(address vault, uint256 fees); - event UpdateCollateralAmount(uint256 totalFeesOvedToVaults, uint256 completeSetCollateralAmount); + event UpdateCollateralAmount(uint256 totalFeesOwedToVaults, uint256 completeSetCollateralAmount); event CreateCompleteSet(uint256 shareTokenSupply, uint256 completeSetsToMint, uint256 completeSetCollateralAmount); event PerformLiquidation(address callerVault, address targetVaultAddress, uint256 debtAmount, uint256 debtToMove, uint256 repToMove); event RedeemRep(address caller, address vault, uint256 repAmount); @@ -120,12 +120,12 @@ contract SecurityPool is ISecurityPool { uint256 newCompleteSetCollateralAmount = completeSetCollateralAmount * SecurityPoolUtils.rpow(currentRetentionRate, timeDelta, SecurityPoolUtils.PRICE_PRECISION) / SecurityPoolUtils.PRICE_PRECISION; uint256 delta = completeSetCollateralAmount - newCompleteSetCollateralAmount; - totalFeesOvedToVaults += delta; + totalFeesOwedToVaults += delta; feeIndex += delta * SecurityPoolUtils.PRICE_PRECISION / totalSecurityBondAllowance; completeSetCollateralAmount = newCompleteSetCollateralAmount; lastUpdatedFeeAccumulator = feeEndDate < block.timestamp ? feeEndDate : block.timestamp; - emit UpdateCollateralAmount(totalFeesOvedToVaults, completeSetCollateralAmount); + emit UpdateCollateralAmount(totalFeesOwedToVaults, completeSetCollateralAmount); } function updateRetentionRate() public { @@ -146,7 +146,7 @@ contract SecurityPool is ISecurityPool { function redeemFees(address vault) public { uint256 fees = securityVaults[vault].unpaidEthFees; securityVaults[vault].unpaidEthFees = 0; - totalFeesOvedToVaults -= fees; + totalFeesOwedToVaults -= fees; (bool sent, ) = payable(vault).call{ value: fees }(''); require(sent, 'Failed to send Ether'); emit RedeemFees(vault, fees); diff --git a/solidity/contracts/peripherals/SecurityPoolForker.sol b/solidity/contracts/peripherals/SecurityPoolForker.sol index 8731a70..a8fc148 100644 --- a/solidity/contracts/peripherals/SecurityPoolForker.sol +++ b/solidity/contracts/peripherals/SecurityPoolForker.sol @@ -98,7 +98,7 @@ contract SecurityPoolForker is ISecurityPoolForker { childReputationToken.transfer(address(child), childReputationToken.balanceOf(address(this))); if (forkData[parent].ownFork) { - child.setPoolOwnershipDenominator(parent.poolOwnershipDenominator() * forkData[parent].repAtFork / (forkData[parent].repAtFork + parent.escalationGame().nonDecisionTreshold()*2/5) ); + child.setPoolOwnershipDenominator(parent.poolOwnershipDenominator() * forkData[parent].repAtFork / (forkData[parent].repAtFork + parent.escalationGame().nonDecisionThreshold()*2/5) ); } else { child.setPoolOwnershipDenominator(parent.poolOwnershipDenominator()); } @@ -118,7 +118,7 @@ contract SecurityPoolForker is ISecurityPoolForker { } (uint256 poolOwnership, , , , ) = child.securityVaults(vault); uint256 ownershipDelta = repToPoolOwnership(child, repMigratedFromEscalationGame); - child.setVaultPoolOwnership(msg.sender, poolOwnership + ownershipDelta); + child.setVaultPoolOwnership(vault, poolOwnership + ownershipDelta); forkData[child].migratedRep += repMigratedFromEscalationGame; emit MigrateFromEscalationGame(parent, vault, outcomeIndex, depositIndexes, repMigratedFromEscalationGame, ownershipDelta); // migrate open interest @@ -184,7 +184,7 @@ contract SecurityPoolForker is ISecurityPoolForker { securityPool.setSystemState(SystemState.Operational); ISecurityPool parent = securityPool.parent(); uint256 repAvailable = forkData[parent].repAtFork; - securityPool.setCompleteSetCollateralAmount(address(securityPool).balance - securityPool.totalFeesOvedToVaults()); //todo, we might want to reduce fees if we didn't get fully funded? + securityPool.setCompleteSetCollateralAmount(address(securityPool).balance - securityPool.totalFeesOwedToVaults()); //todo, we might want to reduce fees if we didn't get fully funded? uint256 parentTotalSecurityBondAllowance = parent.totalSecurityBondAllowance(); forkData[securityPool].auctionedSecurityBondAllowance = parentTotalSecurityBondAllowance - securityPool.totalSecurityBondAllowance(); securityPool.setTotalSecurityBondAllowance(parentTotalSecurityBondAllowance); diff --git a/solidity/contracts/peripherals/SecurityPoolUtils.sol b/solidity/contracts/peripherals/SecurityPoolUtils.sol index 9a19606..c11856f 100644 --- a/solidity/contracts/peripherals/SecurityPoolUtils.sol +++ b/solidity/contracts/peripherals/SecurityPoolUtils.sol @@ -26,7 +26,7 @@ library SecurityPoolUtils { } } - // starts from MAX_RETENTION_RATE, decrases linearly until RETENTION_RATE_DIP% utilization is hit and then caps to MIN_RETENTION_RATE + // starts from MAX_RETENTION_RATE, decreases linearly until RETENTION_RATE_DIP% utilization is hit and then caps to MIN_RETENTION_RATE // TODO: research more on how this should work function calculateRetentionRate(uint256 completeSetCollateralAmount, uint256 securityBondAllowance) external pure returns (uint256 z) { if (securityBondAllowance == 0) return MAX_RETENTION_RATE; diff --git a/solidity/contracts/peripherals/factories/AuctionFactory.sol b/solidity/contracts/peripherals/factories/AuctionFactory.sol index 0b460a9..7c335ba 100644 --- a/solidity/contracts/peripherals/factories/AuctionFactory.sol +++ b/solidity/contracts/peripherals/factories/AuctionFactory.sol @@ -4,6 +4,6 @@ import { Auction } from '../Auction.sol'; contract AuctionFactory { function deployAuction(bytes32 salt) external returns (Auction) { - return new Auction{ salt: keccak256(abi.encodePacked(msg.sender, salt)) }(); + return new Auction{ salt: keccak256(abi.encode(msg.sender, salt)) }(); } } diff --git a/solidity/contracts/peripherals/factories/PriceOracleManagerAndOperatorQueuerFactory.sol b/solidity/contracts/peripherals/factories/PriceOracleManagerAndOperatorQueuerFactory.sol index 6981cfb..2ba4c6c 100644 --- a/solidity/contracts/peripherals/factories/PriceOracleManagerAndOperatorQueuerFactory.sol +++ b/solidity/contracts/peripherals/factories/PriceOracleManagerAndOperatorQueuerFactory.sol @@ -9,6 +9,6 @@ import { PriceOracleManagerAndOperatorQueuer } from '../PriceOracleManagerAndOpe contract PriceOracleManagerAndOperatorQueuerFactory { function deployPriceOracleManagerAndOperatorQueuer(OpenOracle _openOracle, ReputationToken _reputationToken, bytes32 salt) external returns (PriceOracleManagerAndOperatorQueuer) { - return new PriceOracleManagerAndOperatorQueuer{ salt: keccak256(abi.encodePacked(msg.sender, salt)) }(_openOracle, _reputationToken); + return new PriceOracleManagerAndOperatorQueuer{ salt: keccak256(abi.encode(msg.sender, salt)) }(_openOracle, _reputationToken); } } diff --git a/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol b/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol index feebd37..e57b6a5 100644 --- a/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol +++ b/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol @@ -39,7 +39,8 @@ contract SecurityPoolFactory is ISecurityPoolFactory { } function deployChildSecurityPool(ISecurityPool parent, IShareToken shareToken, uint248 universeId, uint256 marketId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount) external returns (ISecurityPool securityPool, Auction truthAuction) { - bytes32 securityPoolSalt = keccak256(abi.encodePacked(parent, universeId, marketId, securityMultiplier)); + require(msg.sender === address(securityPoolForker), 'only securityPoolForker') + bytes32 securityPoolSalt = keccak256(abi.encode(parent, universeId, marketId, securityMultiplier)); ReputationToken reputationToken = zoltar.getRepToken(universeId); PriceOracleManagerAndOperatorQueuer priceOracleManagerAndOperatorQueuer = priceOracleManagerAndOperatorQueuerFactory.deployPriceOracleManagerAndOperatorQueuer(openOracle, reputationToken, securityPoolSalt); @@ -55,13 +56,13 @@ contract SecurityPoolFactory is ISecurityPoolFactory { } function deployOriginSecurityPool(uint248 universeId, string memory extraInfo, uint256 marketEndDate, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice) external returns (ISecurityPool securityPool) { - uint256 marketId = yesNoMarkets.createMarket(extraInfo, marketEndDate, keccak256(abi.encodePacked(address(this), universeId, securityMultiplier, extraInfo, marketEndDate))); + uint256 marketId = yesNoMarkets.createMarket(extraInfo, marketEndDate, keccak256(abi.encode(address(this), universeId, securityMultiplier, extraInfo, marketEndDate))); ReputationToken reputationToken = zoltar.getRepToken(universeId); - bytes32 securityPoolSalt = keccak256(abi.encodePacked(address(0x0), universeId, marketId, securityMultiplier)); + bytes32 securityPoolSalt = keccak256(abi.encode(address(0x0), universeId, marketId, securityMultiplier)); PriceOracleManagerAndOperatorQueuer priceOracleManagerAndOperatorQueuer = priceOracleManagerAndOperatorQueuerFactory.deployPriceOracleManagerAndOperatorQueuer(openOracle, reputationToken, securityPoolSalt); // sharetoken has different salt as sharetoken address does not change in forks - bytes32 shareTokenSalt = keccak256(abi.encodePacked(securityMultiplier, marketId)); + bytes32 shareTokenSalt = keccak256(abi.encode(securityMultiplier, marketId)); IShareToken shareToken = shareTokenFactory.deployShareToken(shareTokenSalt); securityPool = new SecurityPool{ salt: bytes32(uint256(0x0)) }(address(securityPoolForker), this, yesNoMarkets, escalationGameFactory, priceOracleManagerAndOperatorQueuer, shareToken, openOracle, ISecurityPool(payable(0x0)), zoltar, universeId, marketId, securityMultiplier); diff --git a/solidity/contracts/peripherals/interfaces/IAugur.sol b/solidity/contracts/peripherals/interfaces/IAugur.sol index db30c9e..842a9d0 100644 --- a/solidity/contracts/peripherals/interfaces/IAugur.sol +++ b/solidity/contracts/peripherals/interfaces/IAugur.sol @@ -17,7 +17,7 @@ interface IAugur { function createChildUniverse(bytes32 _parentPayoutDistributionHash, uint256[] memory _parentPayoutNumerators) external returns (address); function isKnownUniverse(address _universe) external view returns (bool); function trustedCashTransfer(address _from, address _to, uint256 _amount) external returns (bool); - function isTrustedSender(address _address) external returns (bool); + function isTrustedSender(address _address) external view returns (bool); function onCategoricalMarketCreated(uint256 _endTime, string memory _extraInfo, address _market, address _marketCreator, address _designatedReporter, uint256 _feePerCashInAttoCash, bytes32[] memory _outcomes) external returns (bool); function onYesNoMarketCreated(uint256 _endTime, string memory _extraInfo, address _market, address _marketCreator, address _designatedReporter, uint256 _feePerCashInAttoCash) external returns (bool); function onScalarMarketCreated(uint256 _endTime, string memory _extraInfo, address _market, address _marketCreator, address _designatedReporter, uint256 _feePerCashInAttoCash, int256[] memory _prices, uint256 _numTicks) external returns (bool); @@ -56,7 +56,7 @@ interface IAugur { function isKnownFeeSender(address _feeSender) external view returns (bool); function lookup(bytes32 _key) external view returns (address); function getTimestamp() external view returns (uint256); - function getMaximumMarketEndDate() external returns (uint256); + function getMaximumMarketEndDate() external view returns (uint256); function isKnownMarket(address _market) external view returns (bool); function derivePayoutDistributionHash(uint256[] memory _payoutNumerators, uint256 _numTicks, uint256 numOutcomes) external view returns (bytes32); function logValidityBondChanged(uint256 _validityBond) external returns (bool); diff --git a/solidity/contracts/peripherals/interfaces/ISecurityPool.sol b/solidity/contracts/peripherals/interfaces/ISecurityPool.sol index 2f2aca1..1a59497 100644 --- a/solidity/contracts/peripherals/interfaces/ISecurityPool.sol +++ b/solidity/contracts/peripherals/interfaces/ISecurityPool.sol @@ -41,7 +41,7 @@ interface ISecurityPool { function completeSetCollateralAmount() external view returns (uint256); function poolOwnershipDenominator() external view returns (uint256); function securityMultiplier() external view returns (uint256); - function totalFeesOvedToVaults() external view returns (uint256); + function totalFeesOwedToVaults() external view returns (uint256); function lastUpdatedFeeAccumulator() external view returns (uint256); function currentRetentionRate() external view returns (uint256); function securityVaults(address vault) external view returns (uint256 poolOwnership, uint256 securityBondAllowance, uint256 unpaidEthFees, uint256 feeIndex, uint256 lockedRepInEscalationGame); @@ -77,7 +77,7 @@ interface ISecurityPool { function createCompleteSet() external payable; function redeemCompleteSet(uint256 amount) external; - function escalationGame() external returns (EscalationGame); + function escalationGame() external view returns (EscalationGame); function setRetentionRate(uint256 newRetention) external; function setSystemState(SystemState newState) external; function setVaultOwnership(address vault, uint256 _poolOwnership, uint256 _securityBondAllowance) external; @@ -96,7 +96,7 @@ interface ISecurityPool { function stealAllRep() external; function migrateEth(address payable child, uint256 amount) external; - function securityPoolForker() external returns (address); + function securityPoolForker() external view returns (address); receive() external payable; } diff --git a/solidity/contracts/peripherals/interfaces/ISecurityPoolForker.sol b/solidity/contracts/peripherals/interfaces/ISecurityPoolForker.sol index 7a8365b..1be25e3 100644 --- a/solidity/contracts/peripherals/interfaces/ISecurityPoolForker.sol +++ b/solidity/contracts/peripherals/interfaces/ISecurityPoolForker.sol @@ -12,5 +12,5 @@ interface ISecurityPoolForker { function finalizeTruthAuction(ISecurityPool securityPool) external; function forkZoltarWithOwnEscalationGame(ISecurityPool securityPool) external; function claimAuctionProceeds(ISecurityPool securityPool, address vault) external; - function getMarketOutcome(ISecurityPool securityPool) external returns (YesNoMarkets.Outcome outcome); + function getMarketOutcome(ISecurityPool securityPool) external view returns (YesNoMarkets.Outcome outcome); } diff --git a/solidity/ts/tests/testPeripherals.ts b/solidity/ts/tests/testPeripherals.ts index f9c6af8..d9062f6 100644 --- a/solidity/ts/tests/testPeripherals.ts +++ b/solidity/ts/tests/testPeripherals.ts @@ -14,9 +14,9 @@ import { QuestionOutcome } from '../testsuite/simulator/types/types.js' import { approximatelyEqual, strictEqual18Decimal, strictEqualTypeSafe } from '../testsuite/simulator/utils/testUtils.js' import { claimAuctionProceeds, createChildUniverse, finalizeTruthAuction, forkSecurityPool, getMarketOutcome, getMigratedRep, getSecurityPoolForkerForkData, migrateFromEscalationGame, migrateVault, startTruthAuction } from '../testsuite/simulator/utils/contracts/securityPoolForker.js' import { SystemState } from '../testsuite/simulator/types/peripheralTypes.js' -import { getEscalationGameDeposits, getMarketResolution, getNonDecisionTreshold, getStartBond } from '../testsuite/simulator/utils/contracts/escalationGame.js' +import { getEscalationGameDeposits, getMarketResolution, getnonDecisionThreshold, getStartBond } from '../testsuite/simulator/utils/contracts/escalationGame.js' import { ensureZoltarDeployed, forkUniverse, getRepTokenAddress, getTotalTheoreticalSupply, getUniverseForkData, getZoltarAddress, getZoltarForkTreshold } from '../testsuite/simulator/utils/contracts/zoltar.js' -import { createCompleteSet, depositRep, depositToEscalationGame, getCompleteSetCollateralAmount, getCurrentRetentionRate, getPoolOwnershipDenominator, getRepToken, getSecurityPoolsEscalationGame, getSecurityVault, getSystemState, getTotalFeesOvedToVaults, getTotalSecurityBondAllowance, poolOwnershipToRep, redeemCompleteSet, redeemFees, redeemRep, redeemShares, sharesToCash, updateVaultFees, withdrawFromEscalationGame } from '../testsuite/simulator/utils/contracts/securityPool.js' +import { createCompleteSet, depositRep, depositToEscalationGame, getCompleteSetCollateralAmount, getCurrentRetentionRate, getPoolOwnershipDenominator, getRepToken, getSecurityPoolsEscalationGame, getSecurityVault, getSystemState, gettotalFeesOwedToVaults, getTotalSecurityBondAllowance, poolOwnershipToRep, redeemCompleteSet, redeemFees, redeemRep, redeemShares, sharesToCash, updateVaultFees, withdrawFromEscalationGame } from '../testsuite/simulator/utils/contracts/securityPool.js' describe('Peripherals Contract Test Suite', () => { let mockWindow: MockWindowEthereum @@ -78,7 +78,7 @@ describe('Peripherals Contract Test Suite', () => { const escalationGameAddress = await getSecurityPoolsEscalationGame(client, securityPoolAddresses.securityPool) strictEqualTypeSafe(escalationGameAddress, securityPoolAddresses.escalationGame, 'escalation game addresses do not match') - assert.ok(await getNonDecisionTreshold(client, securityPoolAddresses.escalationGame) > 10n * reportBond, 'fork treshold need to be big enough') + assert.ok(await getnonDecisionThreshold(client, securityPoolAddresses.escalationGame) > 10n * reportBond, 'fork treshold need to be big enough') await mockWindow.advanceTime(10n * DAY) const yesDeposits = await getEscalationGameDeposits(client, securityPoolAddresses.escalationGame, QuestionOutcome.Yes) strictEqualTypeSafe(yesDeposits.length, 1, 'the should have been one deposit') @@ -158,7 +158,7 @@ describe('Peripherals Contract Test Suite', () => { await mockWindow.setTime(endTime + 10000n) await updateVaultFees(client, securityPoolAddresses.securityPool, client.account.address) - const feesAccrued = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + const feesAccrued = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) const ethBalanceBefore = await getETHBalance(client, client.account.address) const securityVault = await getSecurityVault(client, securityPoolAddresses.securityPool, client.account.address) await redeemFees(client, securityPoolAddresses.securityPool, client.account.address) @@ -227,7 +227,7 @@ describe('Peripherals Contract Test Suite', () => { strictEqualTypeSafe(forkData.migratedRep, 0n, 'migrated rep should be 0 so far') strictEqualTypeSafe(forkData.outcomeIndex, 0, 'there should be no outcome') strictEqualTypeSafe(forkData.ownFork, true, 'should be own fork') - const totalFeesOvedToVaultsRightAfterFork = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + const totalFeesOwedToVaultsRightAfterFork = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) strictEqualTypeSafe(await getSystemState(client, securityPoolAddresses.securityPool), SystemState.PoolForked, 'Parent is forked') strictEqualTypeSafe(0n, await getERC20Balance(client, getRepTokenAddress(genesisUniverse), securityPoolAddresses.securityPool), 'Parents original rep is gone') await migrateVault(client, securityPoolAddresses.securityPool, QuestionOutcome.Yes) @@ -243,11 +243,11 @@ describe('Peripherals Contract Test Suite', () => { await startTruthAuction(client, yesSecurityPool.securityPool) strictEqualTypeSafe(await getSystemState(client, yesSecurityPool.securityPool), SystemState.Operational, 'yes System should be operational right away') - const totalFees = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOvedToVaults(client, yesSecurityPool.securityPool) + const totalFees = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await gettotalFeesOwedToVaults(client, yesSecurityPool.securityPool) approximatelyEqual(await getCompleteSetCollateralAmount(client, yesSecurityPool.securityPool), openInterestAmount - totalFees, 10n, 'child contract did not record the amount correctly') - const totalFeesOvedToVaultsAfterFork = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) - strictEqualTypeSafe(totalFeesOvedToVaultsRightAfterFork, totalFeesOvedToVaultsAfterFork, 'parents fees should be frozen') + const totalFeesOwedToVaultsAfterFork = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + strictEqualTypeSafe(totalFeesOwedToVaultsRightAfterFork, totalFeesOwedToVaultsAfterFork, 'parents fees should be frozen') }) test('two security pools with disagreement', async () => { @@ -291,7 +291,7 @@ describe('Peripherals Contract Test Suite', () => { strictEqualTypeSafe(await getERC20Balance(client, getRepTokenAddress(yesUniverse), yesSecurityPool.securityPool), repBalanceInGenesisPool - burnAmount, 'yes has all the rep') assert.ok(await contractExists(client, yesSecurityPool.securityPool), 'yes security pool exist') - const feesOved = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOvedToVaults(client, yesSecurityPool.securityPool) + const feesOved = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await gettotalFeesOwedToVaults(client, yesSecurityPool.securityPool) // attacker migrated to No const noUniverse = getChildUniverseId(genesisUniverse, QuestionOutcome.No) @@ -302,7 +302,7 @@ describe('Peripherals Contract Test Suite', () => { approximatelyEqual(migratedRepInNo, repDeposit, 10n, 'other side migrated to no') strictEqualTypeSafe(await getERC20Balance(client, getRepTokenAddress(noUniverse), noSecurityPool.securityPool), repBalanceInGenesisPool - burnAmount, 'no has all the rep') - approximatelyEqual(await getETHBalance(client, securityPoolAddresses.securityPool), await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool), 10n, 'there should be only fees left in old security pool') + approximatelyEqual(await getETHBalance(client, securityPoolAddresses.securityPool), await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool), 10n, 'there should be only fees left in old security pool') // invalid, no one migrated here await createChildUniverse(client, securityPoolAddresses.securityPool, QuestionOutcome.Invalid) // no one migrated, we need to create the universe as rep holders did not @@ -312,7 +312,7 @@ describe('Peripherals Contract Test Suite', () => { await mockWindow.advanceTime(8n * 7n * DAY + DAY) const getCurrentOpenInterestArray = async () => { - const currentFees = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOvedToVaults(client, yesSecurityPool.securityPool) + const currentFees = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await gettotalFeesOwedToVaults(client, yesSecurityPool.securityPool) return openInterestArray.map((x) => x - currentFees) } @@ -373,7 +373,7 @@ describe('Peripherals Contract Test Suite', () => { await redeemShares(openInterestHolder, yesSecurityPool.securityPool) const currentShares = await getCurrentOpenInterestArray() assert.deepStrictEqual(await balanceOfSharesInCash(client, yesSecurityPool.securityPool, securityPoolAddresses.shareToken, yesUniverse, addressString(TEST_ADDRESSES[2])), [currentShares[0], 0n, currentShares[2]], 'Not enough shares 1') - const fees = await getTotalFeesOvedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOvedToVaults(client, yesSecurityPool.securityPool) + const fees = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await gettotalFeesOwedToVaults(client, yesSecurityPool.securityPool) approximatelyEqual(await getETHBalance(client, addressString(TEST_ADDRESSES[2])), balancePriorYesRedeemal + openInterestAmount - fees, 10n ** 15n, 'did not gain eth after redeeming yes shares') // no status: auction fully funds, 3/4 of rep balance is sold for eth diff --git a/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts index 6e59d23..90aed88 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts @@ -1,5 +1,5 @@ import 'viem/window' -import { encodeDeployData, getCreate2Address, keccak256, numberToBytes, toHex, encodePacked, zeroAddress } from 'viem' +import { encodeDeployData, getCreate2Address, keccak256, numberToBytes, toHex, zeroAddress, encodeAbiParameters } from 'viem' import { WriteClient } from '../viem.js' import { PROXY_DEPLOYER_ADDRESS } from '../constants.js' import { addressString } from '../bigint.js' @@ -123,27 +123,50 @@ export async function ensureInfraDeployed(client: WriteClient): Promise { } const computeSecurityPoolSalt = (parent: `0x${ string }`, universeId: bigint, marketId: bigint, securityMultiplier: bigint) => { - const types = ['address', 'uint248', 'uint256', 'uint256'] as const const values = [parent, universeId, marketId, securityMultiplier] as const - return keccak256(encodePacked(types, values)) + return keccak256(encodeAbiParameters([ + { name: 'parent', type: 'address' }, + { name: 'universeId', type: 'uint248' }, + { name: 'marketId', type: 'uint256' }, + { name: 'securityMultiplier', type: 'uint256' }, + ], values)) } const computeShareTokenSalt = (securityMultiplier: bigint, marketId: bigint) => { - const types = ['uint256', 'uint256'] as const const values = [securityMultiplier, marketId] as const - return keccak256(encodePacked(types, values)) + return keccak256(encodeAbiParameters([ + { name: 'securityMultiplier', type: 'uint256' }, + { name: 'marketId', type: 'uint256' }, + ], values)) } export const getMarketId = (universeId: bigint, securityMultiplier: bigint, extraInfo: string, marketEndDate: bigint) => { const securityPoolfactory = getInfraContractAddresses().securityPoolFactory - const salt = keccak256(encodePacked(['address', 'uint248', 'uint256', 'string', 'uint256'], [securityPoolfactory, universeId, securityMultiplier, extraInfo, marketEndDate])) - return BigInt(keccak256(encodePacked(['address', 'string', 'uint256', 'bytes32'], [securityPoolfactory, extraInfo, marketEndDate, salt]))); + const marketCreationTypes = [ + { name: 'securityPoolfactory', type: 'uint256' }, + { name: 'universeId', type: 'uint248' }, + { name: 'securityMultiplier', type: 'uint256' }, + { name: 'extraInfo', type: 'string' }, + { name: 'marketEndDate', type: 'uint256' }, + ] + const salt = keccak256(encodeAbiParameters(marketCreationTypes, [securityPoolfactory, universeId, securityMultiplier, extraInfo, marketEndDate])) + const saltTypes = [ + { name: 'securityPoolfactory', type: 'address' }, + { name: 'extraInfo', type: 'string' }, + { name: 'marketEndDate', type: 'uint256' }, + { name: 'salt', type: 'bytes32' }, + ] + return BigInt(keccak256(encodeAbiParameters(saltTypes, [securityPoolfactory, extraInfo, marketEndDate, salt]))); } export const getSecurityPoolAddresses = (parent: `0x${ string }`, universeId: bigint, marketId: bigint, securityMultiplier: bigint) => { const securityPoolSalt = computeSecurityPoolSalt(parent, universeId, marketId, securityMultiplier) const infraContracts = getInfraContractAddresses() - const securityPoolSaltWithMsgSender = keccak256(encodePacked(['address', 'bytes32'] as const, [infraContracts.securityPoolFactory, securityPoolSalt])) + const securityPoolTypes = [ + { name: 'securityPoolfactory', type: 'address' }, + { name: 'securityPoolSalt', type: 'bytes32' }, + ] + const securityPoolSaltWithMsgSender = keccak256(encodeAbiParameters(securityPoolTypes, [infraContracts.securityPoolFactory, securityPoolSalt])) const contracts = { priceOracleManagerAndOperatorQueuer: getCreate2Address({ diff --git a/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts b/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts index a7a9f53..9216f66 100644 --- a/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts @@ -1,5 +1,5 @@ import 'viem/window' -import { encodeDeployData, getCreate2Address, keccak256, numberToBytes, toHex, encodePacked, zeroAddress } from 'viem' +import { encodeDeployData, getCreate2Address, keccak256, numberToBytes, toHex, zeroAddress, encodeAbiParameters } from 'viem' import { WriteClient } from './viem.js' import { PROXY_DEPLOYER_ADDRESS } from './constants.js' import { addressString } from './bigint.js' @@ -107,21 +107,23 @@ export async function ensureInfraDeployed(client: WriteClient): Promise { } const computeSecurityPoolSalt = (parent: `0x${ string }`, universeId: bigint, questionId: bigint, securityMultiplier: bigint) => { - const types = ['address', 'uint192', 'uint56', 'uint256'] as const const values = [parent, universeId, questionId, securityMultiplier] as const - return keccak256(encodePacked(types, values)) + return keccak256(encodeAbiParameters([ + { name: 'parent', type: 'address' }, + { name: 'universeId', type: 'uint192' }, + { name: 'questionId', type: 'uint56' }, + { name: 'securityMultiplier', type: 'uint256' }, + ], values)) } const computeShareTokenSalt = (securityMultiplier: bigint) => { - const types = ['uint256'] as const - const values = [securityMultiplier] as const - return keccak256(encodePacked(types, values)) + return keccak256(encodeAbiParameters([{ name: 'securityMultiplier', type: 'uint256' }], [securityMultiplier])) } export const getSecurityPoolAddresses = (parent: `0x${ string }`, universeId: bigint, questionId: bigint, securityMultiplier: bigint) => { const securityPoolSalt = computeSecurityPoolSalt(parent, universeId, questionId, securityMultiplier) const infraContracts = getInfraContractAddresses() - const securityPoolSaltWithMsgSender = keccak256(encodePacked(['address', 'bytes32'] as const, [infraContracts.securityPoolFactory, securityPoolSalt])) + const securityPoolSaltWithMsgSender = keccak256(encodeAbiParameters([{ name: 'securityPoolFactory', type:'address' }, { name: 'securityPoolSalt', type: 'bytes32' }] as const, [infraContracts.securityPoolFactory, securityPoolSalt])) const contracts = { priceOracleManagerAndOperatorQueuer: getCreate2Address({ diff --git a/solidity/ts/testsuite/simulator/utils/peripherals.ts b/solidity/ts/testsuite/simulator/utils/peripherals.ts index 6b82878..60e90b8 100644 --- a/solidity/ts/testsuite/simulator/utils/peripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/peripherals.ts @@ -437,10 +437,10 @@ export const createChildUniverse = async (client: WriteClient, securityPoolAddre }) } -export const getTotalFeesOvedToVaults = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { +export const gettotalFeesOwedToVaults = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { return await client.readContract({ abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'totalFeesOvedToVaults', + functionName: 'totalFeesOwedToVaults', address: securityPoolAddress, args: [], }) diff --git a/solidity/ts/testsuite/simulator/utils/testUtils.ts b/solidity/ts/testsuite/simulator/utils/testUtils.ts index 9221a18..903e232 100644 --- a/solidity/ts/testsuite/simulator/utils/testUtils.ts +++ b/solidity/ts/testsuite/simulator/utils/testUtils.ts @@ -5,6 +5,13 @@ export const strictEqualTypeSafe = (actual: Type, expected: Type, errorMes export const strictEqual18Decimal = (actual: bigint, expected: bigint, errorMessage?: string | Error | undefined) => assert.strictEqual(bigintToDecimalString(actual, 18n), bigintToDecimalString(expected, 18n), errorMessage) -export const approximatelyEqual = (actual: bigint, expected: bigint, errorDelta: bigint, message?: string | Error | undefined) => { - if (abs(actual - expected) > errorDelta) strictEqualTypeSafe(actual, expected, message) +export const approximatelyEqual = (actual: bigint, expected: bigint, errorDelta: bigint, errorMessage?: string | undefined) => { + const diff = abs(actual - expected) + if (diff > errorDelta) { + throw new assert.AssertionError({ + message: errorMessage || `Expected values to be within ${ errorDelta }, but difference was ${ diff }`, + actual, + expected + }) + } } From 84cde54356d4bfb86c492acdf4a4daffc5c23138 Mon Sep 17 00:00:00 2001 From: KillariDev Date: Fri, 13 Feb 2026 16:08:27 +0200 Subject: [PATCH 3/8] fix according to comments --- .../PriceOracleManagerAndOperatorQueuer.sol | 9 ++++++ .../peripherals/SecurityPoolForker.sol | 2 +- .../contracts/peripherals/YesNoMarkets.sol | 2 +- .../factories/SecurityPoolFactory.sol | 2 +- .../peripherals/interfaces/IShareToken.sol | 5 +-- .../peripherals/tokens/ForkedERC1155.sol | 2 +- .../peripherals/tokens/ShareToken.sol | 4 +-- solidity/ts/tests/testPeripherals.ts | 32 +++++++++---------- .../utils/contracts/deployPeripherals.ts | 3 +- .../simulator/utils/contracts/peripherals.ts | 4 +-- .../utils/contracts/peripheralsTestUtils.ts | 2 +- .../simulator/utils/contracts/securityPool.ts | 13 ++------ .../simulator/utils/deployPeripherals.ts | 1 - .../simulator/utils/logExplaining.ts | 2 +- .../testsuite/simulator/utils/peripherals.ts | 6 ++-- .../ts/testsuite/simulator/utils/utilities.ts | 2 +- 16 files changed, 45 insertions(+), 46 deletions(-) diff --git a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol index 7d2ec9d..42d248f 100644 --- a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol +++ b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol @@ -125,6 +125,9 @@ contract PriceOracleManagerAndOperatorQueuer { queuedPendingOperationId = previousQueuedOperationId; requestPrice(); } + // send rest of the eth back + (bool sent, ) = payable(msg.sendder).call{ value: address(this).balance }(''); + require(sent, 'Failed to return ethH'); } function executeQueuedOperation(uint256 operationId) public { @@ -136,18 +139,24 @@ contract PriceOracleManagerAndOperatorQueuer { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); } catch Error(string memory reason) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); + } catch (bytes memory lowLevelData) { + emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, 'Unknown error'); } } else if(queuedOperations[operationId].operation == OperationType.WithdrawRep) { try securityPool.performWithdrawRep(queuedOperations[operationId].initiatorVault, queuedOperations[operationId].amount) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); } catch Error(string memory reason) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); + } catch (bytes memory lowLevelData) { + emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, 'Unknown error'); } } else { try securityPool.performSetSecurityBondsAllowance(queuedOperations[operationId].initiatorVault, queuedOperations[operationId].amount) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); } catch Error(string memory reason) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); + } catch (bytes memory lowLevelData) { + emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, 'Unknown error'); } } queuedOperations[operationId].amount = 0; diff --git a/solidity/contracts/peripherals/SecurityPoolForker.sol b/solidity/contracts/peripherals/SecurityPoolForker.sol index a8fc148..7b625fc 100644 --- a/solidity/contracts/peripherals/SecurityPoolForker.sol +++ b/solidity/contracts/peripherals/SecurityPoolForker.sol @@ -173,7 +173,7 @@ contract SecurityPoolForker is ISecurityPoolForker { // we need to buy all the collateral that is missing (did not migrate) uint256 ethToBuy = parentCollateral - parentCollateral * forkData[securityPool].migratedRep / forkData[parent].repAtFork; // sell all but very small amount of REP for ETH. We cannot sell all for accounting purposes, as `poolOwnershipDenominator` cannot be infinite - // only migratedRep gets this guarrantee that some of their rep never gets sold + // only migratedRep gets this guarantee that some of their rep never gets sold forkData[securityPool].truthAuction.startAuction(ethToBuy, forkData[parent].repAtFork - forkData[securityPool].migratedRep / SecurityPoolUtils.MAX_AUCTION_VAULT_HAIRCUT_DIVISOR); } } diff --git a/solidity/contracts/peripherals/YesNoMarkets.sol b/solidity/contracts/peripherals/YesNoMarkets.sol index 85bd728..1424f3f 100644 --- a/solidity/contracts/peripherals/YesNoMarkets.sol +++ b/solidity/contracts/peripherals/YesNoMarkets.sol @@ -18,7 +18,7 @@ contract YesNoMarkets { mapping(uint256 => MarketData) markets; function createMarket(string memory extraInfo, uint256 marketEndDate, bytes32 salt) external returns (uint256) { - uint256 marketId = uint256(keccak256(abi.encodePacked(msg.sender, extraInfo, marketEndDate, salt))); + uint256 marketId = uint256(keccak256(abi.encode(msg.sender, extraInfo, marketEndDate, salt))); markets[marketId].extraInfo = extraInfo; markets[marketId].marketCreated = block.timestamp; markets[marketId].marketEndDate = marketEndDate; diff --git a/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol b/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol index e57b6a5..94a9db0 100644 --- a/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol +++ b/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol @@ -39,7 +39,7 @@ contract SecurityPoolFactory is ISecurityPoolFactory { } function deployChildSecurityPool(ISecurityPool parent, IShareToken shareToken, uint248 universeId, uint256 marketId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount) external returns (ISecurityPool securityPool, Auction truthAuction) { - require(msg.sender === address(securityPoolForker), 'only securityPoolForker') + require(msg.sender == address(securityPoolForker), 'only securityPoolForker') bytes32 securityPoolSalt = keccak256(abi.encode(parent, universeId, marketId, securityMultiplier)); ReputationToken reputationToken = zoltar.getRepToken(universeId); PriceOracleManagerAndOperatorQueuer priceOracleManagerAndOperatorQueuer = priceOracleManagerAndOperatorQueuerFactory.deployPriceOracleManagerAndOperatorQueuer(openOracle, reputationToken, securityPoolSalt); diff --git a/solidity/contracts/peripherals/interfaces/IShareToken.sol b/solidity/contracts/peripherals/interfaces/IShareToken.sol index 8e833af..c595a27 100644 --- a/solidity/contracts/peripherals/interfaces/IShareToken.sol +++ b/solidity/contracts/peripherals/interfaces/IShareToken.sol @@ -4,18 +4,19 @@ pragma solidity 0.8.33; import '../../Zoltar.sol'; import '../interfaces/ISecurityPool.sol'; import '../tokens/TokenId.sol'; +import '../../YesNoMarkets.sol'; interface IShareToken { function authorize(ISecurityPool _securityPoolCandidate) external; function mintCompleteSets(uint248 _universeId, address _account, uint256 _cashAmount) external payable; function burnCompleteSets(uint248 _universeId, address _owner, uint256 _amount) external; function burnTokenId(uint256 _tokenId, address _owner) external returns (uint256 balance); - function getUniverse(uint256 _tokenId) external pure returns(uint256); + function getUniverse(uint256 _tokenId) external pure returns(uint248); function getOutcome(uint256 _tokenId) external pure returns(YesNoMarkets.Outcome); function totalSupplyForOutcome(uint248 _universeId, YesNoMarkets.Outcome _outcome) external view returns (uint256); function balanceOfOutcome(uint248 _universeId, YesNoMarkets.Outcome _outcome, address _account) external view returns (uint256); function balanceOfShares(uint248 _universeId, address _account) external view returns (uint256[3] memory balances); function getTokenId(uint248 _universeId, YesNoMarkets.Outcome _outcome) external pure returns (uint256 _tokenId); function getTokenIds(uint248 _universeId, YesNoMarkets.Outcome[] memory _outcomes) external pure returns (uint256[] memory _tokenIds); - function unpackTokenId(uint248 _tokenId) external pure returns (uint248 _universe, YesNoMarkets.Outcome _outcome); + function unpackTokenId(uint256 _tokenId) external pure returns (uint248 _universe, YesNoMarkets.Outcome _outcome); } diff --git a/solidity/contracts/peripherals/tokens/ForkedERC1155.sol b/solidity/contracts/peripherals/tokens/ForkedERC1155.sol index d0e672e..0aca463 100644 --- a/solidity/contracts/peripherals/tokens/ForkedERC1155.sol +++ b/solidity/contracts/peripherals/tokens/ForkedERC1155.sol @@ -24,7 +24,7 @@ abstract contract ForkedERC1155 is ERC1155 { _balances[fromId][msg.sender] = 0; _supplys[fromId] -= fromIdBalance; - // TODO, check that outcomes is unique + // TODO, check that outcomes are unique // TODO, do we allow people to migrate later to different universes? for (uint8 i = 0; i < outcomes.length; i++) { uint256 toId = getChildId(fromId, getChildUniverseId(universeId, outcomes[i])); diff --git a/solidity/contracts/peripherals/tokens/ShareToken.sol b/solidity/contracts/peripherals/tokens/ShareToken.sol index cfe34e6..1358d02 100644 --- a/solidity/contracts/peripherals/tokens/ShareToken.sol +++ b/solidity/contracts/peripherals/tokens/ShareToken.sol @@ -82,7 +82,7 @@ contract ShareToken is ForkedERC1155, IShareToken { return uint248(uint256(keccak256(abi.encode(universeId, outcomeIndex)))); } - function getUniverse(uint256 _tokenId) external pure returns(uint256) { + function getUniverse(uint256 _tokenId) external pure returns(uint248) { (uint248 _universe, ) = TokenId.unpackTokenId(_tokenId); return _universe; } @@ -116,7 +116,7 @@ contract ShareToken is ForkedERC1155, IShareToken { return TokenId.getTokenIds(_universeId, _outcomes); } - function unpackTokenId(uint248 _tokenId) public pure returns (uint248 _universe, YesNoMarkets.Outcome _outcome) { + function unpackTokenId(uint256 _tokenId) public override pure returns (uint248 _universe, YesNoMarkets.Outcome _outcome) { return TokenId.unpackTokenId(_tokenId); } } diff --git a/solidity/ts/tests/testPeripherals.ts b/solidity/ts/tests/testPeripherals.ts index d9062f6..9f47836 100644 --- a/solidity/ts/tests/testPeripherals.ts +++ b/solidity/ts/tests/testPeripherals.ts @@ -16,7 +16,7 @@ import { claimAuctionProceeds, createChildUniverse, finalizeTruthAuction, forkSe import { SystemState } from '../testsuite/simulator/types/peripheralTypes.js' import { getEscalationGameDeposits, getMarketResolution, getnonDecisionThreshold, getStartBond } from '../testsuite/simulator/utils/contracts/escalationGame.js' import { ensureZoltarDeployed, forkUniverse, getRepTokenAddress, getTotalTheoreticalSupply, getUniverseForkData, getZoltarAddress, getZoltarForkTreshold } from '../testsuite/simulator/utils/contracts/zoltar.js' -import { createCompleteSet, depositRep, depositToEscalationGame, getCompleteSetCollateralAmount, getCurrentRetentionRate, getPoolOwnershipDenominator, getRepToken, getSecurityPoolsEscalationGame, getSecurityVault, getSystemState, gettotalFeesOwedToVaults, getTotalSecurityBondAllowance, poolOwnershipToRep, redeemCompleteSet, redeemFees, redeemRep, redeemShares, sharesToCash, updateVaultFees, withdrawFromEscalationGame } from '../testsuite/simulator/utils/contracts/securityPool.js' +import { createCompleteSet, depositRep, depositToEscalationGame, getCompleteSetCollateralAmount, getCurrentRetentionRate, getPoolOwnershipDenominator, getRepToken, getSecurityPoolsEscalationGame, getSecurityVault, getSystemState, getTotalFeesOwedToVaults, getTotalSecurityBondAllowance, poolOwnershipToRep, redeemCompleteSet, redeemFees, redeemRep, redeemShares, sharesToCash, updateVaultFees, withdrawFromEscalationGame } from '../testsuite/simulator/utils/contracts/securityPool.js' describe('Peripherals Contract Test Suite', () => { let mockWindow: MockWindowEthereum @@ -81,7 +81,7 @@ describe('Peripherals Contract Test Suite', () => { assert.ok(await getnonDecisionThreshold(client, securityPoolAddresses.escalationGame) > 10n * reportBond, 'fork treshold need to be big enough') await mockWindow.advanceTime(10n * DAY) const yesDeposits = await getEscalationGameDeposits(client, securityPoolAddresses.escalationGame, QuestionOutcome.Yes) - strictEqualTypeSafe(yesDeposits.length, 1, 'the should have been one deposit') + strictEqualTypeSafe(yesDeposits.length, 1, 'there should be one deposit') strictEqualTypeSafe(yesDeposits[0].depositIndex, 0n, 'index should be zero') strictEqualTypeSafe(yesDeposits[0].depositor, client.account.address, 'wrong depositor') strictEqualTypeSafe(yesDeposits[0].cumulativeAmount, reportBond, 'cumulator should be report bond') @@ -158,14 +158,14 @@ describe('Peripherals Contract Test Suite', () => { await mockWindow.setTime(endTime + 10000n) await updateVaultFees(client, securityPoolAddresses.securityPool, client.account.address) - const feesAccrued = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + const feesAccrued = await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) const ethBalanceBefore = await getETHBalance(client, client.account.address) const securityVault = await getSecurityVault(client, securityPoolAddresses.securityPool, client.account.address) await redeemFees(client, securityPoolAddresses.securityPool, client.account.address) strictEqualTypeSafe(securityVault.securityBondAllowance, securityPoolAllowance, 'securityPoolAllowance is all ours') const ethBalanceAfter = await getETHBalance(client, client.account.address) strictEqualTypeSafe(ethBalanceAfter - ethBalanceBefore, securityVault.unpaidEthFees, 'eth gained should be fees accrued') - strictEqualTypeSafe(feesAccrued / 1000n, securityVault.unpaidEthFees / 1000n, 'eth gained should be fees accrued (minus rounding issues') + strictEqualTypeSafe(feesAccrued / 1000n, securityVault.unpaidEthFees / 1000n, 'eth gained should be fees accrued (minus rounding issues)') const completeSetCollateralAmount = await getCompleteSetCollateralAmount(client, securityPoolAddresses.securityPool) strictEqualTypeSafe(feesAccrued + completeSetCollateralAmount, openInterestAmount, 'no eth lost') const timePassed = endTime - aMonthFromNow @@ -175,7 +175,7 @@ describe('Peripherals Contract Test Suite', () => { const expected = Number(1000n * rpow(retentionRate, timePassed, PRICE_PRECISION) / PRICE_PRECISION) / 10 strictEqualTypeSafe(completeSetCollateralAmountPercentage, expected, 'return amount did not match') const contractBalance = await getETHBalance(client, securityPoolAddresses.securityPool) - strictEqualTypeSafe(contractBalance + ethBalanceAfter - ethBalanceBefore, openInterestAmount, 'contract balance+ fees should equal initial open interest') + strictEqualTypeSafe(contractBalance + ethBalanceAfter - ethBalanceBefore, openInterestAmount, 'contract balance + fees should equal initial open interest') }) test('can set security bonds allowance, mint complete sets and fork happily' , async () => { @@ -187,7 +187,7 @@ describe('Peripherals Contract Test Suite', () => { strictEqualTypeSafe(await getLastPrice(client, securityPoolAddresses.priceOracleManagerAndOperatorQueuer), 1n * PRICE_PRECISION, 'Price was not set!') strictEqualTypeSafe(await getTotalSecurityBondAllowance(client, securityPoolAddresses.securityPool), securityPoolAllowance, 'Security pool allowance was not set correctly') - const forkTreshold = (await getTotalTheoreticalSupply(client, await getRepToken(client, securityPoolAddresses.securityPool))) / 20n + const forkThreshold = (await getTotalTheoreticalSupply(client, await getRepToken(client, securityPoolAddresses.securityPool))) / 20n await depositRep(client, securityPoolAddresses.securityPool, forkTreshold * 2n) const openInterestAmount = 100n * 10n ** 18n @@ -218,7 +218,7 @@ describe('Peripherals Contract Test Suite', () => { await triggerOwnGameFork(client, securityPoolAddresses.securityPool) const forkerRepBalance = await getERC20Balance(client, getRepTokenAddress(genesisUniverse), getInfraContractAddresses().securityPoolForker) const zoltarForkData = await getUniverseForkData(client, genesisUniverse) - strictEqualTypeSafe(zoltarForkData.forkerRepDeposit + forkerRepBalance + burnAmount, repBalance, 'forkerRepDeposit + forkerRepBalance+burnAmount should equal deposit') + strictEqualTypeSafe(zoltarForkData.forkerRepDeposit + forkerRepBalance + burnAmount, repBalance, 'forkerRepDeposit + forkerRepBalance + burnAmount should equal deposit') await forkSecurityPool(client, securityPoolAddresses.securityPool) @@ -227,7 +227,7 @@ describe('Peripherals Contract Test Suite', () => { strictEqualTypeSafe(forkData.migratedRep, 0n, 'migrated rep should be 0 so far') strictEqualTypeSafe(forkData.outcomeIndex, 0, 'there should be no outcome') strictEqualTypeSafe(forkData.ownFork, true, 'should be own fork') - const totalFeesOwedToVaultsRightAfterFork = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + const totalFeesOwedToVaultsRightAfterFork = await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) strictEqualTypeSafe(await getSystemState(client, securityPoolAddresses.securityPool), SystemState.PoolForked, 'Parent is forked') strictEqualTypeSafe(0n, await getERC20Balance(client, getRepTokenAddress(genesisUniverse), securityPoolAddresses.securityPool), 'Parents original rep is gone') await migrateVault(client, securityPoolAddresses.securityPool, QuestionOutcome.Yes) @@ -243,10 +243,10 @@ describe('Peripherals Contract Test Suite', () => { await startTruthAuction(client, yesSecurityPool.securityPool) strictEqualTypeSafe(await getSystemState(client, yesSecurityPool.securityPool), SystemState.Operational, 'yes System should be operational right away') - const totalFees = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await gettotalFeesOwedToVaults(client, yesSecurityPool.securityPool) + const totalFees = await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOwedToVaults(client, yesSecurityPool.securityPool) approximatelyEqual(await getCompleteSetCollateralAmount(client, yesSecurityPool.securityPool), openInterestAmount - totalFees, 10n, 'child contract did not record the amount correctly') - const totalFeesOwedToVaultsAfterFork = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + const totalFeesOwedToVaultsAfterFork = await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) strictEqualTypeSafe(totalFeesOwedToVaultsRightAfterFork, totalFeesOwedToVaultsAfterFork, 'parents fees should be frozen') }) @@ -260,7 +260,7 @@ describe('Peripherals Contract Test Suite', () => { const attackerClient = createWriteClient(mockWindow, TEST_ADDRESSES[1], 0) await approveAndDepositRep(attackerClient, repDeposit, marketId) await manipulatePriceOracleAndPerformOperation(attackerClient, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.SetSecurityBondsAllowance, client.account.address, securityPoolAllowance) - const forkTreshold = (await getTotalTheoreticalSupply(client, await getRepToken(client, securityPoolAddresses.securityPool))) / 20n + const forkThreshold = (await getTotalTheoreticalSupply(client, await getRepToken(client, securityPoolAddresses.securityPool))) / 20n const zoltarForkTreshold = await getZoltarForkTreshold(client, genesisUniverse) const burnAmount = zoltarForkTreshold / 5n @@ -291,18 +291,18 @@ describe('Peripherals Contract Test Suite', () => { strictEqualTypeSafe(await getERC20Balance(client, getRepTokenAddress(yesUniverse), yesSecurityPool.securityPool), repBalanceInGenesisPool - burnAmount, 'yes has all the rep') assert.ok(await contractExists(client, yesSecurityPool.securityPool), 'yes security pool exist') - const feesOved = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await gettotalFeesOwedToVaults(client, yesSecurityPool.securityPool) + const feesOved = await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOwedToVaults(client, yesSecurityPool.securityPool) // attacker migrated to No const noUniverse = getChildUniverseId(genesisUniverse, QuestionOutcome.No) const noSecurityPool = getSecurityPoolAddresses(securityPoolAddresses.securityPool, noUniverse, marketId, securityMultiplier) await migrateVault(attackerClient, securityPoolAddresses.securityPool, QuestionOutcome.No) - strictEqualTypeSafe(await getMarketOutcome(client, noSecurityPool.securityPool), QuestionOutcome.No, 'finalized as yes') + strictEqualTypeSafe(await getMarketOutcome(client, noSecurityPool.securityPool), QuestionOutcome.No, 'finalized as no') const migratedRepInNo = await getMigratedRep(client, noSecurityPool.securityPool) approximatelyEqual(migratedRepInNo, repDeposit, 10n, 'other side migrated to no') strictEqualTypeSafe(await getERC20Balance(client, getRepTokenAddress(noUniverse), noSecurityPool.securityPool), repBalanceInGenesisPool - burnAmount, 'no has all the rep') - approximatelyEqual(await getETHBalance(client, securityPoolAddresses.securityPool), await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool), 10n, 'there should be only fees left in old security pool') + approximatelyEqual(await getETHBalance(client, securityPoolAddresses.securityPool), await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool), 10n, 'there should be only fees left in old security pool') // invalid, no one migrated here await createChildUniverse(client, securityPoolAddresses.securityPool, QuestionOutcome.Invalid) // no one migrated, we need to create the universe as rep holders did not @@ -312,7 +312,7 @@ describe('Peripherals Contract Test Suite', () => { await mockWindow.advanceTime(8n * 7n * DAY + DAY) const getCurrentOpenInterestArray = async () => { - const currentFees = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await gettotalFeesOwedToVaults(client, yesSecurityPool.securityPool) + const currentFees = await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOwedToVaults(client, yesSecurityPool.securityPool) return openInterestArray.map((x) => x - currentFees) } @@ -373,7 +373,7 @@ describe('Peripherals Contract Test Suite', () => { await redeemShares(openInterestHolder, yesSecurityPool.securityPool) const currentShares = await getCurrentOpenInterestArray() assert.deepStrictEqual(await balanceOfSharesInCash(client, yesSecurityPool.securityPool, securityPoolAddresses.shareToken, yesUniverse, addressString(TEST_ADDRESSES[2])), [currentShares[0], 0n, currentShares[2]], 'Not enough shares 1') - const fees = await gettotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await gettotalFeesOwedToVaults(client, yesSecurityPool.securityPool) + const fees = await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOwedToVaults(client, yesSecurityPool.securityPool) approximatelyEqual(await getETHBalance(client, addressString(TEST_ADDRESSES[2])), balancePriorYesRedeemal + openInterestAmount - fees, 10n ** 15n, 'did not gain eth after redeeming yes shares') // no status: auction fully funds, 3/4 of rep balance is sold for eth diff --git a/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts index 90aed88..88b8ed3 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts @@ -118,7 +118,7 @@ export async function ensureInfraDeployed(client: WriteClient): Promise { if (!existence.securityPoolForker) await deployBytecode(getSecurityPoolForkerByteCode(contractAddresses.zoltar)) for (const [name, contractAddress] of objectEntries(contractAddresses)) { - if (!(await contractExists(client, contractAddress))) throw new Error(`${ name } does not exist eventhought we deployed it`) + if (!(await contractExists(client, contractAddress))) throw new Error(`${ name } does not exist even thought we deployed it`) } } @@ -217,7 +217,6 @@ export const getSecurityPoolAddresses = (parent: `0x${ string }`, universeId: bi export const deployOriginSecurityPool = async (client: WriteClient, universeId: bigint, extraInfo: string, marketEndDate: bigint, securityMultiplier: bigint, startingRetentionRate: bigint, startingRepEthPrice: bigint) => { const infraAddresses = getInfraContractAddresses() return await client.writeContract({ - chain: mainnet, abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi, functionName: 'deployOriginSecurityPool', address: infraAddresses.securityPoolFactory, diff --git a/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts b/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts index 6f56125..81f247f 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts @@ -242,14 +242,14 @@ export const balanceOfShares = async (client: ReadClient, shareTokenAddress: `0x }) } -export const balanceOfSharesInCash = async (client: ReadClient, seucurityPoolAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { +export const balanceOfSharesInCash = async (client: ReadClient, securityPoolAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { const array = await client.readContract({ abi: peripherals_tokens_ShareToken_ShareToken.abi, functionName: 'balanceOfShares', address: shareTokenAddress, args: [universeId, account], }) - return await shareArrayToCash(client, seucurityPoolAddress, array) + return await shareArrayToCash(client, securityPoolAddress, array) } export const getTokenId = (universeId: bigint, outcome: QuestionOutcome) => { diff --git a/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts b/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts index f7c8d52..5dbdbac 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts @@ -34,7 +34,7 @@ export const approveAndDepositRep = async (client: WriteClient, repDeposit: bigi export const triggerOwnGameFork = async(client: WriteClient, securityPoolAddress: `0x${ string }`) => { const repToken = await getRepToken(client, securityPoolAddress) - const forkTreshold = (await getTotalTheoreticalSupply(client, repToken)) / 20n /2n + const forkThreshold = (await getTotalTheoreticalSupply(client, repToken)) / 20n /2n const vault = await getSecurityVault(client, securityPoolAddress, client.account.address) const repAmount = await poolOwnershipToRep(client, securityPoolAddress, vault.repDepositShare) assert.ok(repAmount >= 2n * forkTreshold, 'not enough rep in vault to fork') diff --git a/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts b/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts index ebf8df5..f5844cd 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts @@ -142,10 +142,10 @@ export const redeemShares = async (client: WriteClient, securityPoolAddress: `0x } -export const getTotalFeesOvedToVaults = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { +export const getTotalFeesOwedToVaults = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { return await client.readContract({ abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'totalFeesOvedToVaults', + functionName: 'totalFeesOwedToVaults', address: securityPoolAddress, args: [], }) @@ -217,12 +217,3 @@ export const getRepToken = async(client: ReadClient, securityPoolAddress: `0x${ args: [], }) } - -export const isFinalized = async(client: ReadClient, securityPoolAddress: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'repToken', - address: securityPoolAddress, - args: [], - }) -} diff --git a/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts b/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts index 9216f66..5472f95 100644 --- a/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts @@ -165,7 +165,6 @@ export const getSecurityPoolAddresses = (parent: `0x${ string }`, universeId: bi export const deployOriginSecurityPool = async (client: WriteClient, universeId: bigint, questionId: bigint, securityMultiplier: bigint, startingRetentionRate: bigint, startingRepEthPrice: bigint, completeSetCollateralAmount: bigint) => { const infraAddresses = getInfraContractAddresses() return await client.writeContract({ - chain: mainnet, abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi, functionName: 'deployOriginSecurityPool', address: infraAddresses.securityPoolFactory, diff --git a/solidity/ts/testsuite/simulator/utils/logExplaining.ts b/solidity/ts/testsuite/simulator/utils/logExplaining.ts index 50aeb98..eda6643 100644 --- a/solidity/ts/testsuite/simulator/utils/logExplaining.ts +++ b/solidity/ts/testsuite/simulator/utils/logExplaining.ts @@ -1,6 +1,6 @@ import { Abi, decodeEventLog, GetLogsReturnType } from 'viem' -import { isUnknownAnAddress } from './utilities.js' +import { isUnknownAddress} from './utilities.js' export type Deployment = { deploymentName: string diff --git a/solidity/ts/testsuite/simulator/utils/peripherals.ts b/solidity/ts/testsuite/simulator/utils/peripherals.ts index 60e90b8..016fb66 100644 --- a/solidity/ts/testsuite/simulator/utils/peripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/peripherals.ts @@ -406,14 +406,14 @@ export const balanceOfShares = async (client: ReadClient, shareTokenAddress: `0x args: [universeId, account], }) } -export const balanceOfSharesInCash = async (client: ReadClient, seucurityPoolAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { +export const balanceOfSharesInCash = async (client: ReadClient, securityPoolAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { const array = await client.readContract({ abi: peripherals_tokens_ShareToken_ShareToken.abi, functionName: 'balanceOfShares', address: shareTokenAddress, args: [universeId, account], }) - return await shareArrayToCash(client, seucurityPoolAddress, array) + return await shareArrayToCash(client, securityPoolAddress, array) } export const getTokenId = (universeId: bigint, outcome: QuestionOutcome) => (universeId << 8n) + BigInt(outcome) @@ -437,7 +437,7 @@ export const createChildUniverse = async (client: WriteClient, securityPoolAddre }) } -export const gettotalFeesOwedToVaults = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { +export const getTotalFeesOwedToVaults = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { return await client.readContract({ abi: peripherals_SecurityPool_SecurityPool.abi, functionName: 'totalFeesOwedToVaults', diff --git a/solidity/ts/testsuite/simulator/utils/utilities.ts b/solidity/ts/testsuite/simulator/utils/utilities.ts index 4a36702..cf9f1f4 100644 --- a/solidity/ts/testsuite/simulator/utils/utilities.ts +++ b/solidity/ts/testsuite/simulator/utils/utilities.ts @@ -224,7 +224,7 @@ export async function ensureProxyDeployerDeployed(client: WriteClient): Promise< export const contractExists = async (client: ReadClient, contract: `0x${ string }`) => await client.getCode({ address: contract }) !== undefined -export const isUnknownAnAddress = (maybeAddress: unknown): maybeAddress is `0x${ string }` => typeof maybeAddress === 'string' && /^0x[a-fA-F0-9]{40}$/.test(maybeAddress) +export const isUnknownAddress= (maybeAddress: unknown): maybeAddress is `0x${ string }` => typeof maybeAddress === 'string' && /^0x[a-fA-F0-9]{40}$/.test(maybeAddress) const uint248BitMask = (1n << 248n) - 1n export function getChildUniverseId(parentUniverseId: bigint, outcome: bigint | QuestionOutcome): bigint { From 99615abf0484eb0efada157d3326d43048138582 Mon Sep 17 00:00:00 2001 From: KillariDev Date: Fri, 13 Feb 2026 16:29:37 +0200 Subject: [PATCH 4/8] fix according to comments --- .../PriceOracleManagerAndOperatorQueuer.sol | 4 ++-- .../contracts/peripherals/SecurityPool.sol | 9 +++++---- .../peripherals/SecurityPoolForker.sol | 2 +- .../contracts/peripherals/YesNoMarkets.sol | 1 + .../factories/SecurityPoolFactory.sol | 2 +- .../contracts/peripherals/tokens/ERC1155.sol | 12 ++++++------ .../peripherals/tokens/ForkedERC1155.sol | 4 ++-- .../peripherals/tokens/ShareToken.sol | 4 ++-- solidity/ts/abi/abis.ts | 2 +- solidity/ts/tests/testPeripherals.ts | 18 +++++++++--------- .../utils/contracts/deployPeripherals.ts | 2 +- .../simulator/utils/contracts/deployments.ts | 6 +++--- .../utils/contracts/peripheralsTestUtils.ts | 6 +++--- .../testsuite/simulator/utils/deployments.ts | 6 +++--- .../testsuite/simulator/utils/logExplaining.ts | 4 ++-- .../ts/testsuite/simulator/utils/testUtils.ts | 1 + .../ts/testsuite/simulator/utils/utilities.ts | 2 +- 17 files changed, 44 insertions(+), 41 deletions(-) diff --git a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol index 42d248f..3d4c6db 100644 --- a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol +++ b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol @@ -126,8 +126,8 @@ contract PriceOracleManagerAndOperatorQueuer { requestPrice(); } // send rest of the eth back - (bool sent, ) = payable(msg.sendder).call{ value: address(this).balance }(''); - require(sent, 'Failed to return ethH'); + (bool sent, ) = payable(msg.sender).call{ value: address(this).balance }(''); + require(sent, 'Failed to return eth'); } function executeQueuedOperation(uint256 operationId) public { diff --git a/solidity/contracts/peripherals/SecurityPool.sol b/solidity/contracts/peripherals/SecurityPool.sol index f5cabb0..1b19a48 100644 --- a/solidity/contracts/peripherals/SecurityPool.sol +++ b/solidity/contracts/peripherals/SecurityPool.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.33; import { Auction } from './Auction.sol'; -import { Zoltar, FORK_TRESHOLD_DIVISOR } from '../Zoltar.sol'; +import { Zoltar, FORK_THRESHOLD_DIVISOR } from '../Zoltar.sol'; import { ReputationToken } from '../ReputationToken.sol'; import { IShareToken } from './interfaces/IShareToken.sol'; import { PriceOracleManagerAndOperatorQueuer, QueuedOperation } from './PriceOracleManagerAndOperatorQueuer.sol'; @@ -114,7 +114,7 @@ contract SecurityPool is ISecurityPool { uint256 endTime = yesNoMarkets.getMarketEndDate(marketId); uint256 feeEndDate = forkTime == 0 ? endTime : forkTime; uint256 clampedCurrentTimestamp = block.timestamp > feeEndDate ? feeEndDate : block.timestamp; - if (lastUpdatedFeeAccumulator > clampedCurrentTimestamp) return; // todo, this probably souldnt here as we shouldn't be getting any fees? + if (lastUpdatedFeeAccumulator > clampedCurrentTimestamp) return; uint256 timeDelta = clampedCurrentTimestamp - lastUpdatedFeeAccumulator; if (timeDelta == 0) return; @@ -307,7 +307,7 @@ contract SecurityPool is ISecurityPool { if (address(escalationGame) == address(0x0)) { uint256 endTime = yesNoMarkets.getMarketEndDate(marketId); require(block.timestamp > endTime, 'market has not ended'); - escalationGame = escalationGameFactory.deployEscalationGame(TODO_INITIAL_ESCALATION_GAME_DEPOSIT, repToken.getTotalTheoreticalSupply() / (FORK_TRESHOLD_DIVISOR * 2)); + escalationGame = escalationGameFactory.deployEscalationGame(TODO_INITIAL_ESCALATION_GAME_DEPOSIT, repToken.getTotalTheoreticalSupply() / (FORK_THRESHOLD_DIVISOR * 2)); } securityVaults[msg.sender].lockedRepInEscalationGame += escalationGame.depositOnOutcome(msg.sender, outcome, maxAmount); require(poolOwnershipToRep(securityVaults[msg.sender].poolOwnership) >= securityVaults[msg.sender].lockedRepInEscalationGame, 'Not enough REP'); @@ -317,7 +317,7 @@ contract SecurityPool is ISecurityPool { require(address(escalationGame) != address(0x0), 'escalation game needs to be deployed'); YesNoMarkets.Outcome outcome = ISecurityPoolForker(securityPoolForker).getMarketOutcome(this); require(outcome != YesNoMarkets.Outcome.None, 'Market has not finalized!'); - require(!escalationGame.hasReachedNonDecision(), 'cannot withdraw, escalation game is undecisive'); + require(!escalationGame.hasReachedNonDecision(), 'cannot withdraw, escalation game is indecisive'); for (uint256 index = 0; index < depositIndexes.length; index++) { (address depositor, uint256 amountToWithdraw) = escalationGame.withdrawDeposit(depositIndexes[index]); securityVaults[depositor].poolOwnership += repToPoolOwnership(amountToWithdraw); @@ -384,5 +384,6 @@ contract SecurityPool is ISecurityPool { receive() external payable { // needed for Truth Auction to send ETH back + // TODO, add check that its truth auction sending } } diff --git a/solidity/contracts/peripherals/SecurityPoolForker.sol b/solidity/contracts/peripherals/SecurityPoolForker.sol index 7b625fc..1e0331d 100644 --- a/solidity/contracts/peripherals/SecurityPoolForker.sol +++ b/solidity/contracts/peripherals/SecurityPoolForker.sol @@ -179,7 +179,7 @@ contract SecurityPoolForker is ISecurityPoolForker { } function _finalizeTruthAuction(ISecurityPool securityPool, uint256 repPurchased) private { - require(securityPool.systemState() == SystemState.ForkTruthAuction, 'Auction need to have started'); + require(securityPool.systemState() == SystemState.ForkTruthAuction, 'Auction needs to have started'); forkData[securityPool].truthAuction.finalizeAuction(address(securityPool)); // this sends the eth back securityPool.setSystemState(SystemState.Operational); ISecurityPool parent = securityPool.parent(); diff --git a/solidity/contracts/peripherals/YesNoMarkets.sol b/solidity/contracts/peripherals/YesNoMarkets.sol index 1424f3f..3fe6ee2 100644 --- a/solidity/contracts/peripherals/YesNoMarkets.sol +++ b/solidity/contracts/peripherals/YesNoMarkets.sol @@ -19,6 +19,7 @@ contract YesNoMarkets { function createMarket(string memory extraInfo, uint256 marketEndDate, bytes32 salt) external returns (uint256) { uint256 marketId = uint256(keccak256(abi.encode(msg.sender, extraInfo, marketEndDate, salt))); + require(markets[marketId].marketCreated == 0, 'Market already exists'); markets[marketId].extraInfo = extraInfo; markets[marketId].marketCreated = block.timestamp; markets[marketId].marketEndDate = marketEndDate; diff --git a/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol b/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol index 94a9db0..043d48e 100644 --- a/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol +++ b/solidity/contracts/peripherals/factories/SecurityPoolFactory.sol @@ -39,7 +39,7 @@ contract SecurityPoolFactory is ISecurityPoolFactory { } function deployChildSecurityPool(ISecurityPool parent, IShareToken shareToken, uint248 universeId, uint256 marketId, uint256 securityMultiplier, uint256 currentRetentionRate, uint256 startingRepEthPrice, uint256 completeSetCollateralAmount) external returns (ISecurityPool securityPool, Auction truthAuction) { - require(msg.sender == address(securityPoolForker), 'only securityPoolForker') + require(msg.sender == address(securityPoolForker), 'only securityPoolForker'); bytes32 securityPoolSalt = keccak256(abi.encode(parent, universeId, marketId, securityMultiplier)); ReputationToken reputationToken = zoltar.getRepToken(universeId); PriceOracleManagerAndOperatorQueuer priceOracleManagerAndOperatorQueuer = priceOracleManagerAndOperatorQueuerFactory.deployPriceOracleManagerAndOperatorQueuer(openOracle, reputationToken, securityPoolSalt); diff --git a/solidity/contracts/peripherals/tokens/ERC1155.sol b/solidity/contracts/peripherals/tokens/ERC1155.sol index c2ed1c3..53a1635 100644 --- a/solidity/contracts/peripherals/tokens/ERC1155.sol +++ b/solidity/contracts/peripherals/tokens/ERC1155.sol @@ -16,7 +16,7 @@ contract ERC1155 is IERC1155 { mapping (uint256 => mapping(address => uint256)) public _balances; // Mapping from token ID to total supply - mapping (uint256 => uint256) public _supplys; + mapping (uint256 => uint256) public _supplies; // Mapping from account to operator approvals mapping (address => mapping(address => bool)) public _operatorApprovals; @@ -38,7 +38,7 @@ contract ERC1155 is IERC1155 { } function totalSupply(uint256 id) public view returns (uint256) { - return _supplys[id]; + return _supplies[id]; } /** @@ -222,7 +222,7 @@ contract ERC1155 is IERC1155 { require(to != address(0), "ERC1155: mint to the zero address"); _balances[id][to] = _balances[id][to] + value; - _supplys[id] = _supplys[id] + value; + _supplies[id] = _supplies[id] + value; emit TransferSingle(msg.sender, address(0), to, id, value); } @@ -239,7 +239,7 @@ contract ERC1155 is IERC1155 { for (uint i = 0; i < ids.length; i++) { _balances[ids[i]][to] = values[i] + _balances[ids[i]][to]; - _supplys[ids[i]] = _supplys[ids[i]] + values[i]; + _supplies[ids[i]] = _supplies[ids[i]] + values[i]; } emit TransferBatch(msg.sender, address(0), to, ids, values); @@ -255,7 +255,7 @@ contract ERC1155 is IERC1155 { require(account != address(0), "ERC1155: attempting to burn tokens on zero account"); _balances[id][account] = _balances[id][account] - value; - _supplys[id] = _supplys[id] - value; + _supplies[id] = _supplies[id] - value; emit TransferSingle(msg.sender, account, address(0), id, value); } @@ -271,7 +271,7 @@ contract ERC1155 is IERC1155 { for (uint i = 0; i < ids.length; i++) { _balances[ids[i]][account] = _balances[ids[i]][account] - values[i]; - _supplys[ids[i]] = _supplys[ids[i]] - values[i]; + _supplies[ids[i]] = _supplies[ids[i]] - values[i]; } emit TransferBatch(msg.sender, account, address(0), ids, values); diff --git a/solidity/contracts/peripherals/tokens/ForkedERC1155.sol b/solidity/contracts/peripherals/tokens/ForkedERC1155.sol index 0aca463..ae3ded2 100644 --- a/solidity/contracts/peripherals/tokens/ForkedERC1155.sol +++ b/solidity/contracts/peripherals/tokens/ForkedERC1155.sol @@ -22,14 +22,14 @@ abstract contract ForkedERC1155 is ERC1155 { uint256 fromIdBalance = _balances[fromId][msg.sender]; _balances[fromId][msg.sender] = 0; - _supplys[fromId] -= fromIdBalance; + _supplies[fromId] -= fromIdBalance; // TODO, check that outcomes are unique // TODO, do we allow people to migrate later to different universes? for (uint8 i = 0; i < outcomes.length; i++) { uint256 toId = getChildId(fromId, getChildUniverseId(universeId, outcomes[i])); _balances[toId][msg.sender] += fromIdBalance; - _supplys[toId] += fromIdBalance; + _supplies[toId] += fromIdBalance; emit Migrate(msg.sender, fromId, toId, fromIdBalance); } } diff --git a/solidity/contracts/peripherals/tokens/ShareToken.sol b/solidity/contracts/peripherals/tokens/ShareToken.sol index 1358d02..0946574 100644 --- a/solidity/contracts/peripherals/tokens/ShareToken.sol +++ b/solidity/contracts/peripherals/tokens/ShareToken.sol @@ -30,7 +30,7 @@ contract ShareToken is ForkedERC1155, IShareToken { } function authorize(ISecurityPool _securityPoolCandidate) external { - require(authorized[msg.sender], 'caller is not owner'); + require(authorized[msg.sender], 'not authorized'); authorized[address(_securityPoolCandidate)] = true; } @@ -116,7 +116,7 @@ contract ShareToken is ForkedERC1155, IShareToken { return TokenId.getTokenIds(_universeId, _outcomes); } - function unpackTokenId(uint256 _tokenId) public override pure returns (uint248 _universe, YesNoMarkets.Outcome _outcome) { + function unpackTokenId(uint256 _tokenId) public override pure returns (uint248 _universe, YesNoMarkets.Outcome _outcome) { return TokenId.unpackTokenId(_tokenId); } } diff --git a/solidity/ts/abi/abis.ts b/solidity/ts/abi/abis.ts index 68b82b7..4f56127 100644 --- a/solidity/ts/abi/abis.ts +++ b/solidity/ts/abi/abis.ts @@ -1,6 +1,6 @@ export const ABIS = { 'mainnet': { 'erc20': [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}], - 'erc1155': [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"_balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"_operatorApprovals","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_supplys","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}], + 'erc1155': [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"_balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"_operatorApprovals","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_supplies","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}], } } as const diff --git a/solidity/ts/tests/testPeripherals.ts b/solidity/ts/tests/testPeripherals.ts index 9f47836..7a29e4f 100644 --- a/solidity/ts/tests/testPeripherals.ts +++ b/solidity/ts/tests/testPeripherals.ts @@ -15,7 +15,7 @@ import { approximatelyEqual, strictEqual18Decimal, strictEqualTypeSafe } from '. import { claimAuctionProceeds, createChildUniverse, finalizeTruthAuction, forkSecurityPool, getMarketOutcome, getMigratedRep, getSecurityPoolForkerForkData, migrateFromEscalationGame, migrateVault, startTruthAuction } from '../testsuite/simulator/utils/contracts/securityPoolForker.js' import { SystemState } from '../testsuite/simulator/types/peripheralTypes.js' import { getEscalationGameDeposits, getMarketResolution, getnonDecisionThreshold, getStartBond } from '../testsuite/simulator/utils/contracts/escalationGame.js' -import { ensureZoltarDeployed, forkUniverse, getRepTokenAddress, getTotalTheoreticalSupply, getUniverseForkData, getZoltarAddress, getZoltarForkTreshold } from '../testsuite/simulator/utils/contracts/zoltar.js' +import { ensureZoltarDeployed, forkUniverse, getRepTokenAddress, getTotalTheoreticalSupply, getUniverseForkData, getZoltarAddress, getZoltarforkThreshold } from '../testsuite/simulator/utils/contracts/zoltar.js' import { createCompleteSet, depositRep, depositToEscalationGame, getCompleteSetCollateralAmount, getCurrentRetentionRate, getPoolOwnershipDenominator, getRepToken, getSecurityPoolsEscalationGame, getSecurityVault, getSystemState, getTotalFeesOwedToVaults, getTotalSecurityBondAllowance, poolOwnershipToRep, redeemCompleteSet, redeemFees, redeemRep, redeemShares, sharesToCash, updateVaultFees, withdrawFromEscalationGame } from '../testsuite/simulator/utils/contracts/securityPool.js' describe('Peripherals Contract Test Suite', () => { @@ -188,7 +188,7 @@ describe('Peripherals Contract Test Suite', () => { strictEqualTypeSafe(await getTotalSecurityBondAllowance(client, securityPoolAddresses.securityPool), securityPoolAllowance, 'Security pool allowance was not set correctly') const forkThreshold = (await getTotalTheoreticalSupply(client, await getRepToken(client, securityPoolAddresses.securityPool))) / 20n - await depositRep(client, securityPoolAddresses.securityPool, forkTreshold * 2n) + await depositRep(client, securityPoolAddresses.securityPool, forkThreshold * 2n) const openInterestAmount = 100n * 10n ** 18n const maxGasFees = openInterestAmount / 4n @@ -213,8 +213,8 @@ describe('Peripherals Contract Test Suite', () => { const repBalance = await getERC20Balance(client, getRepTokenAddress(genesisUniverse), securityPoolAddresses.securityPool) // forking - const zoltarForkTreshold = await getZoltarForkTreshold(client, genesisUniverse) - const burnAmount = zoltarForkTreshold / 5n + const zoltarforkThreshold = await getZoltarforkThreshold (client, genesisUniverse) + const burnAmount = zoltarforkThreshold / 5n await triggerOwnGameFork(client, securityPoolAddresses.securityPool) const forkerRepBalance = await getERC20Balance(client, getRepTokenAddress(genesisUniverse), getInfraContractAddresses().securityPoolForker) const zoltarForkData = await getUniverseForkData(client, genesisUniverse) @@ -262,12 +262,12 @@ describe('Peripherals Contract Test Suite', () => { await manipulatePriceOracleAndPerformOperation(attackerClient, mockWindow, securityPoolAddresses.priceOracleManagerAndOperatorQueuer, OperationType.SetSecurityBondsAllowance, client.account.address, securityPoolAllowance) const forkThreshold = (await getTotalTheoreticalSupply(client, await getRepToken(client, securityPoolAddresses.securityPool))) / 20n - const zoltarForkTreshold = await getZoltarForkTreshold(client, genesisUniverse) - const burnAmount = zoltarForkTreshold / 5n - await depositRep(client, securityPoolAddresses.securityPool, 2n * forkTreshold) + const zoltarforkThreshold = await getZoltarforkThreshold (client, genesisUniverse) + const burnAmount = zoltarforkThreshold / 5n + await depositRep(client, securityPoolAddresses.securityPool, 2n * forkThreshold ) const repBalanceInGenesisPool = await getERC20Balance(client, getRepTokenAddress(genesisUniverse), securityPoolAddresses.securityPool) - strictEqual18Decimal(repBalanceInGenesisPool, 2n * repDeposit + 2n * forkTreshold, 'After two deposits, the system should have 2 x repDeposit worth of REP + 2x fork') + strictEqual18Decimal(repBalanceInGenesisPool, 2n * repDeposit + 2n * forkThreshold , 'After two deposits, the system should have 2 x repDeposit worth of REP + 2x fork') strictEqual18Decimal(await getTotalSecurityBondAllowance(client, securityPoolAddresses.securityPool), 2n * securityPoolAllowance, 'Security bond allowance should be 2x') strictEqual18Decimal(await getPoolOwnershipDenominator(client, securityPoolAddresses.securityPool), repBalanceInGenesisPool * PRICE_PRECISION, 'Pool ownership denominator should equal `pool balance * PRICE_PRECISION` prior fork') @@ -291,7 +291,7 @@ describe('Peripherals Contract Test Suite', () => { strictEqualTypeSafe(await getERC20Balance(client, getRepTokenAddress(yesUniverse), yesSecurityPool.securityPool), repBalanceInGenesisPool - burnAmount, 'yes has all the rep') assert.ok(await contractExists(client, yesSecurityPool.securityPool), 'yes security pool exist') - const feesOved = await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOwedToVaults(client, yesSecurityPool.securityPool) + const feesOwed= await getTotalFeesOwedToVaults(client, securityPoolAddresses.securityPool) + await getTotalFeesOwedToVaults(client, yesSecurityPool.securityPool) // attacker migrated to No const noUniverse = getChildUniverseId(genesisUniverse, QuestionOutcome.No) diff --git a/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts index 88b8ed3..83197a6 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts @@ -143,7 +143,7 @@ const computeShareTokenSalt = (securityMultiplier: bigint, marketId: bigint) => export const getMarketId = (universeId: bigint, securityMultiplier: bigint, extraInfo: string, marketEndDate: bigint) => { const securityPoolfactory = getInfraContractAddresses().securityPoolFactory const marketCreationTypes = [ - { name: 'securityPoolfactory', type: 'uint256' }, + { name: 'securityPoolfactory', type: 'address' }, { name: 'universeId', type: 'uint248' }, { name: 'securityMultiplier', type: 'uint256' }, { name: 'extraInfo', type: 'string' }, diff --git a/solidity/ts/testsuite/simulator/utils/contracts/deployments.ts b/solidity/ts/testsuite/simulator/utils/contracts/deployments.ts index 0b2dae5..713fbbc 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/deployments.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/deployments.ts @@ -57,10 +57,10 @@ export const getDeployments = (genesisUniverse: bigint, questionId: bigint, secu const infraAddresses = getInfraContractAddresses() const originAddresses = getSecurityPoolAddresses(zeroAddress, genesisUniverse, questionId, securityMultiplier) - const oucomes = [QuestionOutcome.Invalid, QuestionOutcome.No, QuestionOutcome.Yes] + const outcomes = [QuestionOutcome.Invalid, QuestionOutcome.No, QuestionOutcome.Yes] const getChildAddresses = (parentSecurityPoolAddress: `0x${ string }`, parentUniverseId: bigint): Deployment[] => { - return oucomes.flatMap((outcome) => { + return outcomes.flatMap((outcome) => { const universeId = getChildUniverseId(parentUniverseId, BigInt(outcome)) const childAddresses = getSecurityPoolAddresses(parentSecurityPoolAddress, universeId, questionId, securityMultiplier) return getDeploymentsForUniverse(universeId, childAddresses.securityPool, getRepTokenAddress(universeId), childAddresses.priceOracleManagerAndOperatorQueuer, childAddresses.shareToken, childAddresses.truthAuction, childAddresses.escalationGame) @@ -70,7 +70,7 @@ export const getDeployments = (genesisUniverse: bigint, questionId: bigint, secu return ([ ...getDeploymentsForUniverse(genesisUniverse, originAddresses.securityPool, getRepTokenAddress(genesisUniverse), originAddresses.priceOracleManagerAndOperatorQueuer, originAddresses.shareToken, originAddresses.truthAuction, originAddresses.escalationGame), ...getChildAddresses(originAddresses.securityPool, genesisUniverse), // children - ...oucomes.flatMap((outcome) => getChildAddresses(getSecurityPoolAddresses(originAddresses.securityPool, genesisUniverse, questionId, securityMultiplier).securityPool, getChildUniverseId(genesisUniverse, BigInt(outcome)))), // grand children + ...outcomes.flatMap((outcome) => getChildAddresses(getSecurityPoolAddresses(originAddresses.securityPool, genesisUniverse, questionId, securityMultiplier).securityPool, getChildUniverseId(genesisUniverse, BigInt(outcome)))), // grand children { abi: Zoltar_Zoltar.abi, deploymentName: 'Zoltar', diff --git a/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts b/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts index 5dbdbac..821e6cb 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts @@ -37,9 +37,9 @@ export const triggerOwnGameFork = async(client: WriteClient, securityPoolAddress const forkThreshold = (await getTotalTheoreticalSupply(client, repToken)) / 20n /2n const vault = await getSecurityVault(client, securityPoolAddress, client.account.address) const repAmount = await poolOwnershipToRep(client, securityPoolAddress, vault.repDepositShare) - assert.ok(repAmount >= 2n * forkTreshold, 'not enough rep in vault to fork') - await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.Yes, forkTreshold) - await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.No, forkTreshold) + assert.ok(repAmount >= 2n * forkThreshold , 'not enough rep in vault to fork') + await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.Yes, forkThreshold ) + await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.No, forkThreshold ) await forkZoltarWithOwnEscalationGame(client, securityPoolAddress) } diff --git a/solidity/ts/testsuite/simulator/utils/deployments.ts b/solidity/ts/testsuite/simulator/utils/deployments.ts index 2e8c761..468a9e7 100644 --- a/solidity/ts/testsuite/simulator/utils/deployments.ts +++ b/solidity/ts/testsuite/simulator/utils/deployments.ts @@ -48,10 +48,10 @@ export const getDeployments = (genesisUniverse: bigint, questionId: bigint, secu const infraAddresses = getInfraContractAddresses() const originAddresses = getSecurityPoolAddresses(zeroAddress, genesisUniverse, questionId, securityMultiplier) - const oucomes = [QuestionOutcome.Invalid, QuestionOutcome.No, QuestionOutcome.Yes] + const outcomes = [QuestionOutcome.Invalid, QuestionOutcome.No, QuestionOutcome.Yes] const getChildAddresses = (parentSecurityPoolAddress: `0x${ string }`, parentUniverseId: bigint): Deployment[] => { - return oucomes.flatMap((outcome) => { + return outcomes.flatMap((outcome) => { const universeId = getChildUniverseId(parentUniverseId, outcome) const childAddresses = getSecurityPoolAddresses(parentSecurityPoolAddress, universeId, questionId, securityMultiplier) return getDeploymentsForUniverse(universeId, childAddresses.securityPool, getRepTokenAddress(universeId), childAddresses.priceOracleManagerAndOperatorQueuer, childAddresses.shareToken, childAddresses.truthAuction) @@ -61,7 +61,7 @@ export const getDeployments = (genesisUniverse: bigint, questionId: bigint, secu return ([ ...getDeploymentsForUniverse(genesisUniverse, originAddresses.securityPool, getRepTokenAddress(genesisUniverse), originAddresses.priceOracleManagerAndOperatorQueuer, originAddresses.shareToken, originAddresses.truthAuction), ...getChildAddresses(originAddresses.securityPool, genesisUniverse), // children - ...oucomes.flatMap((outcome) => getChildAddresses(getSecurityPoolAddresses(originAddresses.securityPool, genesisUniverse, questionId, securityMultiplier).securityPool, getChildUniverseId(genesisUniverse, outcome))), // grand children + ...outcomes.flatMap((outcome) => getChildAddresses(getSecurityPoolAddresses(originAddresses.securityPool, genesisUniverse, questionId, securityMultiplier).securityPool, getChildUniverseId(genesisUniverse, outcome))), // grand children { abi: Zoltar_Zoltar.abi, deploymentName: 'Zoltar', diff --git a/solidity/ts/testsuite/simulator/utils/logExplaining.ts b/solidity/ts/testsuite/simulator/utils/logExplaining.ts index eda6643..611a4b9 100644 --- a/solidity/ts/testsuite/simulator/utils/logExplaining.ts +++ b/solidity/ts/testsuite/simulator/utils/logExplaining.ts @@ -1,6 +1,6 @@ import { Abi, decodeEventLog, GetLogsReturnType } from 'viem' -import { isUnknownAddress} from './utilities.js' +import { isUnknownAddress } from './utilities.js' export type Deployment = { deploymentName: string @@ -71,7 +71,7 @@ export const printLogs = (rawLogs: GetLogsReturnType, deployments: Deployment[]) console.log(`${ padding }${ head }(`) for (const [paramName, paramValue] of Object.entries(log.args)) { let formattedValue = paramValue - if (isUnknownAnAddress(paramValue)) { + if (isUnknownAddress(paramValue)) { const matchingDeployment = deployments.find((deploymentItem) => deploymentItem.address.toLowerCase() === paramValue.toLowerCase()) if (matchingDeployment) { formattedValue = `${ matchingDeployment.deploymentName } (${ paramValue })` diff --git a/solidity/ts/testsuite/simulator/utils/testUtils.ts b/solidity/ts/testsuite/simulator/utils/testUtils.ts index 903e232..6843b1e 100644 --- a/solidity/ts/testsuite/simulator/utils/testUtils.ts +++ b/solidity/ts/testsuite/simulator/utils/testUtils.ts @@ -6,6 +6,7 @@ export const strictEqualTypeSafe = (actual: Type, expected: Type, errorMes export const strictEqual18Decimal = (actual: bigint, expected: bigint, errorMessage?: string | Error | undefined) => assert.strictEqual(bigintToDecimalString(actual, 18n), bigintToDecimalString(expected, 18n), errorMessage) export const approximatelyEqual = (actual: bigint, expected: bigint, errorDelta: bigint, errorMessage?: string | undefined) => { + if (errorDelta < 0n) throw new RangeError('errorDelta must be non-negative') const diff = abs(actual - expected) if (diff > errorDelta) { throw new assert.AssertionError({ diff --git a/solidity/ts/testsuite/simulator/utils/utilities.ts b/solidity/ts/testsuite/simulator/utils/utilities.ts index cf9f1f4..7b97738 100644 --- a/solidity/ts/testsuite/simulator/utils/utilities.ts +++ b/solidity/ts/testsuite/simulator/utils/utilities.ts @@ -224,7 +224,7 @@ export async function ensureProxyDeployerDeployed(client: WriteClient): Promise< export const contractExists = async (client: ReadClient, contract: `0x${ string }`) => await client.getCode({ address: contract }) !== undefined -export const isUnknownAddress= (maybeAddress: unknown): maybeAddress is `0x${ string }` => typeof maybeAddress === 'string' && /^0x[a-fA-F0-9]{40}$/.test(maybeAddress) +export const isUnknownAddress = (maybeAddress: unknown): maybeAddress is `0x${ string }` => typeof maybeAddress === 'string' && /^0x[a-fA-F0-9]{40}$/.test(maybeAddress) const uint248BitMask = (1n << 248n) - 1n export function getChildUniverseId(parentUniverseId: bigint, outcome: bigint | QuestionOutcome): bigint { From 83efe3a27293c9bfbec9c6851c14f2aa2607c10a Mon Sep 17 00:00:00 2001 From: KillariDev Date: Fri, 13 Feb 2026 16:47:45 +0200 Subject: [PATCH 5/8] fix some comments --- .../PriceOracleManagerAndOperatorQueuer.sol | 9 +++++---- .../peripherals/interfaces/IShareToken.sol | 2 +- .../contracts/peripherals/tokens/ShareToken.sol | 3 +++ solidity/ts/tests/testPeripherals.ts | 2 +- .../testsuite/simulator/EthereumClientService.ts | 2 +- .../SimulationModeEthereumClientService.ts | 2 +- .../simulator/utils/contracts/peripherals.ts | 2 +- .../utils/contracts/peripheralsTestUtils.ts | 14 +++++++------- .../simulator/utils/contracts/securityPool.ts | 5 ++--- .../simulator/utils/peripheralsTestUtils.ts | 8 ++++---- 10 files changed, 26 insertions(+), 23 deletions(-) diff --git a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol index 3d4c6db..d49fa84 100644 --- a/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol +++ b/solidity/contracts/peripherals/PriceOracleManagerAndOperatorQueuer.sol @@ -133,9 +133,11 @@ contract PriceOracleManagerAndOperatorQueuer { function executeQueuedOperation(uint256 operationId) public { require(queuedOperations[operationId].amount > 0, 'no such operation or already executed'); require(isPriceValid(), 'price is not valid to execute'); + uint256 amount = queuedOperations[operationId].amount; + queuedOperations[operationId].amount = 0; // todo, we should allow these operations here to fail, but solidity try catch doesnt work inside the same contract if (queuedOperations[operationId].operation == OperationType.Liquidation) { - try securityPool.performLiquidation(queuedOperations[operationId].initiatorVault, queuedOperations[operationId].targetVault, queuedOperations[operationId].amount) { + try securityPool.performLiquidation(queuedOperations[operationId].initiatorVault, queuedOperations[operationId].targetVault, amount) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); } catch Error(string memory reason) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); @@ -143,7 +145,7 @@ contract PriceOracleManagerAndOperatorQueuer { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, 'Unknown error'); } } else if(queuedOperations[operationId].operation == OperationType.WithdrawRep) { - try securityPool.performWithdrawRep(queuedOperations[operationId].initiatorVault, queuedOperations[operationId].amount) { + try securityPool.performWithdrawRep(queuedOperations[operationId].initiatorVault, amount) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); } catch Error(string memory reason) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); @@ -151,7 +153,7 @@ contract PriceOracleManagerAndOperatorQueuer { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, 'Unknown error'); } } else { - try securityPool.performSetSecurityBondsAllowance(queuedOperations[operationId].initiatorVault, queuedOperations[operationId].amount) { + try securityPool.performSetSecurityBondsAllowance(queuedOperations[operationId].initiatorVault, amount) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, true, ''); } catch Error(string memory reason) { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, reason); @@ -159,7 +161,6 @@ contract PriceOracleManagerAndOperatorQueuer { emit ExecutedQueuedOperation(operationId, queuedOperations[operationId].operation, false, 'Unknown error'); } } - queuedOperations[operationId].amount = 0; } function getQueuedOperation() public view returns (QueuedOperation memory) { diff --git a/solidity/contracts/peripherals/interfaces/IShareToken.sol b/solidity/contracts/peripherals/interfaces/IShareToken.sol index c595a27..bcef89f 100644 --- a/solidity/contracts/peripherals/interfaces/IShareToken.sol +++ b/solidity/contracts/peripherals/interfaces/IShareToken.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.33; import '../../Zoltar.sol'; import '../interfaces/ISecurityPool.sol'; import '../tokens/TokenId.sol'; -import '../../YesNoMarkets.sol'; +import '../YesNoMarkets.sol'; interface IShareToken { function authorize(ISecurityPool _securityPoolCandidate) external; diff --git a/solidity/contracts/peripherals/tokens/ShareToken.sol b/solidity/contracts/peripherals/tokens/ShareToken.sol index 0946574..f086310 100644 --- a/solidity/contracts/peripherals/tokens/ShareToken.sol +++ b/solidity/contracts/peripherals/tokens/ShareToken.sol @@ -7,6 +7,7 @@ import './TokenId.sol'; import '../../Zoltar.sol'; import '../interfaces/ISecurityPool.sol'; import '../interfaces/IShareToken.sol'; +import '../YesNoMarkets.sol'; /** * @title Share Token @@ -19,6 +20,7 @@ contract ShareToken is ForkedERC1155, IShareToken { string constant public symbol = 'SHARE'; Zoltar public immutable zoltar; mapping(address => bool) authorized; + event Authorized(address indexed securityPool); function universeHasForked(uint248 universeId) internal override view returns (bool) { return zoltar.getForkTime(universeId) > 0; @@ -32,6 +34,7 @@ contract ShareToken is ForkedERC1155, IShareToken { function authorize(ISecurityPool _securityPoolCandidate) external { require(authorized[msg.sender], 'not authorized'); authorized[address(_securityPoolCandidate)] = true; + emit Authorized(address(_securityPoolCandidate)); } function getUniverseId(uint256 id) internal override pure returns (uint248 universeId) { diff --git a/solidity/ts/tests/testPeripherals.ts b/solidity/ts/tests/testPeripherals.ts index 7a29e4f..9fe377c 100644 --- a/solidity/ts/tests/testPeripherals.ts +++ b/solidity/ts/tests/testPeripherals.ts @@ -349,7 +349,7 @@ describe('Peripherals Contract Test Suite', () => { // yes status: auction fully funds, 1/4 of rep balance is sold for eth await finalizeTruthAuction(client, yesSecurityPool.securityPool) - assert.deepStrictEqual(await balanceOfSharesInCash(client, securityPoolAddresses.securityPool, securityPoolAddresses.shareToken, genesisUniverse, addressString(TEST_ADDRESSES[2])), openInterestArray.map((x) => x - feesOved), 'Shares exist after fork') + assert.deepStrictEqual(await balanceOfSharesInCash(client, securityPoolAddresses.securityPool, securityPoolAddresses.shareToken, genesisUniverse, addressString(TEST_ADDRESSES[2])), openInterestArray.map((x) => x - feesOwed), 'Shares exist after fork') await migrateShares(openInterestHolder, securityPoolAddresses.shareToken, genesisUniverse, QuestionOutcome.Yes, [0n, 1n, 2n]) await migrateShares(openInterestHolder, securityPoolAddresses.shareToken, genesisUniverse, QuestionOutcome.No, [0n, 1n, 2n]) await migrateShares(openInterestHolder, securityPoolAddresses.shareToken, genesisUniverse, QuestionOutcome.Invalid, [0n, 1n, 2n]) diff --git a/solidity/ts/testsuite/simulator/EthereumClientService.ts b/solidity/ts/testsuite/simulator/EthereumClientService.ts index d65d1cd..5c9596e 100644 --- a/solidity/ts/testsuite/simulator/EthereumClientService.ts +++ b/solidity/ts/testsuite/simulator/EthereumClientService.ts @@ -163,7 +163,7 @@ export class EthereumClientService { return EthereumQuantity.parse(response) } - public readonly getGasPrice = async(requestAbortController: AbortController | undefined) => { + public readonly getGasPrice = async (requestAbortController: AbortController | undefined) => { const response = await this.requestHandler.jsonRpcRequest({ method: 'eth_gasPrice' }, requestAbortController) return EthereumQuantity.parse(response) } diff --git a/solidity/ts/testsuite/simulator/SimulationModeEthereumClientService.ts b/solidity/ts/testsuite/simulator/SimulationModeEthereumClientService.ts index 8b55c3d..e2f04ae 100644 --- a/solidity/ts/testsuite/simulator/SimulationModeEthereumClientService.ts +++ b/solidity/ts/testsuite/simulator/SimulationModeEthereumClientService.ts @@ -188,7 +188,7 @@ export const appendTransaction = async (ethereumClientService: EthereumClientSer return await createSimulationState(ethereumClientService, requestAbortController, simulationStateInput) } -export const getNonceFixedSimulatedTransactions = async(ethereumClientService: EthereumClientService, requestAbortController: AbortController | undefined, simulatedTransactions: readonly SimulatedTransaction[]) => { +export const getNonceFixedSimulatedTransactions = async (ethereumClientService: EthereumClientService, requestAbortController: AbortController | undefined, simulatedTransactions: readonly SimulatedTransaction[]) => { const isFixableNonceError = (transaction: SimulatedTransaction) => { return transaction.ethSimulateV1CallResult.status === 'failure' && transaction.ethSimulateV1CallResult.error.message === 'wrong transaction nonce' //TODO, change to error code diff --git a/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts b/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts index 81f247f..fcbfae1 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts @@ -267,7 +267,7 @@ export const migrateShares = async (client: WriteClient, shareTokenAddress: `0x$ }) } -export const getMarketEndDate = async(client: ReadClient, marketId: bigint) => { +export const getMarketEndDate = async (client: ReadClient, marketId: bigint) => { return await client.readContract({ abi: peripherals_YesNoMarkets_YesNoMarkets.abi, functionName: 'getMarketEndDate', diff --git a/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts b/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts index 821e6cb..bab8b29 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/peripheralsTestUtils.ts @@ -32,18 +32,18 @@ export const approveAndDepositRep = async (client: WriteClient, repDeposit: bigi assert.strictEqual(newBalance, startBalance + repDeposit, 'Did not deposit rep') } -export const triggerOwnGameFork = async(client: WriteClient, securityPoolAddress: `0x${ string }`) => { +export const triggerOwnGameFork = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { const repToken = await getRepToken(client, securityPoolAddress) const forkThreshold = (await getTotalTheoreticalSupply(client, repToken)) / 20n /2n const vault = await getSecurityVault(client, securityPoolAddress, client.account.address) const repAmount = await poolOwnershipToRep(client, securityPoolAddress, vault.repDepositShare) - assert.ok(repAmount >= 2n * forkThreshold , 'not enough rep in vault to fork') - await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.Yes, forkThreshold ) - await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.No, forkThreshold ) + assert.ok(repAmount >= 2n * forkThreshold, 'not enough rep in vault to fork') + await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.Yes, forkThreshold) + await depositToEscalationGame(client, securityPoolAddress, QuestionOutcome.No, forkThreshold) await forkZoltarWithOwnEscalationGame(client, securityPoolAddress) } -export const handleOracleReporting = async(client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint) => { +export const handleOracleReporting = async (client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint) => { const pendingReportId = await getPendingReportId(client, priceOracleManagerAndOperatorQueuer) if (pendingReportId === 0n) { // operation already executed @@ -72,12 +72,12 @@ export const handleOracleReporting = async(client: WriteClient, mockWindow: Mock await openOracleSettle(client, pendingReportId) } -export const manipulatePriceOracleAndPerformOperation = async(client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, operation: OperationType, targetVault: `0x${ string }`, amount: bigint, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { +export const manipulatePriceOracleAndPerformOperation = async (client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, operation: OperationType, targetVault: `0x${ string }`, amount: bigint, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { await requestPriceIfNeededAndQueueOperation(client, priceOracleManagerAndOperatorQueuer, operation, targetVault, amount) await handleOracleReporting(client, mockWindow, priceOracleManagerAndOperatorQueuer, forceRepEthPriceTo) } -export const manipulatePriceOracle = async(client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { +export const manipulatePriceOracle = async (client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { await requestPrice(client, priceOracleManagerAndOperatorQueuer) await handleOracleReporting(client, mockWindow, priceOracleManagerAndOperatorQueuer, forceRepEthPriceTo) } diff --git a/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts b/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts index f5844cd..2080dbc 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/securityPool.ts @@ -86,13 +86,12 @@ export const getCurrentRetentionRate = async (client: ReadClient, securityPoolAd } export const getSecurityVault = async (client: ReadClient, securityPoolAddress: `0x${ string }`, securityVault: `0x${ string }`) => { - const vault = await client.readContract({ + const [repDepositShare, securityBondAllowance, unpaidEthFees, feeIndex] = await client.readContract({ abi: peripherals_SecurityPool_SecurityPool.abi, functionName: 'securityVaults', address: securityPoolAddress, args: [securityVault], }) - const [repDepositShare, securityBondAllowance, unpaidEthFees, feeIndex ] = vault return { repDepositShare, securityBondAllowance, unpaidEthFees, feeIndex } } @@ -209,7 +208,7 @@ export const redeemRep = async (client: WriteClient, securityPoolAddress: `0x${ }) } -export const getRepToken = async(client: ReadClient, securityPoolAddress: `0x${ string }`) => { +export const getRepToken = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { return await client.readContract({ abi: peripherals_SecurityPool_SecurityPool.abi, functionName: 'repToken', diff --git a/solidity/ts/testsuite/simulator/utils/peripheralsTestUtils.ts b/solidity/ts/testsuite/simulator/utils/peripheralsTestUtils.ts index 09dce00..a2c58f3 100644 --- a/solidity/ts/testsuite/simulator/utils/peripheralsTestUtils.ts +++ b/solidity/ts/testsuite/simulator/utils/peripheralsTestUtils.ts @@ -46,7 +46,7 @@ export const approveAndDepositRep = async (client: WriteClient, repDeposit: bigi assert.strictEqual(newBalance, startBalance + repDeposit, 'Did not deposit rep') } -export const triggerFork = async(client: WriteClient, mockWindow: MockWindowEthereum, questionId: bigint) => { +export const triggerFork = async (client: WriteClient, mockWindow: MockWindowEthereum, questionId: bigint) => { await ensureZoltarDeployed(client) await mockWindow.advanceTime(DAY) const initialOutcome = QuestionOutcome.Yes @@ -63,7 +63,7 @@ export const triggerFork = async(client: WriteClient, mockWindow: MockWindowEthe } } -export const handleOracleReporting = async(client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint) => { +export const handleOracleReporting = async (client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint) => { const pendingReportId = await getPendingReportId(client, priceOracleManagerAndOperatorQueuer) if (pendingReportId === 0n) { // operation already executed @@ -92,12 +92,12 @@ export const handleOracleReporting = async(client: WriteClient, mockWindow: Mock await openOracleSettle(client, pendingReportId) } -export const manipulatePriceOracleAndPerformOperation = async(client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, operation: OperationType, targetVault: `0x${ string }`, amount: bigint, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { +export const manipulatePriceOracleAndPerformOperation = async (client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, operation: OperationType, targetVault: `0x${ string }`, amount: bigint, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { await requestPriceIfNeededAndQueueOperation(client, priceOracleManagerAndOperatorQueuer, operation, targetVault, amount) await handleOracleReporting(client, mockWindow, priceOracleManagerAndOperatorQueuer, forceRepEthPriceTo) } -export const manipulatePriceOracle = async(client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { +export const manipulatePriceOracle = async (client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { await requestPrice(client, priceOracleManagerAndOperatorQueuer) await handleOracleReporting(client, mockWindow, priceOracleManagerAndOperatorQueuer, forceRepEthPriceTo) } From b9aa870b998724406b9a1888c94a706588f697db Mon Sep 17 00:00:00 2001 From: KillariDev Date: Fri, 13 Feb 2026 16:49:14 +0200 Subject: [PATCH 6/8] no need these --- .../simulator/utils/contracts/peripherals.ts | 6 +- .../simulator/utils/deployPeripherals.ts | 173 ------ .../testsuite/simulator/utils/deployments.ts | 120 ----- .../testsuite/simulator/utils/peripherals.ts | 505 ------------------ .../simulator/utils/peripheralsTestUtils.ts | 106 ---- 5 files changed, 3 insertions(+), 907 deletions(-) delete mode 100644 solidity/ts/testsuite/simulator/utils/deployPeripherals.ts delete mode 100644 solidity/ts/testsuite/simulator/utils/deployments.ts delete mode 100644 solidity/ts/testsuite/simulator/utils/peripherals.ts delete mode 100644 solidity/ts/testsuite/simulator/utils/peripheralsTestUtils.ts diff --git a/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts b/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts index fcbfae1..6f56125 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/peripherals.ts @@ -242,14 +242,14 @@ export const balanceOfShares = async (client: ReadClient, shareTokenAddress: `0x }) } -export const balanceOfSharesInCash = async (client: ReadClient, securityPoolAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { +export const balanceOfSharesInCash = async (client: ReadClient, seucurityPoolAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { const array = await client.readContract({ abi: peripherals_tokens_ShareToken_ShareToken.abi, functionName: 'balanceOfShares', address: shareTokenAddress, args: [universeId, account], }) - return await shareArrayToCash(client, securityPoolAddress, array) + return await shareArrayToCash(client, seucurityPoolAddress, array) } export const getTokenId = (universeId: bigint, outcome: QuestionOutcome) => { @@ -267,7 +267,7 @@ export const migrateShares = async (client: WriteClient, shareTokenAddress: `0x$ }) } -export const getMarketEndDate = async (client: ReadClient, marketId: bigint) => { +export const getMarketEndDate = async(client: ReadClient, marketId: bigint) => { return await client.readContract({ abi: peripherals_YesNoMarkets_YesNoMarkets.abi, functionName: 'getMarketEndDate', diff --git a/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts b/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts deleted file mode 100644 index 5472f95..0000000 --- a/solidity/ts/testsuite/simulator/utils/deployPeripherals.ts +++ /dev/null @@ -1,173 +0,0 @@ -import 'viem/window' -import { encodeDeployData, getCreate2Address, keccak256, numberToBytes, toHex, zeroAddress, encodeAbiParameters } from 'viem' -import { WriteClient } from './viem.js' -import { PROXY_DEPLOYER_ADDRESS } from './constants.js' -import { addressString } from './bigint.js' -import { contractExists, getRepTokenAddress, getZoltarAddress } from './utilities.js' -import { mainnet } from 'viem/chains' -import { peripherals_Auction_Auction, peripherals_factories_AuctionFactory_AuctionFactory, peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory, peripherals_factories_SecurityPoolFactory_SecurityPoolFactory, peripherals_factories_ShareTokenFactory_ShareTokenFactory, peripherals_openOracle_OpenOracle_OpenOracle, peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer, peripherals_SecurityPool_SecurityPool, peripherals_SecurityPoolUtils_SecurityPoolUtils, peripherals_tokens_ShareToken_ShareToken, Zoltar_Zoltar } from '../../../types/contractArtifact.js' - -export function getSecurityPoolUtilsAddress() { - return getCreate2Address({ bytecode: `0x${ peripherals_SecurityPoolUtils_SecurityPoolUtils.evm.bytecode.object }`, from: addressString(PROXY_DEPLOYER_ADDRESS), salt: numberToBytes(0) }) -} - -export const applyLibraries = (bytecode: string): `0x${ string }` => { - const securityPoolUtils = keccak256(toHex('contracts/peripherals/SecurityPoolUtils.sol:SecurityPoolUtils')).slice(2, 36) - return `0x${ bytecode.replaceAll(`__$${ securityPoolUtils }$__`, getSecurityPoolUtilsAddress().slice(2).toLocaleLowerCase()) }` -} - -export const getSecurityPoolFactoryByteCode = (openOracle: `0x${ string }`, zoltar: `0x${ string }`, shareTokenFactory: `0x${ string }`, auctionFactory: `0x${ string }`, priceOracleManagerAndOperatorQueuerFactory: `0x${ string }`) => { - return encodeDeployData({ - abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi, - bytecode: applyLibraries(peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.evm.bytecode.object), - args: [ openOracle, zoltar, shareTokenFactory, auctionFactory, priceOracleManagerAndOperatorQueuerFactory ] - }) -} - -export const getSecurityPoolFactoryAddress = (openOracle: `0x${ string }`, zoltar: `0x${ string }`, shareTokenFactory: `0x${ string }`, auctionFactory: `0x${ string }`, priceOracleManagerAndOperatorQueuerFactory: `0x${ string }`) => { - return getCreate2Address({ - from: addressString(PROXY_DEPLOYER_ADDRESS), - salt: numberToBytes(0), - bytecode: getSecurityPoolFactoryByteCode(openOracle, zoltar, shareTokenFactory, auctionFactory, priceOracleManagerAndOperatorQueuerFactory) - }) -} - -export const getShareTokenFactoryByteCode = (zoltar: `0x${ string }`) => { - return encodeDeployData({ - abi: peripherals_factories_ShareTokenFactory_ShareTokenFactory.abi, - bytecode: `0x${ peripherals_factories_ShareTokenFactory_ShareTokenFactory.evm.bytecode.object }`, - args: [ zoltar ] - }) -} - -export function getInfraContractAddresses() { - const contracts = { - securityPoolUtils: getCreate2Address({ bytecode: `0x${ peripherals_SecurityPoolUtils_SecurityPoolUtils.evm.bytecode.object }`, from: addressString(PROXY_DEPLOYER_ADDRESS), salt: numberToBytes(0) }), - openOracle: getCreate2Address({ bytecode: `0x${ peripherals_openOracle_OpenOracle_OpenOracle.evm.bytecode.object }`, from: addressString(PROXY_DEPLOYER_ADDRESS), salt: numberToBytes(0) }), - zoltar: getZoltarAddress(), - shareTokenFactory: getCreate2Address({ bytecode: getShareTokenFactoryByteCode(getZoltarAddress()), from: addressString(PROXY_DEPLOYER_ADDRESS), salt: numberToBytes(0) }), - auctionFactory: getCreate2Address({ bytecode: `0x${ peripherals_factories_AuctionFactory_AuctionFactory.evm.bytecode.object }`, from: addressString(PROXY_DEPLOYER_ADDRESS), salt: numberToBytes(0) }), - priceOracleManagerAndOperatorQueuerFactory: getCreate2Address({ bytecode: `0x${ peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory.evm.bytecode.object }`, from: addressString(PROXY_DEPLOYER_ADDRESS), salt: numberToBytes(0) }), - } - const securityPoolFactory = getSecurityPoolFactoryAddress(contracts.openOracle, contracts.zoltar, contracts.shareTokenFactory, contracts.auctionFactory, contracts.priceOracleManagerAndOperatorQueuerFactory) - return { ...contracts, securityPoolFactory } -} - -export async function getInfraDeployedInformation(client: WriteClient): Promise<{ [key in keyof ReturnType]: boolean }> { - const contractAddresses = getInfraContractAddresses() - type ContractKeys = keyof typeof contractAddresses - - const contractKeys = Object.keys(contractAddresses) as ContractKeys[] - - const contractExistencePairs = await Promise.all( - contractKeys.map(async key => { - const doesExist = await contractExists(client, contractAddresses[key]) - return [key, doesExist] as const - }) - ) - - const contractExistenceObject: { [key in ContractKeys]: boolean } = {} as { [key in ContractKeys]: boolean } - contractExistencePairs.forEach(([key, doesExist]) => { - contractExistenceObject[key] = doesExist - }) - - return contractExistenceObject -} -export async function ensureInfraDeployed(client: WriteClient): Promise { - const contractAddresses = getInfraContractAddresses() - const existence = await getInfraDeployedInformation(client) - if (!existence.securityPoolUtils) { - await client.sendTransaction({ to: addressString(PROXY_DEPLOYER_ADDRESS), data: `0x${ peripherals_SecurityPoolUtils_SecurityPoolUtils.evm.bytecode.object }` } as const) - if (!(await contractExists(client, contractAddresses.securityPoolUtils))) throw new Error('Security Pool Utils does not exist eventhought we deployed it') - } - if (!existence.openOracle) { - await client.sendTransaction({ to: addressString(PROXY_DEPLOYER_ADDRESS), data: `0x${ peripherals_openOracle_OpenOracle_OpenOracle.evm.bytecode.object }` } as const) - if (!(await contractExists(client, contractAddresses.openOracle))) throw new Error('Open Oracle does not exist eventhought we deployed it') - } - if (!existence.zoltar) { - await client.sendTransaction({ to: addressString(PROXY_DEPLOYER_ADDRESS), data: `0x${ Zoltar_Zoltar.evm.bytecode.object }` } as const) - if (!(await contractExists(client, contractAddresses.zoltar))) throw new Error('Zoltar does not exist eventhought we deployed it') - } - if (!existence.shareTokenFactory) { - await client.sendTransaction({ to: addressString(PROXY_DEPLOYER_ADDRESS), data: getShareTokenFactoryByteCode(getZoltarAddress()) } as const) - if (!(await contractExists(client, contractAddresses.shareTokenFactory))) throw new Error('Share Token Factory does not exist eventhought we deployed it') - } - if (!existence.auctionFactory) { - await client.sendTransaction({ to: addressString(PROXY_DEPLOYER_ADDRESS), data: `0x${ peripherals_factories_AuctionFactory_AuctionFactory.evm.bytecode.object }` } as const) - if (!(await contractExists(client, contractAddresses.auctionFactory))) throw new Error('auctionFactory does not exist eventhought we deployed it') - } - if (!existence.priceOracleManagerAndOperatorQueuerFactory) { - await client.sendTransaction({ to: addressString(PROXY_DEPLOYER_ADDRESS), data: `0x${ peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory.evm.bytecode.object }` } as const) - if (!(await contractExists(client, contractAddresses.priceOracleManagerAndOperatorQueuerFactory))) throw new Error('priceOracleManagerAndOperatorQueuerFactory does not exist eventhought we deployed it') - } - if (!existence.securityPoolFactory) { - await client.sendTransaction({ to: addressString(PROXY_DEPLOYER_ADDRESS), data: getSecurityPoolFactoryByteCode(contractAddresses.openOracle, contractAddresses.zoltar, contractAddresses.shareTokenFactory, contractAddresses.auctionFactory, contractAddresses.priceOracleManagerAndOperatorQueuerFactory) } as const) - if (!(await contractExists(client, contractAddresses.securityPoolFactory))) throw new Error('priceOracleManagerAndOperatorQueuerFactory does not exist eventhought we deployed it') - } -} - -const computeSecurityPoolSalt = (parent: `0x${ string }`, universeId: bigint, questionId: bigint, securityMultiplier: bigint) => { - const values = [parent, universeId, questionId, securityMultiplier] as const - return keccak256(encodeAbiParameters([ - { name: 'parent', type: 'address' }, - { name: 'universeId', type: 'uint192' }, - { name: 'questionId', type: 'uint56' }, - { name: 'securityMultiplier', type: 'uint256' }, - ], values)) -} - -const computeShareTokenSalt = (securityMultiplier: bigint) => { - return keccak256(encodeAbiParameters([{ name: 'securityMultiplier', type: 'uint256' }], [securityMultiplier])) -} - -export const getSecurityPoolAddresses = (parent: `0x${ string }`, universeId: bigint, questionId: bigint, securityMultiplier: bigint) => { - const securityPoolSalt = computeSecurityPoolSalt(parent, universeId, questionId, securityMultiplier) - const infraContracts = getInfraContractAddresses() - const securityPoolSaltWithMsgSender = keccak256(encodeAbiParameters([{ name: 'securityPoolFactory', type:'address' }, { name: 'securityPoolSalt', type: 'bytes32' }] as const, [infraContracts.securityPoolFactory, securityPoolSalt])) - - const contracts = { - priceOracleManagerAndOperatorQueuer: getCreate2Address({ - bytecode: encodeDeployData({ - abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, - bytecode: `0x${ peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.evm.bytecode.object }`, - args: [ infraContracts.openOracle, getRepTokenAddress(universeId) ] - }), - from: infraContracts.priceOracleManagerAndOperatorQueuerFactory, - salt: securityPoolSaltWithMsgSender - }), - shareToken: getCreate2Address({ - bytecode: encodeDeployData({ - abi: peripherals_tokens_ShareToken_ShareToken.abi, - bytecode: `0x${ peripherals_tokens_ShareToken_ShareToken.evm.bytecode.object }`, - args: [ infraContracts.securityPoolFactory, infraContracts.zoltar, questionId ] - }), - from: infraContracts.shareTokenFactory, - salt: computeShareTokenSalt(securityMultiplier) - }), - truthAuction: BigInt(parent) == 0n ? zeroAddress : getCreate2Address({ - bytecode: `0x${ peripherals_Auction_Auction.evm.bytecode.object }`, - from: infraContracts.auctionFactory, - salt: securityPoolSaltWithMsgSender - }), - } - const securityPool = getCreate2Address({ - bytecode: encodeDeployData({ - abi: peripherals_SecurityPool_SecurityPool.abi, - bytecode: applyLibraries(peripherals_SecurityPool_SecurityPool.evm.bytecode.object), - args: [ infraContracts.securityPoolFactory, contracts.truthAuction, contracts.priceOracleManagerAndOperatorQueuer, contracts.shareToken, infraContracts.openOracle, parent, infraContracts.zoltar, universeId, questionId, securityMultiplier] as const - }), - from: infraContracts.securityPoolFactory, - salt: numberToBytes(0) - }) - return { ...contracts, securityPool } -} - -export const deployOriginSecurityPool = async (client: WriteClient, universeId: bigint, questionId: bigint, securityMultiplier: bigint, startingRetentionRate: bigint, startingRepEthPrice: bigint, completeSetCollateralAmount: bigint) => { - const infraAddresses = getInfraContractAddresses() - return await client.writeContract({ - abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi, - functionName: 'deployOriginSecurityPool', - address: infraAddresses.securityPoolFactory, - args: [universeId, questionId, securityMultiplier, startingRetentionRate, startingRepEthPrice, completeSetCollateralAmount] - }) -} diff --git a/solidity/ts/testsuite/simulator/utils/deployments.ts b/solidity/ts/testsuite/simulator/utils/deployments.ts deleted file mode 100644 index 468a9e7..0000000 --- a/solidity/ts/testsuite/simulator/utils/deployments.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { peripherals_interfaces_IAugur_IAugur, IERC20_IERC20, peripherals_interfaces_IWeth9_IWeth9, peripherals_Auction_Auction, peripherals_openOracle_OpenOracle_OpenOracle, peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer, peripherals_SecurityPool_SecurityPool, peripherals_factories_AuctionFactory_AuctionFactory, ReputationToken_ReputationToken, Zoltar_Zoltar, peripherals_SecurityPoolUtils_SecurityPoolUtils, peripherals_tokens_ShareToken_ShareToken, peripherals_factories_SecurityPoolFactory_SecurityPoolFactory, peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory, peripherals_factories_ShareTokenFactory_ShareTokenFactory } from '../../../types/contractArtifact.js' -import { QuestionOutcome } from '../types/types.js' -import { addressString } from './bigint.js' -import { ETHEREUM_LOGS_LOGGER_ADDRESS, TEST_ADDRESSES, WETH_ADDRESS } from './constants.js' -import { Deployment } from './logExplaining.js' -import { getInfraContractAddresses, getSecurityPoolAddresses } from './deployPeripherals.js' -import { getChildUniverseId, getRepTokenAddress } from './utilities.js' -import { zeroAddress } from 'viem' - -const getUniverseName = (universeId: bigint): string => { - const path: string[] = [] - let currentUniverseId = universeId - while (currentUniverseId > 0n) { - const branchIndex = Number(currentUniverseId & 0b11n) - 1 - const outcomeName = QuestionOutcome[branchIndex] as keyof typeof QuestionOutcome - path.push(outcomeName) - currentUniverseId = currentUniverseId >> 2n - } - if (path.length === 0) return 'U-Genesis' - return `U-Genesis-${ path.join('-') }` -} - -const getDeploymentsForUniverse = (universeId: bigint, securityPoolAddress: `0x${ string }`, repTokenAddress: `0x${ string }`, priceOracleManagerAndOperatorQueuerAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, auction: `0x${ string }`): Deployment[] => [ - { - abi: ReputationToken_ReputationToken.abi, - deploymentName: `RepV2 ${ getUniverseName(universeId) }`, - address: repTokenAddress - }, { - abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, - deploymentName: `PriceOracleManagerAndOperatorQueuer ${ getUniverseName(universeId) }`, - address: priceOracleManagerAndOperatorQueuerAddress - }, { - abi: peripherals_SecurityPool_SecurityPool.abi, - deploymentName: `ETH SecurityPool ${ getUniverseName(universeId) }`, - address: securityPoolAddress - }, { - abi: peripherals_tokens_ShareToken_ShareToken.abi, - deploymentName: `ShareToken ${ getUniverseName(universeId) }`, - address: shareTokenAddress - }, { - abi: peripherals_Auction_Auction.abi, - deploymentName: `Truth Auction ${ getUniverseName(universeId) }`, - address: auction - } -] as const - -export const getDeployments = (genesisUniverse: bigint, questionId: bigint, securityMultiplier: bigint): Deployment[] => { - const infraAddresses = getInfraContractAddresses() - const originAddresses = getSecurityPoolAddresses(zeroAddress, genesisUniverse, questionId, securityMultiplier) - - const outcomes = [QuestionOutcome.Invalid, QuestionOutcome.No, QuestionOutcome.Yes] - - const getChildAddresses = (parentSecurityPoolAddress: `0x${ string }`, parentUniverseId: bigint): Deployment[] => { - return outcomes.flatMap((outcome) => { - const universeId = getChildUniverseId(parentUniverseId, outcome) - const childAddresses = getSecurityPoolAddresses(parentSecurityPoolAddress, universeId, questionId, securityMultiplier) - return getDeploymentsForUniverse(universeId, childAddresses.securityPool, getRepTokenAddress(universeId), childAddresses.priceOracleManagerAndOperatorQueuer, childAddresses.shareToken, childAddresses.truthAuction) - }) - } - - return ([ - ...getDeploymentsForUniverse(genesisUniverse, originAddresses.securityPool, getRepTokenAddress(genesisUniverse), originAddresses.priceOracleManagerAndOperatorQueuer, originAddresses.shareToken, originAddresses.truthAuction), - ...getChildAddresses(originAddresses.securityPool, genesisUniverse), // children - ...outcomes.flatMap((outcome) => getChildAddresses(getSecurityPoolAddresses(originAddresses.securityPool, genesisUniverse, questionId, securityMultiplier).securityPool, getChildUniverseId(genesisUniverse, outcome))), // grand children - { - abi: Zoltar_Zoltar.abi, - deploymentName: 'Zoltar', - address: infraAddresses.zoltar, - }, { - abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi, - deploymentName: 'Security Pool Factory', - address: infraAddresses.securityPoolFactory - }, { - abi: peripherals_factories_AuctionFactory_AuctionFactory.abi, - deploymentName: 'Auction Factory', - address: infraAddresses.auctionFactory - }, { - abi: peripherals_factories_PriceOracleManagerAndOperatorQueuerFactory_PriceOracleManagerAndOperatorQueuerFactory.abi, - deploymentName: 'Price Oracle Manager And Operator Queuer Factory', - address: infraAddresses.priceOracleManagerAndOperatorQueuerFactory - }, { - abi: peripherals_factories_ShareTokenFactory_ShareTokenFactory.abi, - deploymentName: 'Share Token Factory', - address: infraAddresses.shareTokenFactory - }, { - abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, - deploymentName: 'Open Oracle', - address: infraAddresses.openOracle - }, { - abi: peripherals_interfaces_IWeth9_IWeth9.abi, - deploymentName: 'WETH', - address: WETH_ADDRESS - }, { - abi: peripherals_interfaces_IAugur_IAugur.abi, - deploymentName: 'Augur', - address: '0x23916a8f5c3846e3100e5f587ff14f3098722f5d' - }, { - abi: IERC20_IERC20.abi, - deploymentName: 'ETH', - address: addressString(ETHEREUM_LOGS_LOGGER_ADDRESS) - }, { - abi: undefined, - deploymentName: 'Micah Deployer', - address: `0x7a0d94f55792c434d74a40883c6ed8545e406d12` - }, { - abi: peripherals_SecurityPoolUtils_SecurityPoolUtils.abi, - deploymentName: 'Security Pool Utils', - address: infraAddresses.securityPoolUtils - }, { - abi: undefined, - deploymentName: 'Augur V2 Genesis', - address: '0x49244BD018Ca9fd1f06ecC07B9E9De773246e5AA' - }, - ...TEST_ADDRESSES.map((testAddress, index) => ({ - abi: undefined, - deploymentName: `TEST_ADDRESSES[${ index }]`, - address: addressString(testAddress) - } as const)) - ] as const).filter((entry) => BigInt(entry.address) !== 0n) -} diff --git a/solidity/ts/testsuite/simulator/utils/peripherals.ts b/solidity/ts/testsuite/simulator/utils/peripherals.ts deleted file mode 100644 index 016fb66..0000000 --- a/solidity/ts/testsuite/simulator/utils/peripherals.ts +++ /dev/null @@ -1,505 +0,0 @@ -import 'viem/window' -import { ReadContractReturnType } from 'viem' -import { ReadClient, WriteClient } from './viem.js' -import { WETH_ADDRESS } from './constants.js' -import { SystemState } from '../types/peripheralTypes.js' -import { peripherals_Auction_Auction, peripherals_openOracle_OpenOracle_OpenOracle, peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer, peripherals_SecurityPool_SecurityPool, peripherals_tokens_ShareToken_ShareToken } from '../../../types/contractArtifact.js' -import { QuestionOutcome } from '../types/types.js' -import { getInfraContractAddresses } from './deployPeripherals.js' - -export const depositRep = async (client: WriteClient, securityPoolAddress: `0x${ string }`, amount: bigint) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'depositRep', - address: securityPoolAddress, - args: [amount] - }) -} - -export enum OperationType { - Liquidation = 0, - WithdrawRep = 1, - SetSecurityBondsAllowance = 2 -} - -export const requestPriceIfNeededAndQueueOperation = async (client: WriteClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`, operation: OperationType, targetVault: `0x${ string }`, amount: bigint) => { - const ethCost = await getRequestPriceEthCost(client, priceOracleManagerAndOperatorQueuer) * 2n; - return await client.writeContract({ - abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, - functionName: 'requestPriceIfNeededAndQueueOperation', - address: priceOracleManagerAndOperatorQueuer, - args: [operation, targetVault, amount], - value: ethCost, - }) -} - -export const requestPrice = async (client: WriteClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`) => { - const ethCost = await getRequestPriceEthCost(client, priceOracleManagerAndOperatorQueuer) * 2n; - return await client.writeContract({ - abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, - functionName: 'requestPrice', - address: priceOracleManagerAndOperatorQueuer, - args: [], - value: ethCost, - }) -} - -export const getPendingReportId = async (client: ReadClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, - functionName: 'pendingReportId', - address: priceOracleManagerAndOperatorQueuer, - args: [] - }) as bigint -} - -interface ExtraReportData { - stateHash: `0x${ string }` - callbackContract: `0x${ string }` - numReports: number - callbackGasLimit: number - callbackSelector: `0x${ string }` - protocolFeeRecipient: `0x${ string }` - trackDisputes: boolean - keepFee: boolean - feeToken: boolean -} - -export const getOpenOracleExtraData = async (client: ReadClient, extraDataId: bigint): Promise => { - const result = await client.readContract({ - abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, - functionName: 'extraData', - address: getInfraContractAddresses().openOracle, - args: [extraDataId] - }) as ReadContractReturnType - - const [ - stateHash, - callbackContract, - numReports, - callbackGasLimit, - callbackSelector, - protocolFeeRecipient, - trackDisputes, - keepFee, - feeToken - ] = result as [ - `0x${ string }`, - `0x${ string }`, - bigint, - bigint, - `0x${ string }`, - `0x${ string }`, - boolean, - boolean, - boolean - ] - - return { - stateHash, - callbackContract, - numReports: Number(numReports), - callbackGasLimit: Number(callbackGasLimit), - callbackSelector, - protocolFeeRecipient, - trackDisputes, - keepFee, - feeToken - } -} - -export const openOracleSubmitInitialReport = async (client: WriteClient, reportId: bigint, amount1: bigint, amount2: bigint, stateHash: `0x${ string }`) => { - return await client.writeContract({ - abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, - functionName: 'submitInitialReport', - address: getInfraContractAddresses().openOracle, - args: [reportId, amount1, amount2, stateHash] - }) -} - -export const openOracleSettle = async (client: WriteClient, reportId: bigint) => { - return await client.writeContract({ - abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, - functionName: 'settle', - address: getInfraContractAddresses().openOracle, - gas: 5_000_000n, //needed because of gas() opcode being used - args: [reportId] - }) -} - -export const getRequestPriceEthCost = async (client: ReadClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, - functionName: 'getRequestPriceEthCost', - address: priceOracleManagerAndOperatorQueuer, - args: [] - }) as bigint -} - -export const wrapWeth = async (client: WriteClient, amount: bigint) => { - const wethAbi = [{ - type: 'function', - name: 'deposit', - stateMutability: 'payable', - inputs: [], - outputs: [] - }] - return await client.writeContract({ - abi: wethAbi, - address: WETH_ADDRESS, - functionName: 'deposit', - value: amount - }) -} - -export interface ReportMeta { - exactToken1Report: bigint - escalationHalt: bigint - fee: bigint - settlerReward: bigint - token1: `0x${ string }` - settlementTime: number - token2: `0x${ string }` - timeType: boolean - feePercentage: number - protocolFee: number - multiplier: number - disputeDelay: number -} - -export const getOpenOracleReportMeta = async (client: ReadClient, reportId: bigint): Promise => { - const reportMetaData = await client.readContract({ - abi: peripherals_openOracle_OpenOracle_OpenOracle.abi, - functionName: 'reportMeta', - address: getInfraContractAddresses().openOracle, - args: [reportId] - }) - - const [ - exactToken1Report, - escalationHalt, - fee, - settlerReward, - token1, - settlementTime, - token2, - timeType, - feePercentage, - protocolFee, - multiplier, - disputeDelay - ] = reportMetaData - - return { - exactToken1Report, - escalationHalt, - fee, - settlerReward, - token1, - settlementTime, - token2, - timeType, - feePercentage, - protocolFee, - multiplier, - disputeDelay - } -} - -export const createCompleteSet = async (client: WriteClient, securityPoolAddress: `0x${ string }`, completeSetsToCreate: bigint) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'createCompleteSet', - address: securityPoolAddress, - args: [], - value: completeSetsToCreate, - }) -} - -export const redeemCompleteSet = async (client: WriteClient, securityPoolAddress: `0x${ string }`, completeSetsToRedeem: bigint) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'redeemCompleteSet', - address: securityPoolAddress, - args: [completeSetsToRedeem], - }) -} - -export const getSecurityBondAllowance = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'securityBondAllowance', - address: securityPoolAddress, - args: [] - }) as bigint -} - -export const getCompleteSetCollateralAmount = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'completeSetCollateralAmount', - address: securityPoolAddress, - args: [] - }) as bigint -} - -export const getLastPrice = async (client: ReadClient, priceOracleManagerAndOperatorQueuer: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_PriceOracleManagerAndOperatorQueuer_PriceOracleManagerAndOperatorQueuer.abi, - functionName: 'lastPrice', - address: priceOracleManagerAndOperatorQueuer, - args: [] - }) as bigint -} - -export const forkSecurityPool = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'forkSecurityPool', - address: securityPoolAddress, - args: [], - }) -} - -export const migrateVault = async (client: WriteClient, securityPoolAddress: `0x${ string }`, outcome: QuestionOutcome) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'migrateVault', - address: securityPoolAddress, - args: [Number(outcome)], - }) -} - -export const startTruthAuction = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'startTruthAuction', - address: securityPoolAddress, - args: [], - }) -} - -export const finalizeTruthAuction = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'finalizeTruthAuction', - address: securityPoolAddress, - args: [], - }) -} - -export const claimAuctionProceeds = async (client: WriteClient, securityPoolAddress: `0x${ string }`, vault: `0x${ string }`) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'claimAuctionProceeds', - address: securityPoolAddress, - args: [vault], - }) -} - -export const participateAuction = async (client: WriteClient, auctionAddress: `0x${ string }`, repToBuy: bigint, ethToInvest: bigint) => { - return await client.writeContract({ - abi: peripherals_Auction_Auction.abi, - functionName: 'participate', - address: auctionAddress, - args: [repToBuy], - value: ethToInvest - }) -} -export const getEthAmountToBuy = async (client: ReadClient, auctionAddress: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_Auction_Auction.abi, - functionName: 'ethAmountToBuy', - address: auctionAddress, - args: [], - }) -} - -export const getMigratedRep = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'migratedRep', - address: securityPoolAddress, - args: [], - }) -} - -export const getSystemState = async (client: ReadClient, securityPoolAddress: `0x${ string }`): Promise => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'systemState', - address: securityPoolAddress, - args: [], - }) -} - -export const getCurrentRetentionRate = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'currentRetentionRate', - address: securityPoolAddress, - args: [], - }) -} - -export const getSecurityVault = async (client: ReadClient, securityPoolAddress: `0x${ string }`, securityVault: `0x${ string }`) => { - const vault = await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'securityVaults', - address: securityPoolAddress, - args: [securityVault], - }) - const [repDepositShare, securityBondAllowance, unpaidEthFees, feeIndex ] = vault - return { repDepositShare, securityBondAllowance, unpaidEthFees, feeIndex } -} - -export const getPoolOwnershipDenominator = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'poolOwnershipDenominator', - address: securityPoolAddress, - args: [], - }) -} - -export const poolOwnershipToRep = async (client: ReadClient, securityPoolAddress: `0x${ string }`, poolOwnership: bigint) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'poolOwnershipToRep', - address: securityPoolAddress, - args: [poolOwnership], - }) -} - -export const repToPoolOwnership = async (client: ReadClient, securityPoolAddress: `0x${ string }`, repAmount: bigint) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'repToPoolOwnership', - address: securityPoolAddress, - args: [repAmount], - }) -} - -export const redeemShares = async (client: WriteClient, securityPoolAddress: `0x${ string }`) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'redeemShares', - address: securityPoolAddress, - args: [], - }) -} - -export const balanceOfOutcome = async (client: ReadClient, shareTokenAddress: `0x${ string }`, universeId: bigint, outcome: QuestionOutcome, account: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_tokens_ShareToken_ShareToken.abi, - functionName: 'balanceOfOutcome', - address: shareTokenAddress, - args: [universeId, outcome, account], - }) -} - -export const balanceOfShares = async (client: ReadClient, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_tokens_ShareToken_ShareToken.abi, - functionName: 'balanceOfShares', - address: shareTokenAddress, - args: [universeId, account], - }) -} -export const balanceOfSharesInCash = async (client: ReadClient, securityPoolAddress: `0x${ string }`, shareTokenAddress: `0x${ string }`, universeId: bigint, account: `0x${ string }`) => { - const array = await client.readContract({ - abi: peripherals_tokens_ShareToken_ShareToken.abi, - functionName: 'balanceOfShares', - address: shareTokenAddress, - args: [universeId, account], - }) - return await shareArrayToCash(client, securityPoolAddress, array) -} - -export const getTokenId = (universeId: bigint, outcome: QuestionOutcome) => (universeId << 8n) + BigInt(outcome) -export const unpackTokenId = (tokenId: bigint): { universe: bigint, outcome: QuestionOutcome } => ({ universe: tokenId >> 8n, outcome: Number(tokenId & 0xFFn) }) - -export const migrateShares = async (client: WriteClient, shareTokenAddress: `0x${ string }`, universeId: bigint, outcome: QuestionOutcome) => { - return await client.writeContract({ - abi: peripherals_tokens_ShareToken_ShareToken.abi, - functionName: 'migrate', - address: shareTokenAddress, - args: [getTokenId(universeId, outcome)], - }) -} - -export const createChildUniverse = async (client: WriteClient, securityPoolAddress: `0x${ string }`, outcome: QuestionOutcome) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'createChildUniverse', - address: securityPoolAddress, - args: [outcome], - }) -} - -export const getTotalFeesOwedToVaults = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'totalFeesOwedToVaults', - address: securityPoolAddress, - args: [], - }) -} - -export const sharesToCash = async (client: ReadClient, securityPoolAddress: `0x${ string }`, completeSetAmount: bigint) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'sharesToCash', - address: securityPoolAddress, - args: [completeSetAmount], - }) -} - -export const cashToShares = async (client: ReadClient, securityPoolAddress: `0x${ string }`, eth: bigint) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'cashToShares', - address: securityPoolAddress, - args: [eth], - }) -} - -export const getShareTokenSupply = async (client: ReadClient, securityPoolAddress: `0x${ string }`) => { - return await client.readContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'shareTokenSupply', - address: securityPoolAddress, - args: [], - }) -} - -export const shareArrayToCash = async (client: ReadClient, securityPoolAddress: `0x${ string }`, shares: readonly bigint[]) => { - return await Promise.all(shares.map((shares) => sharesToCash(client, securityPoolAddress, shares))) -} - -export const updateVaultFees = async (client: WriteClient, securityPoolAddress: `0x${ string }`, vault: `0x${ string }`) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'updateVaultFees', - address: securityPoolAddress, - args: [vault], - }) -} - -export const redeemFees = async (client: WriteClient, securityPoolAddress: `0x${ string }`, vault: `0x${ string }`) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'redeemFees', - address: securityPoolAddress, - args: [vault], - }) -} - -export const redeemRep = async (client: WriteClient, securityPoolAddress: `0x${ string }`, vault: `0x${ string }`) => { - return await client.writeContract({ - abi: peripherals_SecurityPool_SecurityPool.abi, - functionName: 'redeemRep', - address: securityPoolAddress, - args: [vault], - }) -} diff --git a/solidity/ts/testsuite/simulator/utils/peripheralsTestUtils.ts b/solidity/ts/testsuite/simulator/utils/peripheralsTestUtils.ts deleted file mode 100644 index a2c58f3..0000000 --- a/solidity/ts/testsuite/simulator/utils/peripheralsTestUtils.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { zeroAddress } from 'viem' -import { MockWindowEthereum } from '../MockWindowEthereum.js' -import { QuestionOutcome } from '../types/types.js' -import { addressString } from './bigint.js' -import { DAY, GENESIS_REPUTATION_TOKEN, WETH_ADDRESS } from './constants.js' -import { deployOriginSecurityPool, ensureInfraDeployed, getInfraContractAddresses, getSecurityPoolAddresses } from './deployPeripherals.js' -import { approveToken, contractExists, createQuestion, dispute, ensureZoltarDeployed, getERC20Balance, getQuestionData, getUniverseData, getZoltarAddress, isZoltarDeployed, reportOutcome } from './utilities.js' -import { WriteClient } from './viem.js' -import assert from 'node:assert' -import { depositRep, getOpenOracleExtraData, getOpenOracleReportMeta, getPendingReportId, openOracleSettle, openOracleSubmitInitialReport, OperationType, requestPrice, requestPriceIfNeededAndQueueOperation, wrapWeth } from './peripherals.js' - -export const genesisUniverse = 0n -export const questionId = 1n -export const securityMultiplier = 2n -export const startingRepEthPrice = 1n -export const completeSetCollateralAmount = 0n -export const PRICE_PRECISION = 10n ** 18n -export const MAX_RETENTION_RATE = 999_999_996_848_000_000n // ≈90% yearly - -export const deployZoltarAndCreateMarket = async (client: WriteClient, questionEndTime: bigint) => { - await ensureZoltarDeployed(client) - const isDeployed = await isZoltarDeployed(client) - assert.ok(isDeployed, `Zoltar Not Deployed!`) - const zoltar = getZoltarAddress() - await approveToken(client, addressString(GENESIS_REPUTATION_TOKEN), zoltar) - await createQuestion(client, genesisUniverse, questionEndTime, 'test') - return await getQuestionData(client, questionId) -} - -export const deployPeripherals = async (client: WriteClient) => { - await ensureInfraDeployed(client); - await deployOriginSecurityPool(client, genesisUniverse, questionId, securityMultiplier, MAX_RETENTION_RATE, startingRepEthPrice, completeSetCollateralAmount) - const securityPoolAddress = getSecurityPoolAddresses(zeroAddress, genesisUniverse, questionId, securityMultiplier).securityPool - assert.ok(await contractExists(client, securityPoolAddress), 'security pool not deployed') -} - -export const approveAndDepositRep = async (client: WriteClient, repDeposit: bigint) => { - const securityPoolAddress = getSecurityPoolAddresses(zeroAddress, genesisUniverse, questionId, securityMultiplier).securityPool - assert.ok(await contractExists(client, securityPoolAddress), 'security pool not deployed') - - const startBalance = await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddress) - await approveToken(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddress) - await depositRep(client, securityPoolAddress, repDeposit) - - const newBalance = await getERC20Balance(client, addressString(GENESIS_REPUTATION_TOKEN), securityPoolAddress) - assert.strictEqual(newBalance, startBalance + repDeposit, 'Did not deposit rep') -} - -export const triggerFork = async (client: WriteClient, mockWindow: MockWindowEthereum, questionId: bigint) => { - await ensureZoltarDeployed(client) - await mockWindow.advanceTime(DAY) - const initialOutcome = QuestionOutcome.Yes - await reportOutcome(client, genesisUniverse, questionId, initialOutcome) - const disputeOutcome = QuestionOutcome.No - await dispute(client, genesisUniverse, questionId, disputeOutcome) - const invalidUniverseId = 1n - const yesUniverseId = 2n - const noUniverseId = 3n - return { - invalidUniverseData: await getUniverseData(client, invalidUniverseId), - yesUniverseData: await getUniverseData(client, yesUniverseId), - noUniverseData: await getUniverseData(client, noUniverseId) - } -} - -export const handleOracleReporting = async (client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint) => { - const pendingReportId = await getPendingReportId(client, priceOracleManagerAndOperatorQueuer) - if (pendingReportId === 0n) { - // operation already executed - return - } - assert.ok(pendingReportId > 0, 'Operation is not queued') - - const reportMeta = await getOpenOracleReportMeta(client, pendingReportId) - - // initial report - const amount1 = reportMeta.exactToken1Report - const amount2 = amount1 * PRICE_PRECISION / forceRepEthPriceTo - - const openOracle = getInfraContractAddresses().openOracle - await approveToken(client, addressString(GENESIS_REPUTATION_TOKEN), openOracle) - await approveToken(client, WETH_ADDRESS, openOracle) - await wrapWeth(client, amount2) - const wethBalance = await getERC20Balance(client, WETH_ADDRESS, client.account.address) - assert.strictEqual(wethBalance, amount2, 'Did not wrap weth') - - const stateHash = (await getOpenOracleExtraData(client, pendingReportId)).stateHash - await openOracleSubmitInitialReport(client, pendingReportId, amount1, amount2, stateHash) - - await mockWindow.advanceTime(DAY) - - await openOracleSettle(client, pendingReportId) -} - -export const manipulatePriceOracleAndPerformOperation = async (client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, operation: OperationType, targetVault: `0x${ string }`, amount: bigint, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { - await requestPriceIfNeededAndQueueOperation(client, priceOracleManagerAndOperatorQueuer, operation, targetVault, amount) - await handleOracleReporting(client, mockWindow, priceOracleManagerAndOperatorQueuer, forceRepEthPriceTo) -} - -export const manipulatePriceOracle = async (client: WriteClient, mockWindow: MockWindowEthereum, priceOracleManagerAndOperatorQueuer: `0x${ string }`, forceRepEthPriceTo: bigint = PRICE_PRECISION) => { - await requestPrice(client, priceOracleManagerAndOperatorQueuer) - await handleOracleReporting(client, mockWindow, priceOracleManagerAndOperatorQueuer, forceRepEthPriceTo) -} - -export const canLiquidate = (lastPrice: bigint, securityBondAllowance: bigint, stakedRep: bigint, securityMultiplier: bigint) => securityBondAllowance * lastPrice * securityMultiplier > stakedRep * PRICE_PRECISION - From c3b8b9aa33791e06ae254c58b7e3b0be38e81595 Mon Sep 17 00:00:00 2001 From: KillariDev Date: Fri, 13 Feb 2026 16:52:06 +0200 Subject: [PATCH 7/8] fix typescript func --- solidity/ts/testsuite/simulator/utils/typescript.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solidity/ts/testsuite/simulator/utils/typescript.ts b/solidity/ts/testsuite/simulator/utils/typescript.ts index 779adc2..9070ef5 100644 --- a/solidity/ts/testsuite/simulator/utils/typescript.ts +++ b/solidity/ts/testsuite/simulator/utils/typescript.ts @@ -35,4 +35,4 @@ export function modifyObject(original: T, subObject: NoInfer(obj: T) => Object.entries(obj) as { [K in keyof T]-?: [K, T[K]] }[keyof T][] +export const objectEntries = (obj: T) => Object.entries(obj) as [string, T[keyof T & string]][] From fb612cddf977420e3e343eff0097c9025549c1bf Mon Sep 17 00:00:00 2001 From: KillariDev Date: Fri, 13 Feb 2026 17:07:11 +0200 Subject: [PATCH 8/8] typofix --- .../ts/testsuite/simulator/utils/contracts/deployPeripherals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts index 83197a6..b1b39d8 100644 --- a/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts +++ b/solidity/ts/testsuite/simulator/utils/contracts/deployPeripherals.ts @@ -118,7 +118,7 @@ export async function ensureInfraDeployed(client: WriteClient): Promise { if (!existence.securityPoolForker) await deployBytecode(getSecurityPoolForkerByteCode(contractAddresses.zoltar)) for (const [name, contractAddress] of objectEntries(contractAddresses)) { - if (!(await contractExists(client, contractAddress))) throw new Error(`${ name } does not exist even thought we deployed it`) + if (!(await contractExists(client, contractAddress))) throw new Error(`${ name } does not exist even though we deployed it`) } }