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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/mainnet-deployment-addresses.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{
"id": "deploymentStatusOracle",
"label": "Deployment Status Oracle",
"address": "0xBc9300067855B0d06D689B14027aa2608a0E92FD"
"address": "0xf53C5906F9489cA8c5C4f9De268729C8F5e27960"
},
{
"id": "multicall3",
Expand Down Expand Up @@ -53,27 +53,27 @@
{
"id": "shareTokenFactory",
"label": "ShareTokenFactory",
"address": "0x88AC0A56b3CDF78B943bc03f936Ead5544D6D544"
"address": "0x613E9D038B68f9646eaFe668a62b5c8DEF10a45D"
},
{
"id": "priceOracleManagerAndOperatorQueuerFactory",
"label": "Price Oracle Manager Factory",
"address": "0xBC8F9a64742c65c41cf674491b8cD4AE73f37d8d"
"address": "0x616622aBe02cB15Af9d4eD83B12F190A8C66e296"
},
{
"id": "securityPoolForker",
"label": "Security Pool Forker",
"address": "0x450347fAA56C766dfb9a458D7D497DC1cF6237ec"
"address": "0x7B9C22734196A3943562f3bbcE9f779dA99bF9d3"
},
{
"id": "escalationGameFactory",
"label": "Escalation Game Factory",
"address": "0x71E163e0c2aEF36109dA8248A4b596831a25F21D"
"address": "0x8048069571e690E86f8A98A6f635f6521847E371"
},
{
"id": "securityPoolFactory",
"label": "Security Pool Factory",
"address": "0x6c0Df8AeA6Df887C62EC9b9717b20431831ea593"
"address": "0xF84e04E61090930eeBE19604f770ac4C4A31337e"
}
]
}
12 changes: 6 additions & 6 deletions docs/mainnet-deployment-addresses.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ These values are derived from the frozen mainnet protocol config, current contra
| ID | Label | Expected Address |
| --- | --- | --- |
| proxyDeployer | Proxy Deployer | `0x7A0D94F55792C434d74a40883C6ed8545E406D12` |
| deploymentStatusOracle | Deployment Status Oracle | `0xBc9300067855B0d06D689B14027aa2608a0E92FD` |
| deploymentStatusOracle | Deployment Status Oracle | `0xf53C5906F9489cA8c5C4f9De268729C8F5e27960` |
| multicall3 | Multicall3 | `0x77609e84c39893D5fB99049FE0F461aEB4F4Ec79` |
| uniformPriceDualCapBatchAuctionFactory | UniformPriceDualCapBatchAuctionFactory | `0xbA605970da511b08539bcC4F77B54CF1Cd72783c` |
| scalarOutcomes | ScalarOutcomes | `0x5890b011CF7E36d4Fee28Bac8B5be6f61C392a4C` |
| securityPoolUtils | SecurityPoolUtils | `0x04349f6A0302F32f8c87bb8555648AD77634343C` |
| openOracle | OpenOracle | `0x51DED022c087758c187ce636aa5f6adE6B919abB` |
| zoltarQuestionData | ZoltarQuestionData | `0xeadF91d12F549786891350B3D535638713651207` |
| zoltar | Zoltar | `0x6b5003e3715D5Bc8C753ca9822A0c550801B1fca` |
| shareTokenFactory | ShareTokenFactory | `0x88AC0A56b3CDF78B943bc03f936Ead5544D6D544` |
| priceOracleManagerAndOperatorQueuerFactory | Price Oracle Manager Factory | `0xBC8F9a64742c65c41cf674491b8cD4AE73f37d8d` |
| securityPoolForker | Security Pool Forker | `0x450347fAA56C766dfb9a458D7D497DC1cF6237ec` |
| escalationGameFactory | Escalation Game Factory | `0x71E163e0c2aEF36109dA8248A4b596831a25F21D` |
| securityPoolFactory | Security Pool Factory | `0x6c0Df8AeA6Df887C62EC9b9717b20431831ea593` |
| shareTokenFactory | ShareTokenFactory | `0x613E9D038B68f9646eaFe668a62b5c8DEF10a45D` |
| priceOracleManagerAndOperatorQueuerFactory | Price Oracle Manager Factory | `0x616622aBe02cB15Af9d4eD83B12F190A8C66e296` |
| securityPoolForker | Security Pool Forker | `0x7B9C22734196A3943562f3bbcE9f779dA99bF9d3` |
| escalationGameFactory | Escalation Game Factory | `0x8048069571e690E86f8A98A6f635f6521847E371` |
| securityPoolFactory | Security Pool Factory | `0xF84e04E61090930eeBE19604f770ac4C4A31337e` |

Security pool deployments are deterministic per pool input rather than globally fixed. Their addresses are derived from the deployed factory set plus parent universe, universe ID, question ID, and security multiplier.
14 changes: 10 additions & 4 deletions solidity/contracts/peripherals/factories/SecurityPoolFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ReputationToken } from '../../ReputationToken.sol';
import { EscalationGameFactory } from './EscalationGameFactory.sol';
import { ISecurityPoolForker } from '../interfaces/ISecurityPoolForker.sol';
import { SecurityPoolDeployer } from './SecurityPoolDeployer.sol';
import { SecurityPoolUtils } from '../SecurityPoolUtils.sol';

contract SecurityPoolFactory is ISecurityPoolFactory {
ShareTokenFactory immutable shareTokenFactory;
Expand Down Expand Up @@ -131,9 +132,13 @@ contract SecurityPoolFactory is ISecurityPoolFactory {
function deployOriginSecurityPool(
uint248 universeId,
uint256 questionId,
uint256 securityMultiplier,
uint256 currentRetentionRate
uint256 securityMultiplier
) external returns (ISecurityPool securityPool) {
// Origin pool deployment is intentionally public, so first deployers must not be able to
// lock unsafe economic parameters into the canonical pool for a question/multiplier pair.
// Zero-utilization origin pools always start at the protocol retention curve's maximum rate.
require(securityMultiplier > 1, 'security multiplier');

// Validate that the question exists
require(questionData.questionCreatedTimestamp(questionId) > 0, 'question missing');

Expand All @@ -153,14 +158,15 @@ contract SecurityPoolFactory is ISecurityPoolFactory {
// sharetoken has different salt as sharetoken address does not change in forks
bytes32 shareTokenSalt = keccak256(abi.encode(securityMultiplier, questionId));
IShareToken shareToken = shareTokenFactory.deployShareToken(shareTokenSalt, questionId);
uint256 initialRetentionRate = SecurityPoolUtils.calculateRetentionRate(0, 0);
securityPool = deploySecurityPool(
shareToken,
ISecurityPool(payable(address(0))),
priceOracleManagerAndOperatorQueuer,
universeId,
questionId,
securityMultiplier,
currentRetentionRate,
initialRetentionRate,
0,
address(0)
);
Expand All @@ -176,7 +182,7 @@ contract SecurityPoolFactory is ISecurityPoolFactory {
universeId,
questionId,
securityMultiplier,
currentRetentionRate,
initialRetentionRate,
0
)
);
Expand Down
3 changes: 1 addition & 2 deletions solidity/contracts/peripherals/interfaces/ISecurityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@ interface ISecurityPoolFactory {
function deployOriginSecurityPool(
uint248 universeId,
uint256 questionId,
uint256 securityMultiplier,
uint256 currentRetentionRate
uint256 securityMultiplier
) external returns (ISecurityPool securityPool);
function securityPoolDeploymentCount() external view returns (uint256);
function securityPoolDeploymentsRange(
Expand Down
5 changes: 2 additions & 3 deletions solidity/ts/gas-costs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { createWriteClient, WriteClient, writeContractAndWait } from './testsuit

const genesisUniverse = 0n
const securityMultiplier = 2n
const maxRetentionRate = 999_999_996_848_000_000n
const repDepositAmount = 1_000n * 10n ** 18n
const securityBondAllowance = repDepositAmount / 4n
const openInterestAmount = 100n * 10n ** 18n
Expand Down Expand Up @@ -151,7 +150,7 @@ const setupPool = async (title: string): Promise<PoolContext> => {
const questionData = await buildQuestionData(title)
const questionId = getQuestionId(questionData, questionOutcomes)
await confirmTx(alice, createQuestion(alice, questionData, [...questionOutcomes]))
await confirmTx(alice, deployOriginSecurityPool(alice, genesisUniverse, questionId, securityMultiplier, maxRetentionRate))
await confirmTx(alice, deployOriginSecurityPool(alice, genesisUniverse, questionId, securityMultiplier))
const addresses = getSecurityPoolAddresses(zeroAddress, genesisUniverse, questionId, securityMultiplier)
return { questionData, questionId, addresses }
}
Expand Down Expand Up @@ -312,7 +311,7 @@ const scenarios: Scenario[] = [
const questionData = await buildQuestionData('Gas deploy pool')
const questionId = getQuestionId(questionData, questionOutcomes)
await confirmTx(alice, createQuestion(alice, questionData, [...questionOutcomes]))
return await waitForGas(alice, deployOriginSecurityPool(alice, genesisUniverse, questionId, securityMultiplier, maxRetentionRate))
return await waitForGas(alice, deployOriginSecurityPool(alice, genesisUniverse, questionId, securityMultiplier))
},
},
{
Expand Down
3 changes: 1 addition & 2 deletions solidity/ts/tests/escalationGame_forkThreshold.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { getERC20Balance } from '../testsuite/simulator/utils/utilities'
import { GENESIS_REPUTATION_TOKEN } from '../testsuite/simulator/utils/constants'

const DAY = 86400n
const MAX_RETENTION_RATE = 999_999_996_848_000_000n // ≈90% yearly
const ZOLTAR_UNIVERSE_THEORETICAL_SUPPLIES_SLOT = 2n

setDefaultTimeout(TEST_TIMEOUT_MS)
Expand Down Expand Up @@ -67,7 +66,7 @@ describe('Escalation Game Fork Threshold Test', () => {
questionId = getQuestionId(questionData, outcomes)
await createQuestion(client, questionData, outcomes)

await deployOriginSecurityPool(client, genesisUniverse, questionId, securityMultiplier, MAX_RETENTION_RATE)
await deployOriginSecurityPool(client, genesisUniverse, questionId, securityMultiplier)
await approveAndDepositRep(client, 1000n * 10n ** 18n, questionId)

securityPoolAddresses = getSecurityPoolAddresses(addressString(0x0n), genesisUniverse, questionId, securityMultiplier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { beforeEach, describe, test } from 'bun:test'
import { usePeripheralsDeploymentAndOwnForkEscalationFixture, type PeripheralsDeploymentAndOwnForkEscalationFixture } from './fixture'
import type { Abi, Address } from 'viem'
import type { WriteClient } from '../../testsuite/simulator/utils/viem'
import { peripherals_factories_SecurityPoolFactory_SecurityPoolFactory, peripherals_SecurityPool_SecurityPool } from '../../types/contractArtifact'

describe('Peripherals: deployment and own-fork escalation', () => {
const fixture = usePeripheralsDeploymentAndOwnForkEscalationFixture()
Expand Down Expand Up @@ -100,7 +101,7 @@ describe('Peripherals: deployment and own-fork escalation', () => {
const multiOutcomeQuestionId = getQuestionId(multiOutcomeQuestionData, multiOutcomes)

// Attempt to deploy security pool with non-binary question should fail.
await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, multiOutcomeQuestionId, securityMultiplier, MAX_RETENTION_RATE))
await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, multiOutcomeQuestionId, securityMultiplier))
})

test('cannot deploy security pool with scalar question', async () => {
Expand All @@ -120,15 +121,15 @@ describe('Peripherals: deployment and own-fork escalation', () => {
const scalarQuestionId = getQuestionId(scalarQuestionData, scalarOutcomes)

// Attempt to deploy security pool with scalar question should fail.
await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, scalarQuestionId, securityMultiplier, MAX_RETENTION_RATE))
await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, scalarQuestionId, securityMultiplier))
})

test('cannot deploy security pool with non-existent question', async () => {
// Use a questionId that has not been created
const nonExistentQuestionId = 999999999999n

// Attempt to deploy security pool with non-existent question should fail
await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, nonExistentQuestionId, securityMultiplier, MAX_RETENTION_RATE))
await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, nonExistentQuestionId, securityMultiplier))
})

test('cannot deploy origin security pool in an already-forked universe', async () => {
Expand All @@ -143,13 +144,57 @@ describe('Peripherals: deployment and own-fork escalation', () => {
await approveToken(client, addressString(GENESIS_REPUTATION_TOKEN), getZoltarAddress())
await forkUniverse(client, genesisUniverse, forkSourceQuestionId)

await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, questionId, securityMultiplier, MAX_RETENTION_RATE), /universe forked/)
await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, questionId, securityMultiplier), /universe forked/)
})

test('cannot deploy origin security pool in a missing universe', async () => {
const missingUniverseId = 999999n

await assert.rejects(deployOriginSecurityPool(client, missingUniverseId, questionId, securityMultiplier, MAX_RETENTION_RATE), /universe missing/)
await assert.rejects(deployOriginSecurityPool(client, missingUniverseId, questionId, securityMultiplier), /universe missing/)
})

test('origin security pool deployment derives protocol parameters for the first deployer', async () => {
const createBinaryQuestion = async (title: string) => {
const deploymentQuestionData = {
...questionData,
title,
}
await createQuestion(client, deploymentQuestionData, outcomes)
return getQuestionId(deploymentQuestionData, outcomes)
}
const zeroMultiplierQuestionId = await createBinaryQuestion(`zero multiplier ${await mockWindow.getTime()}`)
await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, zeroMultiplierQuestionId, 0n), /security multiplier/)

const oneMultiplierQuestionId = await createBinaryQuestion(`one multiplier ${await mockWindow.getTime()}`)
await assert.rejects(deployOriginSecurityPool(client, genesisUniverse, oneMultiplierQuestionId, 1n), /security multiplier/)

const callerRetentionQuestionId = await createBinaryQuestion(`caller retention ${await mockWindow.getTime()}`)
await deployOriginSecurityPool(client, genesisUniverse, callerRetentionQuestionId, securityMultiplier)
const callerRetentionPool = getSecurityPoolAddresses(addressString(0x0n), genesisUniverse, callerRetentionQuestionId, securityMultiplier).securityPool
const retentionRate = await client.readContract({
abi: peripherals_SecurityPool_SecurityPool.abi,
functionName: 'currentRetentionRate',
address: callerRetentionPool,
args: [],
})
const factoryAddress = getInfraContractAddresses().securityPoolFactory
const deploymentCount = await client.readContract({
abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi,
functionName: 'securityPoolDeploymentCount',
address: factoryAddress,
args: [],
})
const deployments = await client.readContract({
abi: peripherals_factories_SecurityPoolFactory_SecurityPoolFactory.abi,
functionName: 'securityPoolDeploymentsRange',
address: factoryAddress,
args: [deploymentCount - 1n, 1n],
})
const deployment = deployments[0]
if (deployment === undefined) throw new Error('origin deployment record missing')

strictEqualTypeSafe(retentionRate, MAX_RETENTION_RATE, 'origin retention rate should come from protocol math')
strictEqualTypeSafe(deployment.currentRetentionRate, MAX_RETENTION_RATE, 'origin deployment record should store the protocol retention rate')
})

test('can fork security pool using separate initiate and migrate calls with multiple migrations', async () => {
Expand Down Expand Up @@ -682,7 +727,7 @@ describe('Peripherals: deployment and own-fork escalation', () => {
}
const scenarioQuestionId = getQuestionId(scenarioQuestionData, outcomes)
await createQuestion(client, scenarioQuestionData, outcomes)
await deployOriginSecurityPool(client, genesisUniverse, scenarioQuestionId, securityMultiplier, MAX_RETENTION_RATE)
await deployOriginSecurityPool(client, genesisUniverse, scenarioQuestionId, securityMultiplier)
const scenarioPool = getSecurityPoolAddresses(addressString(0x0n), genesisUniverse, scenarioQuestionId, securityMultiplier).securityPool
const forkThreshold = (await getTotalTheoreticalSupply(client, await getRepToken(client, scenarioPool))) / 20n / securityMultiplier
const depositorsByAddress = new Map<Address, WriteClient>([
Expand Down
2 changes: 1 addition & 1 deletion solidity/ts/tests/peripherals/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ function usePeripheralsTestFixture() {
}
questionId = getQuestionId(questionData, outcomes)
await createQuestion(client, questionData, outcomes)
await deployOriginSecurityPool(client, genesisUniverse, questionId, securityMultiplier, MAX_RETENTION_RATE)
await deployOriginSecurityPool(client, genesisUniverse, questionId, securityMultiplier)
await approveAndDepositRep(client, repDeposit, questionId)
securityPoolAddresses = getSecurityPoolAddresses(addressString(0x0n), genesisUniverse, questionId, securityMultiplier)
}
Expand Down
9 changes: 4 additions & 5 deletions solidity/ts/tests/peripherals/truthAuction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ describe('Peripherals: truth auction', () => {
repDeposit,
genesisUniverse,
securityMultiplier,
MAX_RETENTION_RATE,
outcomes,
triggerExternalForkForSecurityPool,
setupTruthAuctionWithMixedBids,
Expand Down Expand Up @@ -350,7 +349,7 @@ describe('Peripherals: truth auction', () => {
}
const secondQuestionId = getQuestionId(secondQuestionData, outcomes)
await createQuestion(client, secondQuestionData, outcomes)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier, MAX_RETENTION_RATE)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier)

const secondPoolOwner = createWriteClient(mockWindow, TEST_ADDRESSES[1], 0)
await approveAndDepositRep(secondPoolOwner, repDeposit, secondQuestionId)
Expand Down Expand Up @@ -396,7 +395,7 @@ describe('Peripherals: truth auction', () => {
}
const secondQuestionId = getQuestionId(secondQuestionData, outcomes)
await createQuestion(client, secondQuestionData, outcomes)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier, MAX_RETENTION_RATE)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier)

const secondPoolOwner = createWriteClient(mockWindow, TEST_ADDRESSES[1], 0)
await approveAndDepositRep(secondPoolOwner, repDeposit, secondQuestionId)
Expand Down Expand Up @@ -506,7 +505,7 @@ describe('Peripherals: truth auction', () => {
}
const secondQuestionId = getQuestionId(secondQuestionData, outcomes)
await createQuestion(client, secondQuestionData, outcomes)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier, MAX_RETENTION_RATE)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier)

const secondPoolOwner = createWriteClient(mockWindow, TEST_ADDRESSES[1], 0)
await approveAndDepositRep(secondPoolOwner, repDeposit, secondQuestionId)
Expand Down Expand Up @@ -553,7 +552,7 @@ describe('Peripherals: truth auction', () => {
}
const secondQuestionId = getQuestionId(secondQuestionData, outcomes)
await createQuestion(client, secondQuestionData, outcomes)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier, MAX_RETENTION_RATE)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier)

const secondPoolOwner = createWriteClient(mockWindow, TEST_ADDRESSES[1], 0)
await approveAndDepositRep(secondPoolOwner, repDeposit, secondQuestionId)
Expand Down
2 changes: 1 addition & 1 deletion solidity/ts/tests/peripherals/vaultAccounting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ describe('Peripherals: vault accounting', () => {
}
const secondQuestionId = getQuestionId(secondQuestionData, outcomes)
await createQuestion(client, secondQuestionData, outcomes)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier, MAX_RETENTION_RATE)
await deployOriginSecurityPool(client, genesisUniverse, secondQuestionId, securityMultiplier)
await approveAndDepositRep(client, repDeposit, secondQuestionId)
await approveAndDepositRep(attackerClient, repDeposit, questionId)
await approveAndDepositRep(attackerClient, repDeposit, secondQuestionId)
Expand Down
3 changes: 1 addition & 2 deletions solidity/ts/tests/peripheralsInvariant.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ setDefaultTimeout(TEST_TIMEOUT_MS)
const genesisUniverse = 0n
const securityMultiplier = 2n
const repDeposit = 1000n * 10n ** 18n
const maxRetentionRate = 999_999_996_848_000_000n
const AUCTION_TIME = 604800n

type HarnessContext = {
Expand Down Expand Up @@ -86,7 +85,7 @@ describe('Peripherals invariant harness', () => {
const outcomes = sortStringArrayByKeccak(['Yes', 'No'])
await createQuestion(client, questionData, outcomes)
const questionId = getQuestionId(questionData, outcomes)
await deployOriginSecurityPool(client, genesisUniverse, questionId, securityMultiplier, maxRetentionRate)
await deployOriginSecurityPool(client, genesisUniverse, questionId, securityMultiplier)
await approveAndDepositRep(client, repDeposit, questionId)
const addresses = getSecurityPoolAddresses(addressString(0x0n), genesisUniverse, questionId, securityMultiplier)
return {
Expand Down
Loading
Loading