Merge remote-tracking branch 'upstream/main' into automated-market-resolution
# Conflicts: # common/contract.ts # package.json # web/components/contract/quick-bet.tsx # web/components/outcome-label.tsx # web/components/resolution-panel.tsx
|
@ -31,7 +31,7 @@ Operations with complicated contracts (e.g. buying shares) are provided in a sep
|
|||
|
||||
- `og-image/`: The OpenGraph image generator; creates the preview images shown on Twitter/social media.
|
||||
|
||||
Also: Our docs are currently in [a separate repo](https://github.com/manifoldmarkets/docs). TODO: move them into this monorepo.
|
||||
- `docs/`: Manifold's public documentation that lives at https://docs.manifold.markets.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { addCpmmLiquidity, getCpmmLiquidity } from './calculate-cpmm'
|
||||
import { Binary, CPMM, FullContract } from './contract'
|
||||
import { CPMMContract } from './contract'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
import { User } from './user'
|
||||
|
||||
export const getNewLiquidityProvision = (
|
||||
user: User,
|
||||
amount: number,
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
newLiquidityProvisionId: string
|
||||
) => {
|
||||
const { pool, p, totalLiquidity } = contract
|
||||
|
|
|
@ -2,12 +2,10 @@ import { range } from 'lodash'
|
|||
import { Bet, NumericBet } from './bet'
|
||||
import { getDpmProbability, getValueFromBucket } from './calculate-dpm'
|
||||
import {
|
||||
Binary,
|
||||
CPMM,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
Numeric,
|
||||
CPMMBinaryContract,
|
||||
DPMBinaryContract,
|
||||
FreeResponseContract,
|
||||
NumericContract,
|
||||
} from './contract'
|
||||
import { User } from './user'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
|
@ -23,7 +21,7 @@ export const HOUSE_LIQUIDITY_PROVIDER_ID = 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' // @Ma
|
|||
|
||||
export function getCpmmInitialLiquidity(
|
||||
providerId: string,
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMBinaryContract,
|
||||
anteId: string,
|
||||
amount: number
|
||||
) {
|
||||
|
@ -47,7 +45,7 @@ export function getCpmmInitialLiquidity(
|
|||
|
||||
export function getAnteBets(
|
||||
creator: User,
|
||||
contract: FullContract<DPM, Binary>,
|
||||
contract: DPMBinaryContract,
|
||||
yesAnteId: string,
|
||||
noAnteId: string
|
||||
) {
|
||||
|
@ -89,7 +87,7 @@ export function getAnteBets(
|
|||
|
||||
export function getFreeAnswerAnte(
|
||||
anteBettorId: string,
|
||||
contract: FullContract<DPM, FreeResponse>,
|
||||
contract: FreeResponseContract,
|
||||
anteBetId: string
|
||||
) {
|
||||
const { totalBets, totalShares } = contract
|
||||
|
@ -117,7 +115,7 @@ export function getFreeAnswerAnte(
|
|||
|
||||
export function getNumericAnte(
|
||||
anteBettorId: string,
|
||||
contract: FullContract<DPM, Numeric>,
|
||||
contract: NumericContract,
|
||||
ante: number,
|
||||
newBetId: string
|
||||
) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { sum, groupBy, mapValues, sumBy } from 'lodash'
|
||||
|
||||
import { Binary, CPMM, FullContract } from './contract'
|
||||
import { CPMMContract } from './contract'
|
||||
import { CREATOR_FEE, Fees, LIQUIDITY_FEE, noFees, PLATFORM_FEE } from './fees'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
import { addObjects } from './util/object'
|
||||
|
@ -14,7 +14,7 @@ export function getCpmmProbability(
|
|||
}
|
||||
|
||||
export function getCpmmProbabilityAfterBetBeforeFees(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
outcome: string,
|
||||
bet: number
|
||||
) {
|
||||
|
@ -31,7 +31,7 @@ export function getCpmmProbabilityAfterBetBeforeFees(
|
|||
}
|
||||
|
||||
export function getCpmmOutcomeProbabilityAfterBet(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
outcome: string,
|
||||
bet: number
|
||||
) {
|
||||
|
@ -59,7 +59,7 @@ function calculateCpmmShares(
|
|||
}
|
||||
|
||||
export function getCpmmLiquidityFee(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
bet: number,
|
||||
outcome: string
|
||||
) {
|
||||
|
@ -78,7 +78,7 @@ export function getCpmmLiquidityFee(
|
|||
}
|
||||
|
||||
export function calculateCpmmSharesAfterFee(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
bet: number,
|
||||
outcome: string
|
||||
) {
|
||||
|
@ -89,7 +89,7 @@ export function calculateCpmmSharesAfterFee(
|
|||
}
|
||||
|
||||
export function calculateCpmmPurchase(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
bet: number,
|
||||
outcome: string
|
||||
) {
|
||||
|
@ -133,7 +133,7 @@ function sellSharesK(
|
|||
}
|
||||
|
||||
function calculateCpmmShareValue(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
shares: number,
|
||||
outcome: 'YES' | 'NO'
|
||||
) {
|
||||
|
@ -168,7 +168,7 @@ function calculateCpmmShareValue(
|
|||
}
|
||||
|
||||
export function calculateCpmmSale(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
shares: number,
|
||||
outcome: string
|
||||
) {
|
||||
|
@ -222,7 +222,7 @@ export function calculateCpmmSale(
|
|||
}
|
||||
|
||||
export function getCpmmProbabilityAfterSale(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
shares: number,
|
||||
outcome: 'YES' | 'NO'
|
||||
) {
|
||||
|
@ -261,7 +261,7 @@ export function addCpmmLiquidity(
|
|||
}
|
||||
|
||||
export function getCpmmLiquidityPoolWeights(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
liquidities: LiquidityProvision[]
|
||||
) {
|
||||
const { p } = contract
|
||||
|
@ -291,7 +291,7 @@ export function getCpmmLiquidityPoolWeights(
|
|||
}
|
||||
|
||||
// export function removeCpmmLiquidity(
|
||||
// contract: FullContract<CPMM, Binary>,
|
||||
// contract: CPMMContract,
|
||||
// liquidity: number
|
||||
// ) {
|
||||
// const { YES, NO } = contract.pool
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import { cloneDeep, range, sum, sumBy, sortBy, mapValues } from 'lodash'
|
||||
import { Bet, NumericBet } from './bet'
|
||||
import {
|
||||
Binary,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
Numeric,
|
||||
NumericContract,
|
||||
} from './contract'
|
||||
import { DPMContract, DPMBinaryContract, NumericContract } from './contract'
|
||||
import { DPM_FEES } from './fees'
|
||||
import { normpdf } from '../common/util/math'
|
||||
import { addObjects } from './util/object'
|
||||
|
@ -202,7 +195,7 @@ export function calculateDpmRawShareValue(
|
|||
}
|
||||
|
||||
export function calculateDpmMoneyRatio(
|
||||
contract: FullContract<DPM, any>,
|
||||
contract: DPMContract,
|
||||
bet: Bet,
|
||||
shareValue: number
|
||||
) {
|
||||
|
@ -228,10 +221,7 @@ export function calculateDpmMoneyRatio(
|
|||
return actual / expected
|
||||
}
|
||||
|
||||
export function calculateDpmShareValue(
|
||||
contract: FullContract<DPM, any>,
|
||||
bet: Bet
|
||||
) {
|
||||
export function calculateDpmShareValue(contract: DPMContract, bet: Bet) {
|
||||
const { pool, totalShares } = contract
|
||||
const { shares, outcome } = bet
|
||||
|
||||
|
@ -243,17 +233,14 @@ export function calculateDpmShareValue(
|
|||
return adjShareValue
|
||||
}
|
||||
|
||||
export function calculateDpmSaleAmount(
|
||||
contract: FullContract<DPM, any>,
|
||||
bet: Bet
|
||||
) {
|
||||
export function calculateDpmSaleAmount(contract: DPMContract, bet: Bet) {
|
||||
const { amount } = bet
|
||||
const winnings = calculateDpmShareValue(contract, bet)
|
||||
return deductDpmFees(amount, winnings)
|
||||
}
|
||||
|
||||
export function calculateDpmPayout(
|
||||
contract: FullContract<DPM, any>,
|
||||
contract: DPMContract,
|
||||
bet: Bet,
|
||||
outcome: string
|
||||
) {
|
||||
|
@ -263,10 +250,7 @@ export function calculateDpmPayout(
|
|||
return calculateStandardDpmPayout(contract, bet, outcome)
|
||||
}
|
||||
|
||||
export function calculateDpmCancelPayout(
|
||||
contract: FullContract<DPM, any>,
|
||||
bet: Bet
|
||||
) {
|
||||
export function calculateDpmCancelPayout(contract: DPMContract, bet: Bet) {
|
||||
const { totalBets, pool } = contract
|
||||
const betTotal = sum(Object.values(totalBets))
|
||||
const poolTotal = sum(Object.values(pool))
|
||||
|
@ -275,7 +259,7 @@ export function calculateDpmCancelPayout(
|
|||
}
|
||||
|
||||
export function calculateStandardDpmPayout(
|
||||
contract: FullContract<DPM, any>,
|
||||
contract: DPMContract,
|
||||
bet: Bet,
|
||||
outcome: string
|
||||
) {
|
||||
|
@ -308,7 +292,7 @@ export function calculateStandardDpmPayout(
|
|||
}
|
||||
|
||||
export function calculateDpmPayoutAfterCorrectBet(
|
||||
contract: FullContract<DPM, any>,
|
||||
contract: DPMContract,
|
||||
bet: Bet
|
||||
) {
|
||||
const { totalShares, pool, totalBets, outcomeType } = contract
|
||||
|
@ -338,13 +322,10 @@ export function calculateDpmPayoutAfterCorrectBet(
|
|||
: outcomeType,
|
||||
}
|
||||
|
||||
return calculateStandardDpmPayout(newContract, bet, outcome)
|
||||
return calculateStandardDpmPayout(newContract as any, bet, outcome)
|
||||
}
|
||||
|
||||
function calculateMktDpmPayout(
|
||||
contract: FullContract<DPM, Binary | FreeResponse | Numeric>,
|
||||
bet: Bet
|
||||
) {
|
||||
function calculateMktDpmPayout(contract: DPMContract, bet: Bet) {
|
||||
if (contract.outcomeType === 'BINARY')
|
||||
return calculateBinaryMktDpmPayout(contract, bet)
|
||||
|
||||
|
@ -389,10 +370,7 @@ function calculateMktDpmPayout(
|
|||
return deductDpmFees(amount, winnings)
|
||||
}
|
||||
|
||||
function calculateBinaryMktDpmPayout(
|
||||
contract: FullContract<DPM, Binary>,
|
||||
bet: Bet
|
||||
) {
|
||||
function calculateBinaryMktDpmPayout(contract: DPMBinaryContract, bet: Bet) {
|
||||
const { resolutionProbability, totalShares, phantomShares } = contract
|
||||
const p =
|
||||
resolutionProbability !== undefined
|
||||
|
@ -413,7 +391,7 @@ function calculateBinaryMktDpmPayout(
|
|||
return deductDpmFees(amount, winnings)
|
||||
}
|
||||
|
||||
export function resolvedDpmPayout(contract: FullContract<DPM, any>, bet: Bet) {
|
||||
export function resolvedDpmPayout(contract: DPMContract, bet: Bet) {
|
||||
if (contract.resolution)
|
||||
return calculateDpmPayout(contract, bet, contract.resolution)
|
||||
throw new Error('Contract was not resolved')
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Bet } from './bet'
|
||||
import { getProbability } from './calculate'
|
||||
import { Binary, FixedPayouts, FullContract } from './contract'
|
||||
import { CPMMContract } from './contract'
|
||||
|
||||
export function calculateFixedPayout(
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
contract: CPMMContract,
|
||||
bet: Bet,
|
||||
outcome: string
|
||||
) {
|
||||
|
@ -23,10 +23,7 @@ export function calculateStandardFixedPayout(bet: Bet, outcome: string) {
|
|||
return shares
|
||||
}
|
||||
|
||||
function calculateFixedMktPayout(
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
bet: Bet
|
||||
) {
|
||||
function calculateFixedMktPayout(contract: CPMMContract, bet: Bet) {
|
||||
const { resolutionProbability } = contract
|
||||
const p =
|
||||
resolutionProbability !== undefined
|
||||
|
|
|
@ -18,24 +18,15 @@ import {
|
|||
getDpmProbabilityAfterSale,
|
||||
} from './calculate-dpm'
|
||||
import { calculateFixedPayout } from './calculate-fixed-payouts'
|
||||
import {
|
||||
Binary,
|
||||
Contract,
|
||||
CPMM,
|
||||
DPM,
|
||||
FreeResponseContract,
|
||||
FullContract,
|
||||
} from './contract'
|
||||
import { Contract, BinaryContract, FreeResponseContract } from './contract'
|
||||
|
||||
export function getProbability(contract: FullContract<DPM | CPMM, Binary>) {
|
||||
export function getProbability(contract: BinaryContract) {
|
||||
return contract.mechanism === 'cpmm-1'
|
||||
? getCpmmProbability(contract.pool, contract.p)
|
||||
: getDpmProbability(contract.totalShares)
|
||||
}
|
||||
|
||||
export function getInitialProbability(
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
) {
|
||||
export function getInitialProbability(contract: BinaryContract) {
|
||||
if (contract.initialProbability) return contract.initialProbability
|
||||
|
||||
if (contract.mechanism === 'dpm-2' || (contract as any).totalShares)
|
||||
|
@ -59,11 +50,7 @@ export function getOutcomeProbabilityAfterBet(
|
|||
bet: number
|
||||
) {
|
||||
return contract.mechanism === 'cpmm-1'
|
||||
? getCpmmOutcomeProbabilityAfterBet(
|
||||
contract as FullContract<CPMM, Binary>,
|
||||
outcome,
|
||||
bet
|
||||
)
|
||||
? getCpmmOutcomeProbabilityAfterBet(contract, outcome, bet)
|
||||
: getDpmOutcomeProbabilityAfterBet(contract.totalShares, outcome, bet)
|
||||
}
|
||||
|
||||
|
@ -73,11 +60,7 @@ export function calculateShares(
|
|||
betChoice: string
|
||||
) {
|
||||
return contract.mechanism === 'cpmm-1'
|
||||
? calculateCpmmSharesAfterFee(
|
||||
contract as FullContract<CPMM, Binary>,
|
||||
bet,
|
||||
betChoice
|
||||
)
|
||||
? calculateCpmmSharesAfterFee(contract, bet, betChoice)
|
||||
: calculateDpmShares(contract.totalShares, bet, betChoice)
|
||||
}
|
||||
|
||||
|
@ -99,11 +82,7 @@ export function getProbabilityAfterSale(
|
|||
shares: number
|
||||
) {
|
||||
return contract.mechanism === 'cpmm-1'
|
||||
? getCpmmProbabilityAfterSale(
|
||||
contract as FullContract<CPMM, Binary>,
|
||||
shares,
|
||||
outcome as 'YES' | 'NO'
|
||||
)
|
||||
? getCpmmProbabilityAfterSale(contract, shares, outcome as 'YES' | 'NO')
|
||||
: getDpmProbabilityAfterSale(contract.totalShares, outcome, shares)
|
||||
}
|
||||
|
||||
|
|
|
@ -47,8 +47,10 @@ export const charities: Charity[] = [
|
|||
website: 'https://funds.effectivealtruism.org/funds/far-future',
|
||||
photo: 'https://i.imgur.com/C2qka9g.png',
|
||||
preview:
|
||||
'Positively influence the long-term trajectory of civilization by making grants that address global catastrophic risks.',
|
||||
description: `The Fund has a broad remit to make grants that promote, implement and advocate for longtermist ideas. Many of our grants aim to address potential risks from advanced artificial intelligence and to build infrastructure and advocate for longtermist projects. However, we welcome applications related to long-term institutional reform or other global catastrophic risks (e.g., pandemics or nuclear conflict).
|
||||
'The Long-Term Future Fund aims to improve the long-term trajectory of civilization by making grants that address global catastrophic risks.',
|
||||
description: `The Long-Term Future Fund aims to positively influence the long-term trajectory of civilization by making grants that address global catastrophic risks, especially potential risks from advanced artificial intelligence and pandemics. In addition, we seek to promote, implement, and advocate for longtermist ideas, and to otherwise increase the likelihood that future generations will flourish.
|
||||
|
||||
The Fund has a broad remit to make grants that promote, implement and advocate for longtermist ideas. Many of our grants aim to address potential risks from advanced artificial intelligence and to build infrastructure and advocate for longtermist projects. However, we welcome applications related to long-term institutional reform or other global catastrophic risks (e.g., pandemics or nuclear conflict).
|
||||
|
||||
We intend to support:
|
||||
- Projects that directly contribute to reducing existential risks through technical research, policy analysis, advocacy, and/or demonstration projects
|
||||
|
@ -56,6 +58,53 @@ export const charities: Charity[] = [
|
|||
- Promoting long-term thinking`,
|
||||
tags: ['Featured'] as CharityTag[],
|
||||
},
|
||||
{
|
||||
name: 'Global Health and Development Fund',
|
||||
website: 'https://funds.effectivealtruism.org/funds/global-development',
|
||||
photo: 'https://i.imgur.com/C2qka9g.png',
|
||||
preview:
|
||||
"The Global Health and Development Fund aims to improve people's lives, typically in the poorest regions of the world where the need for healthcare and economic empowerment is greatest.",
|
||||
description: `The Global Health and Development Fund recommends grants with the aim of improving people's lives, typically in the poorest regions of the world where the need for healthcare and economic empowerment is greatest. This will be achieved primarily by supporting projects that:
|
||||
|
||||
- Directly provide healthcare, or preventive measures that will improve health, well-being, or life expectancy
|
||||
- Directly provide services that raise incomes or otherwise improve economic conditions
|
||||
- Provide assistance to governments in the design and implementation of effective policies
|
||||
|
||||
In addition, the Global Health and Development Fund has a broad remit, and may fund other activities whose ultimate purpose is to serve people living in the poorest regions of the world, for example by raising additional funds (e.g. One for the World) or by exploring novel financing arrangements (e.g. Instiglio).
|
||||
|
||||
The Fund manager recommends grants to GiveWell top charities as a baseline, but will recommend higher-risk grants they believe to be more effective (in expectation) than GiveWell top charities. As such, the fund makes grants with a variety of different risk profiles.`,
|
||||
},
|
||||
{
|
||||
name: 'Animal Welfare Fund',
|
||||
website: 'https://funds.effectivealtruism.org/funds/animal-welfare',
|
||||
photo: 'https://i.imgur.com/C2qka9g.png',
|
||||
preview:
|
||||
'The Animal Welfare Fund aims to effectively improve the well-being of nonhuman animals.',
|
||||
description: `The Animal Welfare Fund aims to effectively improve the well-being of nonhuman animals, by making grants that focus on one or more of the following:
|
||||
|
||||
- Relatively neglected geographic regions or groups of animals
|
||||
- Promising research into animal advocacy or animal well-being
|
||||
- Activities that could make it easier to help animals in the future
|
||||
- Otherwise best-in-class opportunities
|
||||
|
||||
The Fund focuses on projects that primarily address farmed animals, as well as projects that could affect other large populations of nonhuman animals. Some examples of projects that the Fund could support:
|
||||
|
||||
- Supporting farmed animal advocacy in Asia
|
||||
- Researching ways to improve the welfare of farmed fish
|
||||
- Promoting alternative proteins in order to reduce demand for animal products
|
||||
- Advocating against the use of some cruel practice within the industrial agriculture system
|
||||
- Growing the field of welfare biology in order to improve our understanding of different ways to address wild animal suffering`,
|
||||
},
|
||||
{
|
||||
name: 'Effective Altruism Infrastructure Fund',
|
||||
website: 'https://funds.effectivealtruism.org/funds/ea-community',
|
||||
photo: 'https://i.imgur.com/C2qka9g.png',
|
||||
preview:
|
||||
'The Effective Altruism Infrastructure Fund aims to increase the impact of projects that use the principles of effective altruism.',
|
||||
description: `The Effective Altruism Infrastructure Fund (EA Infrastructure Fund) recommends grants that aim to improve the work of projects using principles of effective altruism, by increasing their access to talent, capital, and knowledge.
|
||||
|
||||
The EA Infrastructure Fund has historically attempted to make strategic grants to incubate and grow projects that attempt to use reason and evidence to do as much good as possible. These include meta-charities that fundraise for highly effective charities doing direct work on important problems, research organizations that improve our understanding of how to do good more effectively, and projects that promote principles of effective altruism in contexts like academia.`,
|
||||
},
|
||||
{
|
||||
name: 'Nonlinear',
|
||||
website: 'https://www.nonlinear.org/',
|
||||
|
@ -104,6 +153,32 @@ export const charities: Charity[] = [
|
|||
- Each charity’s plans for additional funding
|
||||
- The cost-effectiveness of each funding gap`,
|
||||
},
|
||||
{
|
||||
name: "Founder's Pledge Climate Change Fund",
|
||||
website: 'https://founderspledge.com/funds/climate-change-fund',
|
||||
photo: 'https://i.imgur.com/ZAhzHu4.png',
|
||||
preview:
|
||||
'The Climate Change Fund aims to sustainably reach net-zero emissions globally, while still allowing growth to free millions from energy poverty.',
|
||||
description: `The Climate Change Fund aims to sustainably reach net-zero emissions globally.
|
||||
|
||||
Current levels of emissions are contributing to millions of deaths annually from air pollution and causing irrevocable damage to our planet. In addition, millions worldwide do not have access to modern energy technology, severely hampering development goals.
|
||||
|
||||
This Fund is committed to finding and funding sustainable solutions to the emissions crisis that still allow growth, freeing millions from the prison of energy poverty.
|
||||
|
||||
The Fund is a philanthropic co-funding vehicle that does not provide investment returns.`,
|
||||
},
|
||||
{
|
||||
name: "Founder's Pledge Patient Philanthropy Fund",
|
||||
website: 'https://founderspledge.com/funds/patient-philanthropy-fund',
|
||||
photo: 'https://i.imgur.com/ZAhzHu4.png',
|
||||
preview:
|
||||
'The Patient Philanthropy Project aims to safeguard and benefit the long-term future of humanity',
|
||||
description: `The Patient Philanthropy Project focuses on how we can collectively grow our resources to support the long-term flourishing of humanity. It addresses a crucial gap: as a society, we spend much too little on safeguarding and benefiting future generations. In fact, we spend more money on ice cream each year than we do on preventing our own extinction. However, people in the future - who do not have a voice in their future survival or environment - matter. Lots of them may yet come into existence and we have the ability to positively affect their lives now, if only by making sure we avoid major catastrophes that could destroy our common future.
|
||||
|
||||
Housed within the Project is the Patient Philanthropy Fund, a philanthropic co-funding vehicle which invests to give and ensures capital is at the ready when extraordinary opportunities to safeguard and improve the long-term future arise.
|
||||
|
||||
The Fund’s patient approach means that we aim to identify the point in time when the highest-impact opportunities are available, which may be years, decades, or even centuries ahead.`,
|
||||
},
|
||||
{
|
||||
name: 'ARC',
|
||||
website: 'https://alignment.org/',
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import { Answer } from './answer'
|
||||
import { Fees } from './fees'
|
||||
|
||||
export type FullContract<
|
||||
M extends DPM | CPMM,
|
||||
T extends Binary | Multi | FreeResponse | Numeric
|
||||
> = {
|
||||
export type AnyMechanism = DPM | CPMM
|
||||
export type AnyOutcomeType = Binary | FreeResponse | Numeric
|
||||
export type AnyContractType =
|
||||
| (CPMM & Binary)
|
||||
| (DPM & Binary)
|
||||
| (DPM & FreeResponse)
|
||||
| (DPM & Numeric)
|
||||
|
||||
export type Contract<T extends AnyContractType = AnyContractType> = {
|
||||
id: string
|
||||
slug: string // auto-generated; must be unique
|
||||
|
||||
|
@ -37,16 +42,15 @@ export type FullContract<
|
|||
volume7Days: number
|
||||
|
||||
collectedFees: Fees
|
||||
} & M &
|
||||
T
|
||||
} & T
|
||||
|
||||
export type Contract = FullContract<
|
||||
DPM | CPMM,
|
||||
Binary | Multi | FreeResponse | Numeric
|
||||
>
|
||||
export type BinaryContract = FullContract<DPM | CPMM, Binary>
|
||||
export type FreeResponseContract = FullContract<DPM | CPMM, FreeResponse>
|
||||
export type NumericContract = FullContract<DPM, Numeric>
|
||||
export type BinaryContract = Contract & Binary
|
||||
export type NumericContract = Contract & Numeric
|
||||
export type FreeResponseContract = Contract & FreeResponse
|
||||
export type DPMContract = Contract & DPM
|
||||
export type CPMMContract = Contract & CPMM
|
||||
export type DPMBinaryContract = BinaryContract & DPM
|
||||
export type CPMMBinaryContract = BinaryContract & CPMM
|
||||
|
||||
export type DPM = {
|
||||
mechanism: 'dpm-2'
|
||||
|
@ -64,8 +68,6 @@ export type CPMM = {
|
|||
totalLiquidity: number // in M$
|
||||
}
|
||||
|
||||
export type FixedPayouts = CPMM
|
||||
|
||||
export type Binary = {
|
||||
outcomeType: 'BINARY'
|
||||
initialProbability: number
|
||||
|
@ -73,12 +75,6 @@ export type Binary = {
|
|||
resolution?: resolution
|
||||
}
|
||||
|
||||
export type Multi = {
|
||||
outcomeType: 'MULTI'
|
||||
multiOutcomes: string[] // Used for outcomeType 'MULTI'.
|
||||
resolutions?: { [outcome: string]: number } // Used for MKT resolution.
|
||||
}
|
||||
|
||||
export type FreeResponse = {
|
||||
outcomeType: 'FREE_RESPONSE'
|
||||
answers: Answer[] // Used for outcomeType 'FREE_RESPONSE'.
|
||||
|
@ -96,13 +92,12 @@ export type Numeric = {
|
|||
}
|
||||
|
||||
export type contractField = keyof Contract
|
||||
export type outcomeType = 'BINARY' | 'MULTI' | 'FREE_RESPONSE' | 'NUMERIC'
|
||||
export type outcomeType = AnyOutcomeType['outcomeType']
|
||||
export type resolution = 'YES' | 'NO' | 'MKT' | 'CANCEL'
|
||||
|
||||
export const RESOLUTIONS = [ 'YES', 'NO', 'MKT', 'CANCEL'] as const
|
||||
export const OUTCOME_TYPES = [ 'BINARY', 'MULTI', 'FREE_RESPONSE', 'NUMERIC'] as const
|
||||
export const RESOLUTION_TYPES = ['MANUAL', 'COMBINED'] as const
|
||||
|
||||
export const OUTCOME_TYPES = ['BINARY', 'FREE_RESPONSE', 'NUMERIC'] as const
|
||||
export const MAX_QUESTION_LENGTH = 480
|
||||
export const MAX_DESCRIPTION_LENGTH = 10000
|
||||
export const MAX_TAG_LENGTH = 60
|
||||
|
|
|
@ -10,12 +10,9 @@ import {
|
|||
} from './calculate-dpm'
|
||||
import { calculateCpmmPurchase, getCpmmProbability } from './calculate-cpmm'
|
||||
import {
|
||||
Binary,
|
||||
CPMM,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
Multi,
|
||||
CPMMBinaryContract,
|
||||
DPMBinaryContract,
|
||||
FreeResponseContract,
|
||||
NumericContract,
|
||||
} from './contract'
|
||||
import { noFees } from './fees'
|
||||
|
@ -35,7 +32,7 @@ export type BetInfo = {
|
|||
export const getNewBinaryCpmmBetInfo = (
|
||||
outcome: 'YES' | 'NO',
|
||||
amount: number,
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMBinaryContract,
|
||||
loanAmount: number
|
||||
) => {
|
||||
const { shares, newPool, newP, fees } = calculateCpmmPurchase(
|
||||
|
@ -69,7 +66,7 @@ export const getNewBinaryCpmmBetInfo = (
|
|||
export const getNewBinaryDpmBetInfo = (
|
||||
outcome: 'YES' | 'NO',
|
||||
amount: number,
|
||||
contract: FullContract<DPM, Binary>,
|
||||
contract: DPMBinaryContract,
|
||||
loanAmount: number
|
||||
) => {
|
||||
const { YES: yesPool, NO: noPool } = contract.pool
|
||||
|
@ -116,7 +113,7 @@ export const getNewBinaryDpmBetInfo = (
|
|||
export const getNewMultiBetInfo = (
|
||||
outcome: string,
|
||||
amount: number,
|
||||
contract: FullContract<DPM, Multi | FreeResponse>,
|
||||
contract: FreeResponseContract,
|
||||
loanAmount: number
|
||||
) => {
|
||||
const { pool, totalShares, totalBets } = contract
|
||||
|
|
29
common/notification.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
export type Notification = {
|
||||
id: string
|
||||
userId: string
|
||||
reasonText?: string
|
||||
reason?: notification_reason_types
|
||||
createdTime: number
|
||||
viewTime?: number
|
||||
isSeen: boolean
|
||||
|
||||
sourceId?: string
|
||||
sourceType?: notification_source_types
|
||||
sourceContractId?: string
|
||||
sourceUserName?: string
|
||||
sourceUserUsername?: string
|
||||
sourceUserAvatarUrl?: string
|
||||
}
|
||||
export type notification_source_types =
|
||||
| 'contract'
|
||||
| 'comment'
|
||||
| 'bet'
|
||||
| 'answer'
|
||||
| 'liquidity'
|
||||
|
||||
export type notification_reason_types =
|
||||
| 'created'
|
||||
| 'updated'
|
||||
| 'resolved'
|
||||
| 'tagged'
|
||||
| 'replied'
|
|
@ -2,14 +2,11 @@ import { sum, groupBy, sumBy, mapValues } from 'lodash'
|
|||
|
||||
import { Bet, NumericBet } from './bet'
|
||||
import { deductDpmFees, getDpmProbability } from './calculate-dpm'
|
||||
import { DPM, FreeResponse, FullContract, Multi } from './contract'
|
||||
import { DPMContract, FreeResponseContract } from './contract'
|
||||
import { DPM_CREATOR_FEE, DPM_FEES, DPM_PLATFORM_FEE } from './fees'
|
||||
import { addObjects } from './util/object'
|
||||
|
||||
export const getDpmCancelPayouts = (
|
||||
contract: FullContract<DPM, any>,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
export const getDpmCancelPayouts = (contract: DPMContract, bets: Bet[]) => {
|
||||
const { pool } = contract
|
||||
const poolTotal = sum(Object.values(pool))
|
||||
console.log('resolved N/A, pool M$', poolTotal)
|
||||
|
@ -31,7 +28,7 @@ export const getDpmCancelPayouts = (
|
|||
|
||||
export const getDpmStandardPayouts = (
|
||||
outcome: string,
|
||||
contract: FullContract<DPM, any>,
|
||||
contract: DPMContract,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const winningBets = bets.filter((bet) => bet.outcome === outcome)
|
||||
|
@ -78,7 +75,7 @@ export const getDpmStandardPayouts = (
|
|||
|
||||
export const getNumericDpmPayouts = (
|
||||
outcome: string,
|
||||
contract: FullContract<DPM, any>,
|
||||
contract: DPMContract,
|
||||
bets: NumericBet[]
|
||||
) => {
|
||||
const totalShares = sumBy(bets, (bet) => bet.allOutcomeShares[outcome] ?? 0)
|
||||
|
@ -129,7 +126,7 @@ export const getNumericDpmPayouts = (
|
|||
}
|
||||
|
||||
export const getDpmMktPayouts = (
|
||||
contract: FullContract<DPM, any>,
|
||||
contract: DPMContract,
|
||||
bets: Bet[],
|
||||
resolutionProbability?: number
|
||||
) => {
|
||||
|
@ -183,7 +180,7 @@ export const getDpmMktPayouts = (
|
|||
|
||||
export const getPayoutsMultiOutcome = (
|
||||
resolutions: { [outcome: string]: number },
|
||||
contract: FullContract<DPM, Multi | FreeResponse>,
|
||||
contract: FreeResponseContract,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const poolTotal = sum(Object.values(contract.pool))
|
||||
|
|
|
@ -3,7 +3,7 @@ import { sum } from 'lodash'
|
|||
import { Bet } from './bet'
|
||||
import { getProbability } from './calculate'
|
||||
import { getCpmmLiquidityPoolWeights } from './calculate-cpmm'
|
||||
import { Binary, CPMM, FixedPayouts, FullContract } from './contract'
|
||||
import { CPMMContract } from './contract'
|
||||
import { noFees } from './fees'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
|
||||
|
@ -30,7 +30,7 @@ export const getFixedCancelPayouts = (
|
|||
|
||||
export const getStandardFixedPayouts = (
|
||||
outcome: string,
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
contract: CPMMContract,
|
||||
bets: Bet[],
|
||||
liquidities: LiquidityProvision[]
|
||||
) => {
|
||||
|
@ -65,7 +65,7 @@ export const getStandardFixedPayouts = (
|
|||
}
|
||||
|
||||
export const getLiquidityPoolPayouts = (
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
outcome: string,
|
||||
liquidities: LiquidityProvision[]
|
||||
) => {
|
||||
|
@ -81,7 +81,7 @@ export const getLiquidityPoolPayouts = (
|
|||
}
|
||||
|
||||
export const getMktFixedPayouts = (
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
contract: CPMMContract,
|
||||
bets: Bet[],
|
||||
liquidities: LiquidityProvision[],
|
||||
resolutionProbability?: number
|
||||
|
@ -116,7 +116,7 @@ export const getMktFixedPayouts = (
|
|||
}
|
||||
|
||||
export const getLiquidityPoolProbPayouts = (
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
p: number,
|
||||
liquidities: LiquidityProvision[]
|
||||
) => {
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
import { sumBy, groupBy, mapValues } from 'lodash'
|
||||
|
||||
import { Bet, NumericBet } from './bet'
|
||||
import {
|
||||
Binary,
|
||||
Contract,
|
||||
DPM,
|
||||
FixedPayouts,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
Multi,
|
||||
} from './contract'
|
||||
import { Contract, CPMMBinaryContract, DPMContract } from './contract'
|
||||
import { Fees } from './fees'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
import {
|
||||
|
@ -55,7 +47,7 @@ export type PayoutInfo = {
|
|||
}
|
||||
|
||||
export const getPayouts = (
|
||||
outcome: string,
|
||||
outcome: string | undefined,
|
||||
resolutions: {
|
||||
[outcome: string]: number
|
||||
},
|
||||
|
@ -73,7 +65,6 @@ export const getPayouts = (
|
|||
resolutionProbability
|
||||
)
|
||||
}
|
||||
|
||||
return getDpmPayouts(
|
||||
outcome,
|
||||
resolutions,
|
||||
|
@ -84,8 +75,8 @@ export const getPayouts = (
|
|||
}
|
||||
|
||||
export const getFixedPayouts = (
|
||||
outcome: string,
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
outcome: string | undefined,
|
||||
contract: CPMMBinaryContract,
|
||||
bets: Bet[],
|
||||
liquidities: LiquidityProvision[],
|
||||
resolutionProbability?: number
|
||||
|
@ -108,11 +99,11 @@ export const getFixedPayouts = (
|
|||
}
|
||||
|
||||
export const getDpmPayouts = (
|
||||
outcome: string,
|
||||
outcome: string | undefined,
|
||||
resolutions: {
|
||||
[outcome: string]: number
|
||||
},
|
||||
contract: Contract,
|
||||
contract: DPMContract,
|
||||
bets: Bet[],
|
||||
resolutionProbability?: number
|
||||
): PayoutInfo => {
|
||||
|
@ -125,13 +116,10 @@ export const getDpmPayouts = (
|
|||
|
||||
case 'MKT':
|
||||
return contract.outcomeType === 'FREE_RESPONSE'
|
||||
? getPayoutsMultiOutcome(
|
||||
resolutions,
|
||||
contract as FullContract<DPM, Multi | FreeResponse>,
|
||||
openBets
|
||||
)
|
||||
? getPayoutsMultiOutcome(resolutions, contract, openBets)
|
||||
: getDpmMktPayouts(contract, openBets, resolutionProbability)
|
||||
case 'CANCEL':
|
||||
case undefined:
|
||||
return getDpmCancelPayouts(contract, openBets)
|
||||
|
||||
default:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { groupBy, sumBy, mapValues, partition } from 'lodash'
|
||||
|
||||
import { Bet } from './bet'
|
||||
import { Binary, Contract, FullContract } from './contract'
|
||||
import { Contract } from './contract'
|
||||
import { getPayouts } from './payouts'
|
||||
|
||||
export function scoreCreators(contracts: Contract[]) {
|
||||
|
@ -24,23 +24,24 @@ export function scoreTraders(contracts: Contract[], bets: Bet[][]) {
|
|||
return userScores
|
||||
}
|
||||
|
||||
export function scoreUsersByContract(
|
||||
contract: FullContract<any, Binary>,
|
||||
bets: Bet[]
|
||||
) {
|
||||
const { resolution, resolutionProbability } = contract
|
||||
export function scoreUsersByContract(contract: Contract, bets: Bet[]) {
|
||||
const { resolution } = contract
|
||||
const resolutionProb =
|
||||
contract.outcomeType == 'BINARY'
|
||||
? contract.resolutionProbability
|
||||
: undefined
|
||||
|
||||
const [closedBets, openBets] = partition(
|
||||
bets,
|
||||
(bet) => bet.isSold || bet.sale
|
||||
)
|
||||
const { payouts: resolvePayouts } = getPayouts(
|
||||
resolution,
|
||||
resolution as string,
|
||||
{},
|
||||
contract,
|
||||
openBets,
|
||||
[],
|
||||
resolutionProbability
|
||||
resolutionProb
|
||||
)
|
||||
|
||||
const salePayouts = closedBets.map((bet) => {
|
||||
|
|
|
@ -5,14 +5,14 @@ import {
|
|||
deductDpmFees,
|
||||
} from './calculate-dpm'
|
||||
import { calculateCpmmSale, getCpmmProbability } from './calculate-cpmm'
|
||||
import { Binary, DPM, CPMM, FullContract } from './contract'
|
||||
import { CPMMContract, DPMContract } from './contract'
|
||||
import { DPM_CREATOR_FEE, DPM_PLATFORM_FEE, Fees } from './fees'
|
||||
import { User } from './user'
|
||||
|
||||
export const getSellBetInfo = (
|
||||
user: User,
|
||||
bet: Bet,
|
||||
contract: FullContract<DPM, any>,
|
||||
contract: DPMContract,
|
||||
newBetId: string
|
||||
) => {
|
||||
const { pool, totalShares, totalBets } = contract
|
||||
|
@ -87,7 +87,7 @@ export const getCpmmSellBetInfo = (
|
|||
user: User,
|
||||
shares: number,
|
||||
outcome: 'YES' | 'NO',
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
contract: CPMMContract,
|
||||
prevLoanAmount: number,
|
||||
newBetId: string
|
||||
) => {
|
||||
|
|
2
docs/.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
20
docs/.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
2
docs/.prettierignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
.docusaurus/
|
||||
build/
|
7
docs/.prettierrc
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": false,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true
|
||||
}
|
3
docs/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# docs
|
||||
|
||||
Manifold Markets Docs
|
3
docs/babel.config.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
}
|
85
docs/docs/about.md
Normal file
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
id: about
|
||||
slug: /
|
||||
---
|
||||
|
||||
# About Manifold Markets
|
||||
|
||||
Manifold Markets lets anyone create a prediction market on any topic. Win virtual money betting on what you know, from **[chess tournaments](https://manifold.markets/SG/will-magnus-carlsen-lose-any-regula)** to **[lunar collisions](https://manifold.markets/Duncan/will-the-wayward-falcon-9-booster-h)** to **[newsletter subscriber rates](https://manifold.markets/Nu%C3%B1oSempere/how-many-additional-subscribers-wil)** - or learn about the future by creating your own market!
|
||||
|
||||
### **What are prediction markets?**
|
||||
|
||||
**Prediction markets are a place where you can bet on the outcome of future events.**
|
||||
|
||||
Consider a question like: "Will Democrats win the 2024 US presidential election?"
|
||||
|
||||
If I think the Democrats are very likely to win, and you disagree, I might offer $70 to your $30 (with the winner taking home $100 total). This set of bets imply a 70% probability of the Democrats winning.
|
||||
|
||||
Now, you or I could be mistaken and overshooting the true probability one way or another. If so, there's an incentive for someone else to bet and correct it! Over time, the implied probability will converge to the **[market's best estimate](https://en.wikipedia.org/wiki/Efficient-market_hypothesis)**. Since these probabilities are public, anyone can use them to make better decisions!
|
||||
|
||||
### **How does Manifold Markets work?**
|
||||
|
||||
1. **Anyone can create a market for any yes-or-no question.**
|
||||
|
||||
You can ask questions about the future like "Will Taiwan remove its 14-day COVID quarantine by Jun 01, 2022?" If the market thinks this is very likely, you can plan more activities for your trip.
|
||||
|
||||
You can also ask subjective, personal questions like "Will I enjoy my 2022 Taiwan trip?". Then share the market with your family and friends and get their takes!
|
||||
|
||||
2. **Anyone can bet on a market using Manifold Dollars (M$), our platform currency.**
|
||||
|
||||
You get M$ 1,000 just for signing up, so you can start betting immediately! When a market creator decides an outcome in your favor, you'll win Manifold Dollars from people who bet against you.
|
||||
|
||||
More questions? Check out **[this community-driven FAQ](https://outsidetheasylum.blog/manifold-markets-faq/)**!
|
||||
|
||||
### **Can prediction markets work without real money?**
|
||||
|
||||
Yes! There is substantial evidence that play-money prediction markets provide real predictive power. Examples include **[sports betting](http://www.electronicmarkets.org/fileadmin/user_upload/doc/Issues/Volume_16/Issue_01/V16I1_Statistical_Tests_of_Real-Money_versus_Play-Money_Prediction_Markets.pdf)** and internal prediction markets at firms like **[Google](https://www.networkworld.com/article/2284098/google-bets-on-value-of-prediction-markets.html)**.
|
||||
|
||||
Our overall design also ensures that good forecasting will come out on top in the long term. In the competitive environment of the marketplace, bettors that are correct more often will gain influence, leading to better-calibrated forecasts over time.
|
||||
|
||||
Since our launch, we've seen hundreds of users trade each day, on over a thousand different markets! You can track the popularity of our platform at **[http://manifold.markets/analytics](http://manifold.markets/analytics)**.
|
||||
|
||||
### **Why is this important?**
|
||||
|
||||
Prediction markets aggregate and reveal crucial information that would not otherwise be known. They are a bottom-up mechanism that can influence everything from politics, economics, and business, to scientific research and education.
|
||||
|
||||
Prediction markets can predict **[which research papers will replicate](https://www.pnas.org/content/112/50/15343)**; which drug is the most effective; which policy would generate the most tax revenue; which charity will be underfunded; or which startup idea is the most promising. By surfacing and quantifying our collective knowledge, we as a society become wiser.
|
||||
|
||||
### **How are markets resolved?**
|
||||
|
||||
The creator of the prediction market decides the outcome and earns a commission based on the trade volume.
|
||||
|
||||
This simple resolution mechanism has surprising benefits in allowing a diversity of views to flourish. Competition between market creators will lead to traders flocking to the creators with good judgment on market resolution.
|
||||
|
||||
What's more, when the creator is free to use their judgment, many new kinds of prediction markets can be created that are less objective or even personal. (E.g. "Will I enjoy participating in the Metaverse in 2023?")
|
||||
|
||||
<!-- ### **Can I create private markets?**
|
||||
|
||||
Soon! We're running a pilot version of Manifold for Teams - private Manifold instances where you can discuss internal topics and predict on outcomes for your organization.
|
||||
|
||||
If this sounds like something you’d want, **[join the waitlist here](https://docs.google.com/forms/d/e/1FAIpQLSfM_rxRHemCjKE6KPiYXGyP2nBSInZNKn_wc7yS1-rvlLAVnA/viewform?usp=sf_link)**! -->
|
||||
|
||||
### **Who are we?**
|
||||
|
||||
Manifold Markets is currently a team of three:
|
||||
|
||||
- James Grugett
|
||||
- Stephen Grugett
|
||||
- Austin Chen
|
||||
|
||||
We've previously launched consumer-facing startups (**[Throne](https://throne.live/)**, **[One Word](http://oneword.games/platform)**), and worked at top tech and trading firms (Google, Susquehanna).
|
||||
|
||||
## **Talk to us!**
|
||||
|
||||
Questions? Comments? Want to create a market? Talk to us!
|
||||
|
||||
- Email: **[info@manifold.markets](mailto:info@manifold.markets)**
|
||||
- Office hours:
|
||||
- **[Calendly — Austin](https://calendly.com/austinchen/manifold)**
|
||||
- **[Calendly — James](https://calendly.com/jamesgrugett/manifold)**
|
||||
- Chat: **[Manifold Markets Discord server](https://discord.gg/eHQBNBqXuh)**
|
||||
|
||||
## **Further Reading**
|
||||
|
||||
- **[Above the fold](https://manifoldmarkets.substack.com/)**, our newsletter
|
||||
- **[Scott Alexander on play-money prediction markets](https://astralcodexten.substack.com/p/play-money-and-reputation-systems)**
|
369
docs/docs/api.md
Normal file
|
@ -0,0 +1,369 @@
|
|||
# API
|
||||
|
||||
:::caution
|
||||
|
||||
Our API is still in alpha — things may change or break at any time!
|
||||
|
||||
:::
|
||||
|
||||
Manifold currently supports a basic, read-only API for getting information about our markets.
|
||||
|
||||
If you have questions, come chat with us on [Discord](https://discord.com/invite/eHQBNBqXuh). We’d love to hear about what you build!
|
||||
|
||||
## List out all markets
|
||||
|
||||
### `/v0/markets`
|
||||
|
||||
- Example request
|
||||
```
|
||||
http://manifold.markets/api/v0/markets
|
||||
```
|
||||
- Example response
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id":"FKtYX3t8ZfIp5gytJWAI",
|
||||
"creatorUsername":"JamesGrugett",
|
||||
"creatorName":"James Grugett",
|
||||
"createdTime":1645139406452,
|
||||
"closeTime":1647406740000,
|
||||
"question":"What will be the best assessment of the Free response feature on March 15th?",
|
||||
"description":"Hey guys, let's try this out!\nWe will see how people use the new Free response market type over the next month. Then I will pick the answer that I think best describes the consensus view of this feature on March 15th. Cheers.",
|
||||
"tags":[
|
||||
"ManifoldMarkets"
|
||||
],
|
||||
"url":"https://manifold.markets/JamesGrugett/what-will-be-the-best-assessment-of",
|
||||
"pool":null,
|
||||
"probability":0,
|
||||
"volume7Days":100,
|
||||
"volume24Hours":100,
|
||||
"isResolved":false,
|
||||
...
|
||||
}
|
||||
```
|
||||
- Response type: Array of `LiteMarket`
|
||||
|
||||
```tsx
|
||||
// Information about a market, but without bets or comments
|
||||
type LiteMarket = {
|
||||
// Unique identifer for this market
|
||||
id: string
|
||||
|
||||
// Attributes about the creator
|
||||
creatorUsername: string
|
||||
creatorName: string
|
||||
createdTime: number
|
||||
creatorAvatarUrl?: string
|
||||
|
||||
// Market attributes. All times are in milliseconds since epoch
|
||||
closeTime?: number // Min of creator's chosen date, and resolutionTime
|
||||
question: string
|
||||
description: string
|
||||
tags: string[]
|
||||
url: string
|
||||
|
||||
pool: number
|
||||
probability: number
|
||||
volume7Days: number
|
||||
volume24Hours: number
|
||||
isResolved: boolean
|
||||
resolutionTime?: number
|
||||
resolution?: string
|
||||
}
|
||||
```
|
||||
|
||||
## Get information about one market
|
||||
|
||||
### `/v0/market/[marketId]`
|
||||
|
||||
- Example request
|
||||
|
||||
```
|
||||
https://manifold.markets/api/v0/market/3zspH9sSzMlbFQLn9GKR
|
||||
```
|
||||
|
||||
- <details><summary>Example response</summary><p>
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "3zspH9sSzMlbFQLn9GKR",
|
||||
"creatorUsername": "Austin",
|
||||
"creatorName": "Austin Chen",
|
||||
"createdTime": 1644103005345,
|
||||
"closeTime": 1667894340000,
|
||||
"question": "Will Carrick Flynn win the general election for Oregon's 6th District?",
|
||||
"description": "The Effective Altruism movement usually stays out of politics, but here is a recent, highly-upvoted endorsement of donating to Carrick Flynn as a high-impact area: https://forum.effectivealtruism.org/posts/Qi9nnrmjwNbBqWbNT/the-best-usd5-800-i-ve-ever-donated-to-pandemic-prevention\nFurther reading: https://ballotpedia.org/Oregon%27s_6th_Congressional_District_election,_2022\n\n#EffectiveAltruism #Politics",
|
||||
"tags": ["EffectiveAltruism", "Politics"],
|
||||
"url": "https://manifold.markets/Austin/will-carrick-flynn-win-the-general",
|
||||
"pool": 400.0916328426886,
|
||||
"probability": 0.34455568984059187,
|
||||
"volume7Days": 326.9083671573114,
|
||||
"volume24Hours": 0,
|
||||
"isResolved": false,
|
||||
"bets": [
|
||||
{
|
||||
"createdTime": 1644103005345,
|
||||
"isAnte": true,
|
||||
"shares": 83.66600265340756,
|
||||
"userId": "igi2zGXsfxYPgB0DJTXVJVmwCOr2",
|
||||
"amount": 70,
|
||||
"probAfter": 0.3,
|
||||
"probBefore": 0.3,
|
||||
"id": "E1MjiVYBM0GkqRXhv5cR",
|
||||
"outcome": "NO",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR"
|
||||
},
|
||||
{
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"probAfter": 0.3,
|
||||
"shares": 54.77225575051661,
|
||||
"userId": "igi2zGXsfxYPgB0DJTXVJVmwCOr2",
|
||||
"isAnte": true,
|
||||
"createdTime": 1644103005345,
|
||||
"id": "jn3iIGwD5f0vxOHxo62o",
|
||||
"amount": 30,
|
||||
"probBefore": 0.3,
|
||||
"outcome": "YES"
|
||||
},
|
||||
{
|
||||
"shares": 11.832723364874056,
|
||||
"probAfter": 0.272108843537415,
|
||||
"userId": "PkBnU8cAZiOLa0fjxiUzMKsFMYZ2",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"outcome": "NO",
|
||||
"amount": 10,
|
||||
"id": "f6sHBab6lbGw9PsnVXdc",
|
||||
"probBefore": 0.3,
|
||||
"createdTime": 1644203305863
|
||||
},
|
||||
{
|
||||
"userId": "BTksWMdCeHfDitWVaAZdjLSdu3o1",
|
||||
"amount": 10,
|
||||
"id": "Vfui2KOQwy7gkRPP7xc6",
|
||||
"shares": 18.12694184700382,
|
||||
"outcome": "YES",
|
||||
"createdTime": 1644212358699,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"probBefore": 0.272108843537415,
|
||||
"probAfter": 0.3367768595041322
|
||||
},
|
||||
{
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"probAfter": 0.3659259259259259,
|
||||
"userId": "BTksWMdCeHfDitWVaAZdjLSdu3o1",
|
||||
"probBefore": 0.3367768595041322,
|
||||
"amount": 5,
|
||||
"outcome": "YES",
|
||||
"createdTime": 1644433184238,
|
||||
"id": "eGI1VwAWF822LkcmOUot",
|
||||
"shares": 8.435122540124937
|
||||
},
|
||||
{
|
||||
"userId": "NHA7Gv9nNpb7b60GpLD3oFkBvPa2",
|
||||
"shares": 59.79133423528123,
|
||||
"amount": 50,
|
||||
"probAfter": 0.24495867768595042,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"createdTime": 1644693685223,
|
||||
"probBefore": 0.3659259259259259,
|
||||
"id": "fbU0DbmDWMnubggpQotw",
|
||||
"outcome": "NO"
|
||||
},
|
||||
{
|
||||
"amount": 25,
|
||||
"userId": "iXw2OSyhs0c4QW2fAfK3yqmaYDv1",
|
||||
"probAfter": 0.20583333333333328,
|
||||
"outcome": "NO",
|
||||
"shares": 28.3920247989266,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"createdTime": 1644695698202,
|
||||
"id": "k9hyljJD3MMXK2OYxTsR",
|
||||
"probBefore": 0.24495867768595042
|
||||
},
|
||||
{
|
||||
"createdTime": 1644716782308,
|
||||
"shares": 11.17480183821209,
|
||||
"probBefore": 0.20583333333333328,
|
||||
"userId": "clvYFhVDzccYu20OUc5NBKJyDxj2",
|
||||
"probAfter": 0.1927679500520291,
|
||||
"id": "yYkZ4JpLgZHrRQUugpCD",
|
||||
"outcome": "NO",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"amount": 10
|
||||
},
|
||||
{
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"outcome": "YES",
|
||||
"amount": 30,
|
||||
"id": "IU2Hb1DesgKIN140BkhE",
|
||||
"shares": 58.893424111838016,
|
||||
"createdTime": 1644736846538,
|
||||
"probBefore": 0.1927679500520291,
|
||||
"userId": "BTksWMdCeHfDitWVaAZdjLSdu3o1",
|
||||
"probAfter": 0.3289359861591695
|
||||
},
|
||||
{
|
||||
"isSold": true,
|
||||
"userId": "5zeWhzi9nlNNf5C9TVjshAN7QOd2",
|
||||
"createdTime": 1644751343436,
|
||||
"outcome": "NO",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"amount": 25,
|
||||
"probBefore": 0.3289359861591695,
|
||||
"id": "fkCxVH7THaDbEhyJjXVk",
|
||||
"probAfter": 0.2854194032651529,
|
||||
"shares": 30.022082866721178
|
||||
},
|
||||
{
|
||||
"probAfter": 0.2838618650900295,
|
||||
"id": "Ao05LRRMXVWw8d7LtwhL",
|
||||
"outcome": "NO",
|
||||
"probBefore": 0.2854194032651529,
|
||||
"shares": 1.1823269994736165,
|
||||
"userId": "pUF3dMs9oLNpgU2LYtFmodaoDow1",
|
||||
"amount": 1,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"createdTime": 1644768321860
|
||||
},
|
||||
{
|
||||
"id": "LJ8H8DTuK7CH9vN3u0Sd",
|
||||
"createdTime": 1644771352663,
|
||||
"shares": 113.5114039238785,
|
||||
"probAfter": 0.17510453314667793,
|
||||
"outcome": "NO",
|
||||
"amount": 100,
|
||||
"probBefore": 0.2838618650900295,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"userId": "ebX5nzwrs8V0M5UynWvbtcj7KAI2"
|
||||
},
|
||||
{
|
||||
"outcome": "YES",
|
||||
"amount": 20,
|
||||
"probBefore": 0.17510453314667793,
|
||||
"id": "TECEF9I5FqTqt6uTIsJX",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"createdTime": 1644805061501,
|
||||
"shares": 43.88281646028875,
|
||||
"userId": "lHxg3179e4amWm5LJhJoJrcWK482",
|
||||
"probAfter": 0.24160019644701852
|
||||
},
|
||||
{
|
||||
"amount": -25.908367157311375,
|
||||
"id": "G3u2EzETWOyrGo15wtiQ",
|
||||
"outcome": "NO",
|
||||
"createdTime": 1644847494264,
|
||||
"sale": {
|
||||
"betId": "fkCxVH7THaDbEhyJjXVk",
|
||||
"amount": 25.862948799445807
|
||||
},
|
||||
"probAfter": 0.26957595409437557,
|
||||
"shares": -30.022082866721178,
|
||||
"probBefore": 0.24160019644701852,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"userId": "5zeWhzi9nlNNf5C9TVjshAN7QOd2"
|
||||
},
|
||||
{
|
||||
"createdTime": 1644853733891,
|
||||
"userId": "lbTXACtCnIacKDloKfXxYkDn0zM2",
|
||||
"amount": 10,
|
||||
"id": "z443uCkbYRLZW9QdXu1u",
|
||||
"probAfter": 0.25822886066938844,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"outcome": "NO",
|
||||
"shares": 11.655141043149968,
|
||||
"probBefore": 0.26957595409437557
|
||||
},
|
||||
{
|
||||
"userId": "BTksWMdCeHfDitWVaAZdjLSdu3o1",
|
||||
"amount": 15,
|
||||
"shares": 28.311399392675895,
|
||||
"id": "axoryV664uzHZ0jzWSXR",
|
||||
"outcome": "YES",
|
||||
"probBefore": 0.25822886066938844,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"createdTime": 1644863335939,
|
||||
"probAfter": 0.3033936853512369
|
||||
},
|
||||
{
|
||||
"createdTime": 1644987330420,
|
||||
"id": "jHAYDdZRkDw3lFoDXdmm",
|
||||
"shares": 26.353902809992064,
|
||||
"userId": "BTksWMdCeHfDitWVaAZdjLSdu3o1",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"probAfter": 0.34455568984059187,
|
||||
"probBefore": 0.3033936853512369,
|
||||
"amount": 15,
|
||||
"outcome": "YES"
|
||||
}
|
||||
],
|
||||
"comments": [
|
||||
{
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"userUsername": "Celer",
|
||||
"userAvatarUrl": "https://lh3.googleusercontent.com/a/AATXAJwp0vAolZgOmT7GbzFq7mOf8lr0BFEB_LqWWfZk=s96-c",
|
||||
"userId": "NHA7Gv9nNpb7b60GpLD3oFkBvPa2",
|
||||
"text": "It's a D+3 district, and the person we're pushing is functionally an outsider. I maxed my donation, but 25%, what I bought down to, implying even odds on both the general and the primary, seems if anything optimistic.",
|
||||
"createdTime": 1644693740967,
|
||||
"id": "fbU0DbmDWMnubggpQotw",
|
||||
"betId": "fbU0DbmDWMnubggpQotw",
|
||||
"userName": "Celer"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
- Response type: A `FullMarket`
|
||||
|
||||
```tsx
|
||||
// A complete market, along with bets and comments
|
||||
type FullMarket = LiteMarket & {
|
||||
bets: Bet[]
|
||||
comments: Comment[]
|
||||
}
|
||||
|
||||
type Bet = {
|
||||
id: string
|
||||
contractId: string
|
||||
|
||||
amount: number // bet size; negative if SELL bet
|
||||
outcome: string
|
||||
shares: number // dynamic parimutuel pool weight; negative if SELL bet
|
||||
|
||||
probBefore: number
|
||||
probAfter: number
|
||||
|
||||
sale?: {
|
||||
amount: number // amount user makes from sale
|
||||
betId: string // id of bet being sold
|
||||
}
|
||||
|
||||
isSold?: boolean // true if this BUY bet has been sold
|
||||
isAnte?: boolean
|
||||
|
||||
createdTime: number
|
||||
}
|
||||
```
|
||||
|
||||
### `/v0/slug/[marketSlug]`
|
||||
|
||||
This is a convenience endpoint for getting info about a market from it slug (everything after the last slash in a market’s URL).
|
||||
|
||||
- Example request
|
||||
```
|
||||
https://manifold.markets/api/v0/slug/will-carrick-flynn-win-the-general
|
||||
```
|
||||
- Response type: A `FullMarket` ; same as above.
|
||||
|
||||
## Deprecated
|
||||
|
||||
- Our old Markets API was available at [https://us-central1-mantic-markets.cloudfunctions.net/markets](https://us-central1-mantic-markets.cloudfunctions.net/markets)
|
||||
- We don’t plan on continuing to change this, but we’ll support this endpoint until 2022-03-30
|
||||
|
||||
## Changelog
|
||||
|
||||
- 2022-02-28: Add `resolutionTime` to markets, change `closeTime` definition
|
||||
- 2022-02-19: Removed user IDs from bets
|
||||
- 2022-02-17: Released our v0 API, with `/markets`, `/market/[marketId]`, and `/slug/[slugId]`
|
52
docs/docs/binary-markets.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
# Guide to YES/NO markets
|
||||
|
||||
# Overview
|
||||
|
||||
Historically, Manifold used a special type of automated market marker based on a dynamic pari-mutuel (DPM) betting
|
||||
system. Free response and numeric markets still use this system. Binary markets created prior to March 15, 2022 used
|
||||
this system.
|
||||
|
||||
Binary markets created after March 15 use a constant-function market maker which holds constant the weighted geometric
|
||||
mean, with weights equal to the probabilities chosen by the market creator at creation. This design was inspired by
|
||||
Uniswap's CPMM and a suggestion from Manifold user Pepe.
|
||||
|
||||
# Basic facts
|
||||
|
||||
- Markets are structured around a question with a binary outcome.
|
||||
- Traders can place a bet on either YES or NO and receive shares in the outcome in return.
|
||||
- 1 YES share = M$1 if the event happens. 1 NO share = M$1 if the event does not happen.
|
||||
- Notice that 1 YES share + 1 NO share = M$1. If you ever get multiple YES and NO shares, they will cancel out and you will be left with cash.
|
||||
- When the market is resolved, you will be paid out according to your shares. If you own 100 YES shares, if the event resolves YES, you will earn M$100. (If the event resolves NO, you will earn M$0).
|
||||
- The creator of each market is responsible for resolving each market YES or NO.
|
||||
- Creators can also resolve N/A to cancel all transactions and return the money, or resolve to a particular probability (say 50%).
|
||||
|
||||
# Betting
|
||||
|
||||
- Betting on YES will increase the market’s implied probability; betting on NO will decrease the probability.
|
||||
- Manifold's automated market automatically adjusts the market probability after each trade and determines how many shares a user will get for their bet.
|
||||
- You can sell back your shares for cash. If you sell YES shares, the market probability will go down. If you sell NO shares, the probability will go up.
|
||||
- Manifold charges fees on each trade. They are baked into the number of shares you receive.
|
||||
- If you place a M$100 bet on YES when the probability is 50%, you may end up with 150 YES shares. These shares already include our fees. Notice also that when you buy, the probability goes up, so you are not getting in exactly at 200 shares or 50%.
|
||||
- Our fee schedule is currently: 13% _ (1 - post-bet probability) _ bet amount
|
||||
- The post-trade probability is what the market probability would be after your bet if there were no fees.
|
||||
- Example:
|
||||
- If you bet M$100 on NO and the resulting probability without fees would be 10%, then you pay M$100 _ 13% _ 10% = M$1.3.
|
||||
- If you bet M$100 on YES and the resulting probability without fees would be 90%, then you pay `M$100 * 13% * 10% = M$1.3`.
|
||||
- The fees are used to provide a commission to the market creator and to subsidize trading within the market.
|
||||
- The market creator’s commission is paid out only after the market is resolved.
|
||||
- No fees are levied on sales.
|
||||
|
||||
# Market creation
|
||||
|
||||
- Users can create a market on any question they want.
|
||||
- When you create a market, you must choose an initial probability and a close date (after which trading will halt).
|
||||
- You must also pay a M$ 50 market creation fee, which is used to subsidize trading on your market.
|
||||
- You will earn a commission on all bets placed in your market.
|
||||
- You are responsible for resolving your market in a timely manner. All the fees you earned as a commission will be paid out after resolution.
|
||||
|
||||
# Liquidity
|
||||
|
||||
- The liquidity in a market is the amount of capital available for traders to trade against.
|
||||
- The more liquidity, the greater incentive there is for traders to bet, the more accurate the market will be.
|
||||
- You can add liquidity to a market you are interested in to increase the incentives for traders to participate. You can think of added liquidity as a subsidy for getting your question answered.
|
||||
- You can add liquidity to any market by opening up the market info popup window located in the (...) section of the header on the market page.
|
67
docs/docs/bounties.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Bounties
|
||||
|
||||
## What are Manifold bounties?
|
||||
|
||||
From time to time, a member of our community goes above and beyond in helping Manifold make prediction markets accessible & ubiquitous. We’d like to recognize such contributions publicly, and include a token of our appreciation in the form of M$!
|
||||
|
||||
Examples of community work that may be eligible for a bounty:
|
||||
|
||||
- Blog posts, markets, or comments which lead us to significantly change our views
|
||||
- A track record of creating markets that help people make better decisions
|
||||
- Promoting Manifold & forecasting to a wider audience
|
||||
- Identifying serious exploits with our financial infrastructure
|
||||
|
||||
Our community is the beating heart of Manifold; your individual contributions are what make this platform valuable at all. Thanks to everyone listed here (as well as countless unnamed others) for your help & support!
|
||||
|
||||
## Awarded bounties
|
||||
|
||||
🥧 *Awarded 2022-03-14*
|
||||
|
||||
**[Kevin Zielnicki](https://manifold.markets/kjz): M$ 10,000**
|
||||
|
||||
- For identifying issues with our Dynamic Parimutuel Market Maker in an [excellent blog post](https://kevin.zielnicki.com/2022/02/17/manifold/) (and [associated market](https://manifold.markets/kjz/will-manifolds-developers-agree-wit)), leading us to change to a different mechanism.
|
||||
|
||||
**[Pepe](https://manifold.markets/Pepe): M$ 10,000**
|
||||
|
||||
- For developing the function used in our Constant Function Market Maker and working with us to polish it on Discord, making it easier for us to provision liquidity compared to a CPMM.
|
||||
|
||||
**[Gurkenglas](https://manifold.markets/Gurkenglas): M$ 5,000**
|
||||
|
||||
- For concrete suggestions on Discord around improving our market maker algorithms, and creating useful graphs to make our different market makers more legible.
|
||||
|
||||
**[Scott Alexander](https://manifold.markets/ScottAlexander): M$ 5,000**
|
||||
|
||||
- For [developing and publicizing the idea of providing interest-free loans on each market](https://astralcodexten.substack.com/p/play-money-and-reputation-systems), helping make long-term markets more accurate.
|
||||
|
||||
**[David Glidden](https://manifold.markets/dglid): M$ 5,000**
|
||||
|
||||
- For taking on the mantle of [@MetaculusBot](https://manifold.markets/MetaculusBot), which allows traders access to a wider spread of topics, and permits head-to-head comparisons between our prediction markets and other forecasting platforms.
|
||||
|
||||
**[Isaac King](https://manifold.markets/IsaacKing): M$ 5,000**
|
||||
|
||||
- For [compiling a comprehensive FAQ](https://outsidetheasylum.blog/manifold-markets-faq/) that answers a variety of questions that new users commonly face, and also inspiring us to move to [this open-source docs platform](http://docs.manifold.markets/).
|
||||
|
||||
**[Blazer](https://manifold.markets/BlazingDarkness): M$ 2,500**
|
||||
|
||||
- For [calling out our mistake](https://manifold.markets/BlazingDarkness/was-it-an-unpleasant-surprise-when) in retroactively publicizing all market creator’s trades, leading us to revert this feature entirely.
|
||||
|
||||
⛑️ _Awarded 2022-01-09_
|
||||
|
||||
**[Duncan](https://manifold.markets/Duncan): USD $50**
|
||||
|
||||
- For identifying and confidentially reporting an exploit where entering negative numbers into the trade box would allow the trade to go through.
|
||||
- _Note: this was denominated in USD, as it predated the creation of our bounty program._
|
||||
|
||||
## Final note
|
||||
|
||||
If a particular contribution isn't listed here, that doesn't mean we didn't really appreciate it. There’s so much great work by our community; we aren't always able to catch them all!
|
||||
|
||||
If you feel that someone's exceptional contribution has fallen through the cracks (including your own!), please consider creating a market for “Will <X\> be recognized for a Manifold bounty?” and posting it on our Discord. Thanks!
|
||||
|
||||
## See also
|
||||
|
||||
- [Will Manifold implement retroactive public goods funding by June 1?](https://manifold.markets/Austin/will-manifold-implement-retroactive)
|
||||
- [Bounties as described on LessWrong](https://www.lesswrong.com/tag/bounties-active)
|
||||
- Mistakes pages we admire: [Scott Alexander](https://astralcodexten.substack.com/p/mistakes), [Nintil](https://nintil.com/mistakes), [80K Hours](https://80000hours.org/about/credibility/evaluations/mistakes/)
|
||||
- [Donald Knuth’s reward checks](https://en.wikipedia.org/wiki/Knuth_reward_check)
|
||||
![https://imgs.xkcd.com/comics/applied_math.png](https://imgs.xkcd.com/comics/applied_math.png)
|
124
docs/docs/faq.md
Normal file
|
@ -0,0 +1,124 @@
|
|||
# Community FAQ
|
||||
|
||||
## General
|
||||
|
||||
### Do I have to pay real money in order to participate?
|
||||
|
||||
Nope! Each account starts with a free M$ 1000. If you invest it wisely, you can increase your total without ever needing to put any real money into the site.
|
||||
|
||||
### What is the name for the currency Manifold uses, represented by M$?
|
||||
|
||||
Manifold Dollars, or mana for short.
|
||||
|
||||
### Can M$ be sold for real money?
|
||||
|
||||
No. Gambling laws put many restrictions on real-money prediction markets, so Manifold uses play money instead.
|
||||
|
||||
### How do the free response markets work?
|
||||
|
||||
Any user can enter a response and bet on it, or they can bet on on other people's responses. The response probabilities are weighted proportionally to how many people have bet on them. The market creator's ante goes into a "none of the above" pseudo-option that can't be bet on and can't be chosen as a correct answer when the market is resolved. (This means that free response markets tend to lose their creator almost their entire ante, whereas normal markets only lose them a small fraction that's proportional to how well they chose their starting odds. It also means that if there are only a finite number of options that could win, traders can make guaranteed money by investing in them all equally.) See [here](https://manifoldmarkets.substack.com/p/above-the-fold-milestones-and-new) for more information.
|
||||
|
||||
### How accurate are the market probabilities?
|
||||
|
||||
In general, prediction markets are very accurate. They do have some known issues, most of which can be found on the [Wikipedia page.](https://en.wikipedia.org/wiki/Prediction_market#Accuracy). There are also a few factors that are specific to Manifold Markets:
|
||||
|
||||
- Manifold uses play money for their markets, so there's less of an incentive for people to invest safely. People often goof around with silly markets and investments that they don't expect to win M$ from.
|
||||
- Anyone can create a market on Manifold, and there's nothing preventing the creator of a market from trying to manipulate it to make a profit.
|
||||
- Manifold Markets is a new project and has a large number of individual markets, which means that many of their markets don't have many participants, sometimes less than 5 people.
|
||||
- Manifold's betting system isn't perfect and has some sources of error, discussed in detail [here](https://kevin.zielnicki.com/2022/02/17/manifold/).
|
||||
|
||||
As a general heuristic, check the total pool for the market in question. The more M$ there is in the market, the more likely it is to be accurate.
|
||||
|
||||
### Can I participate without having a Google account?
|
||||
|
||||
No. See [here](https://manifold.markets/hamnox/will-manifold-markets-add-nongoogle) for the probability that this changes.
|
||||
|
||||
## Placing and winning bets
|
||||
|
||||
### The payout probabilities I'm shown sometimes aren't right. For example if a market is at 15% and I bet M$ 1 on "no", it tells me that I'll make a 42% profit if I win, but the listed payout is just M$ 1. What's going on?
|
||||
|
||||
Payout amounts are visually rounded to the nearest M$ 1, and only integer amounts can be put into markets. Behind the scenes however, your balance does track fractional amounts, so you're making a M$ 0.42 profit on that bet. Once you win another M$ 0.08, that fractional M$ 0.5 will display as an extra M$ 1 in your account. (There's no way to view your exact balance, you can only see the rounded value.)
|
||||
|
||||
### What are the rules about insider trading? (Using private information about a market to make a profit.)
|
||||
|
||||
It's not only allowed, but encouraged. The whole point of a prediction market is to uncover and amplify this sort of hidden information. For example, if there's a market like "will [company] make [decision]?" and you work for that company and know what decision they're going to make, you can use that information to win M$ and make the market more accurate at the same time. (Subject to your company's policies on disclosing internal information of course.) However, if the reason you have private information is because you're colluding with the market creator, this will likely earn both of you a bad reputation and people will be less interested in participating in your markets in the future.
|
||||
|
||||
### Can I see who is buying/selling in a market?
|
||||
|
||||
Trading is anonymous by default. You'll only see their username if they leave a comment. As an exception, trading from the market's creator has their name attached.
|
||||
|
||||
## Creating and resolving markets
|
||||
|
||||
### Is there any benefit to creating markets?
|
||||
|
||||
You get your question answered! Plus, you earn a commission on trades in your markets.
|
||||
|
||||
### What can I create a market about?
|
||||
|
||||
Anything you want to! People ask about politics, science, gaming, and even [their personal lives](https://www.smbc-comics.com/?id=2418). Take a look at the [current list of markets](https://manifold.markets/markets) to see what sorts of things people ask about.
|
||||
|
||||
### What's the difference between a market being "closed" and being "resolved"?
|
||||
|
||||
A market being "closed" means that people can no longer place or sell bets, "locking in" the current probability. Markets close when the close date of the market is met. A market being "resolved" means that the market creator has indicated a given resolution to the market's question, such as "yes", "no", "N/A", or a certain probability. This is the point at which people are cashed out of the market. Resolving a market automatically closes it, but a market can close days, weeks, or even years before it gets resolved.
|
||||
|
||||
### What does "PROB" mean?
|
||||
|
||||
Resolving a market as "PROB" means that it's resolved at a certain probability, chosen by the market creator. PROB 100% is the same as "yes", and PROB 0% is the same as "no". For example, if a market is resolved at PROB 75%, anyone who bought "yes" at less than 75% will (usually) make a profit, and anyone who bought "yes" at greater than 75% will (usually) take a loss. Vice versa for "no".
|
||||
|
||||
### What happens if a market creator resolves a market incorrectly, or doesn't resolve it at all?
|
||||
|
||||
Nothing. The idea is for Manifold Markets to function with similar freedom and versatility to a Twitter poll, but with more accurate results due to the dynamics of prediction markets. Individual market resolution is not enforced by the site, so if you don't trust a certain user to judge their markets fairly, you probably shouldn't participate in their markets.
|
||||
|
||||
### How do I tell if a certain market creator is trustworthy?
|
||||
|
||||
Look at their market resolution history on their profile page. If their past markets have all been resolved correctly, their future ones probably will be too. You can also look at the comments on those markets to see if any traders noticed anything suspicious. You can also ask about that person in the [Manifold Markets Discord](https://discord.gg/eHQBNBqXuh). And if their profile links to their website or social media pages, you can take that into account too.
|
||||
|
||||
### Are there any content filters? What happens if someone creates an inappropriate, offensive, or [dangerous](https://en.wikipedia.org/wiki/Assassination_market) market?
|
||||
|
||||
Right now, there are no restrictions on what markets can be created. If this becomes a problem, they may change their policies.
|
||||
|
||||
### Can a market creator change the close date of their market?
|
||||
|
||||
Yes. As long as the market hasn't been resolved yet, the creator can freely change its close date. They can even reopen a market that has already closed.
|
||||
|
||||
### Is there a way to see my closed markets that I need to resolve?
|
||||
|
||||
You'll get an automated email when they close. You can also go to your profile page and select "closed" in the dropdown menu. (This will display only markets that you haven't resolved yet.)
|
||||
|
||||
### When do market creators get their commission fees?
|
||||
|
||||
When the creator resolves their market, they get the commission from all the trades that were exectuted in the market.
|
||||
|
||||
### How do I see markets that are currently open?
|
||||
|
||||
You can see the top 99 markets in various categories [here](https://manifold.markets/markets).
|
||||
|
||||
### Can I bet in a market I created?
|
||||
|
||||
Yes. However if you're doing things that the community would perceive as "shady", such as put all your money on the correct resolution immediately before closing the market, people may be more reluctant to participate in your markets in the future. Betting "normally" in your own market is fine though.
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
### How do I report bugs or ask for new features?
|
||||
|
||||
Contact them via [email](mailto:info@manifold.markets), post in their [Discord](https://discord.gg/eHQBNBqXuh), or create a market about that bug/feature in order to draw more attention to it and get community input.
|
||||
|
||||
### How can I get notified of new developments?
|
||||
|
||||
Being a very recent project, Manifold is adding new features and tweaking existing ones quite frequently. You can keep up with changes by subscribing to their [Substack](https://manifoldmarkets.substack.com/), or joining their [Discord server](https://discord.gg/eHQBNBqXuh).
|
||||
|
||||
### Is there an app?
|
||||
|
||||
No, but the website is designed responsively and looks great on mobile.
|
||||
|
||||
### Does Manifold have an API for programmers?
|
||||
|
||||
Yep. Documentation is [here](https://www.notion.so/Manifold-Markets-API-5e7d0aef4dcf452bb04b319e178fabc5).
|
||||
|
||||
### If I have a question that isn't answered here, where can I ask it?
|
||||
|
||||
You can contact Manifold Markets via [email](mailto:info@manifold.markets) or post in their [Discord](https://discord.gg/eHQBNBqXuh). Once you have an answer, please consider updating this FAQ via "Edit this page" on Github!
|
||||
|
||||
## Credits
|
||||
|
||||
This FAQ was originally compiled by [Isaac King](https://outsidetheasylum.blog/manifold-markets-faq/).
|
133
docs/docusaurus.config.js
Normal file
|
@ -0,0 +1,133 @@
|
|||
// @ts-check
|
||||
// Note: type annotations allow type checking and IDEs autocompletion
|
||||
|
||||
const lightCodeTheme = require('prism-react-renderer/themes/github')
|
||||
const darkCodeTheme = require('prism-react-renderer/themes/dracula')
|
||||
const math = require('remark-math')
|
||||
const katex = require('rehype-katex')
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
title: 'Manifold Docs',
|
||||
tagline: 'Learn more about the BESTEST prediction market platform~',
|
||||
url: 'https://docs.manifold.markets',
|
||||
baseUrl: '/',
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
favicon: 'https://manifold.markets/favicon.ico',
|
||||
organizationName: 'manifoldmarkets', // Usually your GitHub org/user name.
|
||||
projectName: 'docs', // Usually your repo name.
|
||||
|
||||
presets: [
|
||||
[
|
||||
'classic',
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: {
|
||||
routeBasePath: '/',
|
||||
sidebarPath: require.resolve('./sidebars.js'),
|
||||
// Please change this to your repo.
|
||||
editUrl: 'https://github.com/manifoldmarkets/docs/tree/main/',
|
||||
remarkPlugins: [math],
|
||||
rehypePlugins: [katex],
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
],
|
||||
|
||||
stylesheets: [
|
||||
{
|
||||
href: 'https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css',
|
||||
type: 'text/css',
|
||||
integrity:
|
||||
'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM',
|
||||
crossorigin: 'anonymous',
|
||||
},
|
||||
],
|
||||
|
||||
scripts: [
|
||||
{
|
||||
src: 'https://cdn.jsdelivr.net/npm/link-summoner@1.0.2/dist/browser.min.js',
|
||||
async: 'true',
|
||||
},
|
||||
],
|
||||
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
navbar: {
|
||||
title: 'Manifold Docs',
|
||||
logo: {
|
||||
alt: 'Manifold Markets Logo',
|
||||
src: 'https://manifold.markets/logo.svg',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'doc',
|
||||
docId: 'about',
|
||||
position: 'left',
|
||||
label: 'Docs',
|
||||
},
|
||||
{
|
||||
href: 'https://github.com/manifoldmarkets/docs',
|
||||
label: 'GitHub',
|
||||
position: 'right',
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
style: 'dark',
|
||||
links: [
|
||||
{
|
||||
title: 'Manifold',
|
||||
items: [
|
||||
{
|
||||
label: 'Manifold Markets',
|
||||
to: 'https://manifold.markets',
|
||||
},
|
||||
{
|
||||
label: 'Docs',
|
||||
to: '/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Community',
|
||||
items: [
|
||||
{
|
||||
label: 'Discord',
|
||||
href: 'https://discord.gg/eHQBNBqXuh',
|
||||
},
|
||||
{
|
||||
label: 'Twitter',
|
||||
href: 'https://twitter.com/manifoldmarkets',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'More',
|
||||
items: [
|
||||
{
|
||||
label: 'Blog',
|
||||
to: 'https://manifoldmarkets.substack.com',
|
||||
},
|
||||
{
|
||||
label: 'GitHub',
|
||||
href: 'https://github.com/manifoldmarkets/docs',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Manifold Markets, Inc. Built with Docusaurus.`,
|
||||
},
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
module.exports = config
|
47
docs/package.json
Normal file
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "docs",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
"dev": "yarn start",
|
||||
"start": "docusaurus start",
|
||||
"build": "docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids",
|
||||
"typecheck": "tsc",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "2.0.0-beta.17",
|
||||
"@docusaurus/preset-classic": "2.0.0-beta.17",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.1.1",
|
||||
"hast-util-is-element": "1.1.0",
|
||||
"prism-react-renderer": "^1.2.1",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"rehype-katex": "5",
|
||||
"remark-math": "3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.0.0-beta.17",
|
||||
"@tsconfig/docusaurus": "^1.0.4"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.5%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
31
docs/sidebars.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Creating a sidebar enables you to:
|
||||
- create an ordered group of docs
|
||||
- render a sidebar for each doc of that group
|
||||
- provide next/previous navigation
|
||||
|
||||
The sidebars can be generated from the filesystem, or explicitly defined here.
|
||||
|
||||
Create as many sidebars as you want.
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||
const sidebars = {
|
||||
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||
tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }],
|
||||
|
||||
// But you can create a sidebar manually
|
||||
/*
|
||||
tutorialSidebar: [
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Tutorial',
|
||||
items: ['hello'],
|
||||
},
|
||||
],
|
||||
*/
|
||||
}
|
||||
|
||||
module.exports = sidebars
|
70
docs/src/components/HomepageFeatures/index.tsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import styles from './styles.module.css'
|
||||
|
||||
type FeatureItem = {
|
||||
title: string
|
||||
Svg: React.ComponentType<React.ComponentProps<'svg'>>
|
||||
description: JSX.Element
|
||||
}
|
||||
|
||||
const FeatureList: FeatureItem[] = [
|
||||
{
|
||||
title: 'Easy to Use',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Docusaurus was designed from the ground up to be easily installed and
|
||||
used to get your website up and running quickly.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Focus on What Matters',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Docusaurus lets you focus on your docs, and we'll do the chores. Go
|
||||
ahead and move your docs into the <code>docs</code> directory.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Powered by React',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Extend or customize your website layout by reusing React. Docusaurus can
|
||||
be extended while reusing the same header and footer.
|
||||
</>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
function Feature({ title, Svg, description }: FeatureItem) {
|
||||
return (
|
||||
<div className={clsx('col col--4')}>
|
||||
<div className="text--center">
|
||||
<Svg className={styles.featureSvg} role="img" />
|
||||
</div>
|
||||
<div className="text--center padding-horiz--md">
|
||||
<h3>{title}</h3>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function HomepageFeatures(): JSX.Element {
|
||||
return (
|
||||
<section className={styles.features}>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{FeatureList.map((props, idx) => (
|
||||
<Feature key={idx} {...props} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
11
docs/src/components/HomepageFeatures/styles.module.css
Normal file
|
@ -0,0 +1,11 @@
|
|||
.features {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.featureSvg {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
44
docs/src/css/custom.css
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Any CSS included here will be global. The classic template
|
||||
* bundles Infima by default. Infima is a CSS framework designed to
|
||||
* work well for content-centric websites.
|
||||
*/
|
||||
|
||||
article {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* You can override the default Infima variables here. */
|
||||
:root {
|
||||
--ifm-color-primary: #2e8555;
|
||||
--ifm-color-primary-dark: #29784c;
|
||||
--ifm-color-primary-darker: #277148;
|
||||
--ifm-color-primary-darkest: #205d3b;
|
||||
--ifm-color-primary-light: #33925d;
|
||||
--ifm-color-primary-lighter: #359962;
|
||||
--ifm-color-primary-lightest: #3cad6e;
|
||||
--ifm-code-font-size: 95%;
|
||||
}
|
||||
|
||||
/* For readability concerns, you should choose a lighter palette in dark mode. */
|
||||
[data-theme='dark'] {
|
||||
--ifm-color-primary: #25c2a0;
|
||||
--ifm-color-primary-dark: #21af90;
|
||||
--ifm-color-primary-darker: #1fa588;
|
||||
--ifm-color-primary-darkest: #1a8870;
|
||||
--ifm-color-primary-light: #29d5b0;
|
||||
--ifm-color-primary-lighter: #32d8b4;
|
||||
--ifm-color-primary-lightest: #4fddbf;
|
||||
}
|
||||
|
||||
.docusaurus-highlight-code-line {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
display: block;
|
||||
margin: 0 calc(-1 * var(--ifm-pre-padding));
|
||||
padding: 0 var(--ifm-pre-padding);
|
||||
}
|
||||
|
||||
[data-theme='dark'] .docusaurus-highlight-code-line {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
7
docs/src/pages/markdown-page.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: Markdown page example
|
||||
---
|
||||
|
||||
# Markdown page example
|
||||
|
||||
You don't need React to write simple standalone pages.
|
0
docs/static/.nojekyll
vendored
Normal file
BIN
docs/static/img/docusaurus.png
vendored
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
docs/static/img/favicon.ico
vendored
Normal file
After Width: | Height: | Size: 3.5 KiB |
1
docs/static/img/logo.svg
vendored
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
docs/static/img/tutorial/docsVersionDropdown.png
vendored
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
docs/static/img/tutorial/localeDropdown.png
vendored
Normal file
After Width: | Height: | Size: 29 KiB |
171
docs/static/img/undraw_docusaurus_mountain.svg
vendored
Normal file
|
@ -0,0 +1,171 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1088" height="687.962" viewBox="0 0 1088 687.962">
|
||||
<title>Easy to Use</title>
|
||||
<g id="Group_12" data-name="Group 12" transform="translate(-57 -56)">
|
||||
<g id="Group_11" data-name="Group 11" transform="translate(57 56)">
|
||||
<path id="Path_83" data-name="Path 83" d="M1017.81,560.461c-5.27,45.15-16.22,81.4-31.25,110.31-20,38.52-54.21,54.04-84.77,70.28a193.275,193.275,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.282,657.282,0,0,0-104.09-13.16q-14.97-.675-29.97-.67c-15.42.02-293.07,5.29-360.67-131.57-16.69-33.76-28.13-75-32.24-125.27-11.63-142.12,52.29-235.46,134.74-296.47,155.97-115.41,369.76-110.57,523.43,7.88C941.15,276.621,1036.99,396.031,1017.81,560.461Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_84" data-name="Path 84" d="M986.56,670.771c-20,38.52-47.21,64.04-77.77,80.28a193.272,193.272,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.3,657.3,0,0,0-104.09-13.16q-14.97-.675-29.97-.67-23.13.03-46.25,1.72c-100.17,7.36-253.82-6.43-321.42-143.29L382,283.981,444.95,445.6l20.09,51.59,55.37-75.98L549,381.981l130.2,149.27,36.8-81.27L970.78,657.9l14.21,11.59Z" transform="translate(-56 -106.019)" fill="#f2f2f2"/>
|
||||
<path id="Path_85" data-name="Path 85" d="M302,282.962l26-57,36,83-31-60Z" opacity="0.1"/>
|
||||
<path id="Path_86" data-name="Path 86" d="M610.5,753.821q-14.97-.675-29.97-.67L465.04,497.191Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<path id="Path_87" data-name="Path 87" d="M464.411,315.191,493,292.962l130,150-132-128Z" opacity="0.1"/>
|
||||
<path id="Path_88" data-name="Path 88" d="M908.79,751.051a193.265,193.265,0,0,1-27.46,11.94L679.2,531.251Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<circle id="Ellipse_11" data-name="Ellipse 11" cx="3" cy="3" r="3" transform="translate(479 98.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_12" data-name="Ellipse 12" cx="3" cy="3" r="3" transform="translate(396 201.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_13" data-name="Ellipse 13" cx="2" cy="2" r="2" transform="translate(600 220.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_14" data-name="Ellipse 14" cx="2" cy="2" r="2" transform="translate(180 265.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_15" data-name="Ellipse 15" cx="2" cy="2" r="2" transform="translate(612 96.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_16" data-name="Ellipse 16" cx="2" cy="2" r="2" transform="translate(736 192.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_17" data-name="Ellipse 17" cx="2" cy="2" r="2" transform="translate(858 344.962)" fill="#f2f2f2"/>
|
||||
<path id="Path_89" data-name="Path 89" d="M306,121.222h-2.76v-2.76h-1.48v2.76H299V122.7h2.76v2.759h1.48V122.7H306Z" fill="#f2f2f2"/>
|
||||
<path id="Path_90" data-name="Path 90" d="M848,424.222h-2.76v-2.76h-1.48v2.76H841V425.7h2.76v2.759h1.48V425.7H848Z" fill="#f2f2f2"/>
|
||||
<path id="Path_91" data-name="Path 91" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_92" data-name="Path 92" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<ellipse id="Ellipse_18" data-name="Ellipse 18" cx="544" cy="30" rx="544" ry="30" transform="translate(0 583.962)" fill="#3f3d56"/>
|
||||
<path id="Path_93" data-name="Path 93" d="M624,677.981c0,33.137-14.775,24-33,24s-33,9.137-33-24,33-96,33-96S624,644.844,624,677.981Z" transform="translate(-56 -106.019)" fill="#ff6584"/>
|
||||
<path id="Path_94" data-name="Path 94" d="M606,690.66c0,15.062-6.716,10.909-15,10.909s-15,4.153-15-10.909,15-43.636,15-43.636S606,675.6,606,690.66Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<rect id="Rectangle_97" data-name="Rectangle 97" width="92" height="18" rx="9" transform="translate(489 604.962)" fill="#2f2e41"/>
|
||||
<rect id="Rectangle_98" data-name="Rectangle 98" width="92" height="18" rx="9" transform="translate(489 586.962)" fill="#2f2e41"/>
|
||||
<path id="Path_95" data-name="Path 95" d="M193,596.547c0,55.343,34.719,100.126,77.626,100.126" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_96" data-name="Path 96" d="M270.626,696.673c0-55.965,38.745-101.251,86.626-101.251" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_97" data-name="Path 97" d="M221.125,601.564c0,52.57,22.14,95.109,49.5,95.109" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_98" data-name="Path 98" d="M270.626,696.673c0-71.511,44.783-129.377,100.126-129.377" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_99" data-name="Path 99" d="M254.3,697.379s11.009-.339,14.326-2.7,16.934-5.183,17.757-1.395,16.544,18.844,4.115,18.945-28.879-1.936-32.19-3.953S254.3,697.379,254.3,697.379Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_100" data-name="Path 100" d="M290.716,710.909c-12.429.1-28.879-1.936-32.19-3.953-2.522-1.536-3.527-7.048-3.863-9.591l-.368.014s.7,8.879,4.009,10.9,19.761,4.053,32.19,3.953c3.588-.029,4.827-1.305,4.759-3.2C294.755,710.174,293.386,710.887,290.716,710.909Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_101" data-name="Path 101" d="M777.429,633.081c0,38.029,23.857,68.8,53.341,68.8" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_102" data-name="Path 102" d="M830.769,701.882c0-38.456,26.623-69.575,59.525-69.575" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_103" data-name="Path 103" d="M796.755,636.528c0,36.124,15.213,65.354,34.014,65.354" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_104" data-name="Path 104" d="M830.769,701.882c0-49.139,30.773-88.9,68.8-88.9" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_105" data-name="Path 105" d="M819.548,702.367s7.565-.233,9.844-1.856,11.636-3.562,12.2-.958,11.368,12.949,2.828,13.018-19.844-1.33-22.119-2.716S819.548,702.367,819.548,702.367Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_106" data-name="Path 106" d="M844.574,711.664c-8.54.069-19.844-1.33-22.119-2.716-1.733-1.056-2.423-4.843-2.654-6.59l-.253.01s.479,6.1,2.755,7.487,13.579,2.785,22.119,2.716c2.465-.02,3.317-.9,3.27-2.2C847.349,711.159,846.409,711.649,844.574,711.664Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_107" data-name="Path 107" d="M949.813,724.718s11.36-1.729,14.5-4.591,16.89-7.488,18.217-3.667,19.494,17.447,6.633,19.107-30.153,1.609-33.835-.065S949.813,724.718,949.813,724.718Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_108" data-name="Path 108" d="M989.228,734.173c-12.86,1.659-30.153,1.609-33.835-.065-2.8-1.275-4.535-6.858-5.2-9.45l-.379.061s1.833,9.109,5.516,10.783,20.975,1.725,33.835.065c3.712-.479,4.836-1.956,4.529-3.906C993.319,732.907,991.991,733.817,989.228,734.173Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_109" data-name="Path 109" d="M670.26,723.9s9.587-1.459,12.237-3.875,14.255-6.32,15.374-3.095,16.452,14.725,5.6,16.125-25.448,1.358-28.555-.055S670.26,723.9,670.26,723.9Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_110" data-name="Path 110" d="M703.524,731.875c-10.853,1.4-25.448,1.358-28.555-.055-2.367-1.076-3.827-5.788-4.39-7.976l-.32.051s1.547,7.687,4.655,9.1,17.7,1.456,28.555.055c3.133-.4,4.081-1.651,3.822-3.3C706.977,730.807,705.856,731.575,703.524,731.875Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_111" data-name="Path 111" d="M178.389,719.109s7.463-1.136,9.527-3.016,11.1-4.92,11.969-2.409,12.808,11.463,4.358,12.553-19.811,1.057-22.23-.043S178.389,719.109,178.389,719.109Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_112" data-name="Path 112" d="M204.285,725.321c-8.449,1.09-19.811,1.057-22.23-.043-1.842-.838-2.979-4.506-3.417-6.209l-.249.04s1.2,5.984,3.624,7.085,13.781,1.133,22.23.043c2.439-.315,3.177-1.285,2.976-2.566C206.973,724.489,206.1,725.087,204.285,725.321Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_113" data-name="Path 113" d="M439.7,707.337c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873,42.118-36.793,93.694-36.793S439.7,677.117,439.7,707.337Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<path id="Path_114" data-name="Path 114" d="M439.7,699.9c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873S295.04,663.1,346.616,663.1,439.7,669.676,439.7,699.9Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
</g>
|
||||
<g id="docusaurus_keytar" transform="translate(312.271 493.733)">
|
||||
<path id="Path_40" data-name="Path 40" d="M99,52h91.791V89.153H99Z" transform="translate(5.904 -14.001)" fill="#fff" fill-rule="evenodd"/>
|
||||
<path id="Path_41" data-name="Path 41" d="M24.855,163.927A21.828,21.828,0,0,1,5.947,153a21.829,21.829,0,0,0,18.908,32.782H46.71V163.927Z" transform="translate(-3 -4.634)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_42" data-name="Path 42" d="M121.861,61.1l76.514-4.782V45.39A21.854,21.854,0,0,0,176.52,23.535H78.173L75.441,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L64.513,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L53.586,18.8a3.154,3.154,0,0,0-5.464,0L45.39,23.535c-.024,0-.046,0-.071,0l-4.526-4.525a3.153,3.153,0,0,0-5.276,1.414l-1.5,5.577-5.674-1.521a3.154,3.154,0,0,0-3.863,3.864L26,34.023l-5.575,1.494a3.155,3.155,0,0,0-1.416,5.278l4.526,4.526c0,.023,0,.046,0,.07L18.8,48.122a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,59.05a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,69.977a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,80.9a3.154,3.154,0,0,0,0,5.464L23.535,89.1,18.8,91.832a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,102.76a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,113.687a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,124.615a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,135.542a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,146.469a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,157.4a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,168.324a3.154,3.154,0,0,0,0,5.464l4.732,2.732A21.854,21.854,0,0,0,45.39,198.375H176.52a21.854,21.854,0,0,0,21.855-21.855V89.1l-76.514-4.782a11.632,11.632,0,0,1,0-23.219" transform="translate(-1.681 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_43" data-name="Path 43" d="M143,186.71h32.782V143H143Z" transform="translate(9.984 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_44" data-name="Path 44" d="M196.71,159.855a5.438,5.438,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(10.912 -6.025)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_45" data-name="Path 45" d="M153,124.855h32.782V103H153Z" transform="translate(10.912 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_46" data-name="Path 46" d="M194.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.814,2.814,0,0,0,.349.035" transform="translate(12.767 -9.377)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_47" data-name="Path 47" d="M65.087,56.891a2.732,2.732,0,0,1-2.732-2.732,8.2,8.2,0,0,0-16.391,0,2.732,2.732,0,0,1-5.464,0,13.659,13.659,0,0,1,27.319,0,2.732,2.732,0,0,1-2.732,2.732" transform="translate(0.478 -15.068)" fill-rule="evenodd"/>
|
||||
<path id="Path_48" data-name="Path 48" d="M103,191.347h65.565a21.854,21.854,0,0,0,21.855-21.855V93H124.855A21.854,21.854,0,0,0,103,114.855Z" transform="translate(6.275 -10.199)" fill="#ffff50" fill-rule="evenodd"/>
|
||||
<path id="Path_49" data-name="Path 49" d="M173.216,129.787H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0-54.434H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.652H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186M189.585,61.611c-.013,0-.024-.007-.037-.005-3.377.115-4.974,3.492-6.384,6.472-1.471,3.114-2.608,5.139-4.473,5.078-2.064-.074-3.244-2.406-4.494-4.874-1.436-2.835-3.075-6.049-6.516-5.929-3.329.114-4.932,3.053-6.346,5.646-1.5,2.762-2.529,4.442-4.5,4.364-2.106-.076-3.225-1.972-4.52-4.167-1.444-2.443-3.112-5.191-6.487-5.1-3.272.113-4.879,2.606-6.3,4.808-1.5,2.328-2.552,3.746-4.551,3.662-2.156-.076-3.27-1.65-4.558-3.472-1.447-2.047-3.077-4.363-6.442-4.251-3.2.109-4.807,2.153-6.224,3.954-1.346,1.709-2.4,3.062-4.621,2.977a1.093,1.093,0,0,0-.079,2.186c3.3.11,4.967-1.967,6.417-3.81,1.286-1.635,2.4-3.045,4.582-3.12,2.1-.09,3.091,1.218,4.584,3.327,1.417,2,3.026,4.277,6.263,4.394,3.391.114,5.022-2.42,6.467-4.663,1.292-2,2.406-3.734,4.535-3.807,1.959-.073,3.026,1.475,4.529,4.022,1.417,2.4,3.023,5.121,6.324,5.241,3.415.118,5.064-2.863,6.5-5.5,1.245-2.282,2.419-4.437,4.5-4.509,1.959-.046,2.981,1.743,4.492,4.732,1.412,2.79,3.013,5.95,6.365,6.071l.185,0c3.348,0,4.937-3.36,6.343-6.331,1.245-2.634,2.423-5.114,4.444-5.216Z" transform="translate(7.109 -13.11)" fill-rule="evenodd"/>
|
||||
<path id="Path_50" data-name="Path 50" d="M83,186.71h43.71V143H83Z" transform="translate(4.42 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 109.327, 91.085)">
|
||||
<rect id="Rectangle_3" data-name="Rectangle 3" width="92.361" height="36.462" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
|
||||
<g id="Group_2" data-name="Group 2" transform="translate(1.531 23.03)">
|
||||
<rect id="Rectangle_4" data-name="Rectangle 4" width="5.336" height="5.336" rx="1" transform="translate(16.797 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_5" data-name="Rectangle 5" width="5.336" height="5.336" rx="1" transform="translate(23.12 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_6" data-name="Rectangle 6" width="5.336" height="5.336" rx="1" transform="translate(29.444 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_7" data-name="Rectangle 7" width="5.336" height="5.336" rx="1" transform="translate(35.768 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_8" data-name="Rectangle 8" width="5.336" height="5.336" rx="1" transform="translate(42.091 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_9" data-name="Rectangle 9" width="5.336" height="5.336" rx="1" transform="translate(48.415 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_10" data-name="Rectangle 10" width="5.336" height="5.336" rx="1" transform="translate(54.739 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_11" data-name="Rectangle 11" width="5.336" height="5.336" rx="1" transform="translate(61.063 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_12" data-name="Rectangle 12" width="5.336" height="5.336" rx="1" transform="translate(67.386 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_51" data-name="Path 51" d="M1.093,0H14.518a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0ZM75,0H88.426a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H75a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,75,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_3" data-name="Group 3" transform="translate(1.531 10.261)">
|
||||
<path id="Path_52" data-name="Path 52" d="M1.093,0H6.218A1.093,1.093,0,0,1,7.31,1.093V4.242A1.093,1.093,0,0,1,6.218,5.335H1.093A1.093,1.093,0,0,1,0,4.242V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_13" data-name="Rectangle 13" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_14" data-name="Rectangle 14" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_15" data-name="Rectangle 15" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_16" data-name="Rectangle 16" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_17" data-name="Rectangle 17" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_18" data-name="Rectangle 18" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_19" data-name="Rectangle 19" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_20" data-name="Rectangle 20" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_21" data-name="Rectangle 21" width="5.336" height="5.336" rx="1" transform="translate(58.888 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_22" data-name="Rectangle 22" width="5.336" height="5.336" rx="1" transform="translate(65.212 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_23" data-name="Rectangle 23" width="5.336" height="5.336" rx="1" transform="translate(71.536 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_24" data-name="Rectangle 24" width="5.336" height="5.336" rx="1" transform="translate(77.859 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_25" data-name="Rectangle 25" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_4" data-name="Group 4" transform="translate(91.05 9.546) rotate(180)">
|
||||
<path id="Path_53" data-name="Path 53" d="M1.093,0H6.219A1.093,1.093,0,0,1,7.312,1.093v3.15A1.093,1.093,0,0,1,6.219,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_26" data-name="Rectangle 26" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_27" data-name="Rectangle 27" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_28" data-name="Rectangle 28" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_29" data-name="Rectangle 29" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_30" data-name="Rectangle 30" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_31" data-name="Rectangle 31" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_32" data-name="Rectangle 32" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_33" data-name="Rectangle 33" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_34" data-name="Rectangle 34" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_35" data-name="Rectangle 35" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_36" data-name="Rectangle 36" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_37" data-name="Rectangle 37" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_38" data-name="Rectangle 38" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_39" data-name="Rectangle 39" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_40" data-name="Rectangle 40" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_41" data-name="Rectangle 41" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_42" data-name="Rectangle 42" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_43" data-name="Rectangle 43" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_44" data-name="Rectangle 44" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_45" data-name="Rectangle 45" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_46" data-name="Rectangle 46" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_47" data-name="Rectangle 47" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_48" data-name="Rectangle 48" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_49" data-name="Rectangle 49" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_50" data-name="Rectangle 50" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_51" data-name="Rectangle 51" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_6" data-name="Group 6" transform="translate(1.531 16.584)">
|
||||
<path id="Path_54" data-name="Path 54" d="M1.093,0h7.3A1.093,1.093,0,0,1,9.485,1.093v3.15A1.093,1.093,0,0,1,8.392,5.336h-7.3A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<g id="Group_5" data-name="Group 5" transform="translate(10.671 0)">
|
||||
<rect id="Rectangle_52" data-name="Rectangle 52" width="5.336" height="5.336" rx="1" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_53" data-name="Rectangle 53" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_54" data-name="Rectangle 54" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_55" data-name="Rectangle 55" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_56" data-name="Rectangle 56" width="5.336" height="5.336" rx="1" transform="translate(25.295 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_57" data-name="Rectangle 57" width="5.336" height="5.336" rx="1" transform="translate(31.619 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_58" data-name="Rectangle 58" width="5.336" height="5.336" rx="1" transform="translate(37.942 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_59" data-name="Rectangle 59" width="5.336" height="5.336" rx="1" transform="translate(44.265 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_60" data-name="Rectangle 60" width="5.336" height="5.336" rx="1" transform="translate(50.589 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_61" data-name="Rectangle 61" width="5.336" height="5.336" rx="1" transform="translate(56.912 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_62" data-name="Rectangle 62" width="5.336" height="5.336" rx="1" transform="translate(63.236 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<path id="Path_55" data-name="Path 55" d="M1.094,0H8A1.093,1.093,0,0,1,9.091,1.093v3.15A1.093,1.093,0,0,1,8,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(80.428 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_7" data-name="Group 7" transform="translate(1.531 29.627)">
|
||||
<rect id="Rectangle_63" data-name="Rectangle 63" width="5.336" height="5.336" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_64" data-name="Rectangle 64" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_65" data-name="Rectangle 65" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_66" data-name="Rectangle 66" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_56" data-name="Path 56" d="M1.093,0H31.515a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.244V1.093A1.093,1.093,0,0,1,1.093,0ZM34.687,0h3.942a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H34.687a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,34.687,0Z" transform="translate(25.294 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_67" data-name="Rectangle 67" width="5.336" height="5.336" rx="1" transform="translate(66.003 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_68" data-name="Rectangle 68" width="5.336" height="5.336" rx="1" transform="translate(72.327 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_69" data-name="Rectangle 69" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_57" data-name="Path 57" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(83.59 2.273) rotate(180)" fill="#4a4a4a"/>
|
||||
<path id="Path_58" data-name="Path 58" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(78.255 3.063)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<rect id="Rectangle_70" data-name="Rectangle 70" width="88.927" height="2.371" rx="1.085" transform="translate(1.925 1.17)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_71" data-name="Rectangle 71" width="4.986" height="1.581" rx="0.723" transform="translate(4.1 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_72" data-name="Rectangle 72" width="4.986" height="1.581" rx="0.723" transform="translate(10.923 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_73" data-name="Rectangle 73" width="4.986" height="1.581" rx="0.723" transform="translate(16.173 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_74" data-name="Rectangle 74" width="4.986" height="1.581" rx="0.723" transform="translate(21.421 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_75" data-name="Rectangle 75" width="4.986" height="1.581" rx="0.723" transform="translate(26.671 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_76" data-name="Rectangle 76" width="4.986" height="1.581" rx="0.723" transform="translate(33.232 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_77" data-name="Rectangle 77" width="4.986" height="1.581" rx="0.723" transform="translate(38.48 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_78" data-name="Rectangle 78" width="4.986" height="1.581" rx="0.723" transform="translate(43.73 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_79" data-name="Rectangle 79" width="4.986" height="1.581" rx="0.723" transform="translate(48.978 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_80" data-name="Rectangle 80" width="4.986" height="1.581" rx="0.723" transform="translate(55.54 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_81" data-name="Rectangle 81" width="4.986" height="1.581" rx="0.723" transform="translate(60.788 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_82" data-name="Rectangle 82" width="4.986" height="1.581" rx="0.723" transform="translate(66.038 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_83" data-name="Rectangle 83" width="4.986" height="1.581" rx="0.723" transform="translate(72.599 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_84" data-name="Rectangle 84" width="4.986" height="1.581" rx="0.723" transform="translate(77.847 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_85" data-name="Rectangle 85" width="4.986" height="1.581" rx="0.723" transform="translate(83.097 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
</g>
|
||||
<path id="Path_59" data-name="Path 59" d="M146.71,159.855a5.439,5.439,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(6.275 -6.025)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_60" data-name="Path 60" d="M83,124.855h43.71V103H83Z" transform="translate(4.42 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_61" data-name="Path 61" d="M134.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.811,2.811,0,0,0,.349.035" transform="translate(7.202 -9.377)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_62" data-name="Path 62" d="M143.232,42.33a2.967,2.967,0,0,1-.535-.055,2.754,2.754,0,0,1-.514-.153,2.838,2.838,0,0,1-.471-.251,4.139,4.139,0,0,1-.415-.339,3.2,3.2,0,0,1-.338-.415A2.7,2.7,0,0,1,140.5,39.6a2.968,2.968,0,0,1,.055-.535,3.152,3.152,0,0,1,.152-.514,2.874,2.874,0,0,1,.252-.47,2.633,2.633,0,0,1,.753-.754,2.837,2.837,0,0,1,.471-.251,2.753,2.753,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,4.019,4.019,0,0,1,.339.415,2.786,2.786,0,0,1,.251.47,2.864,2.864,0,0,1,.208,1.049,2.77,2.77,0,0,1-.8,1.934,4.139,4.139,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459m21.855-1.366a2.789,2.789,0,0,1-1.935-.8,4.162,4.162,0,0,1-.338-.415,2.7,2.7,0,0,1-.459-1.519,2.789,2.789,0,0,1,.8-1.934,4.139,4.139,0,0,1,.415-.339,2.838,2.838,0,0,1,.471-.251,2.752,2.752,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,2.79,2.79,0,0,1,.8,1.934,3.069,3.069,0,0,1-.055.535,2.779,2.779,0,0,1-.153.514,3.885,3.885,0,0,1-.251.47,4.02,4.02,0,0,1-.339.415,4.138,4.138,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459" transform="translate(9.753 -15.532)" fill-rule="evenodd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 31 KiB |
170
docs/static/img/undraw_docusaurus_react.svg
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1041.277" height="554.141" viewBox="0 0 1041.277 554.141">
|
||||
<title>Powered by React</title>
|
||||
<g id="Group_24" data-name="Group 24" transform="translate(-440 -263)">
|
||||
<g id="Group_23" data-name="Group 23" transform="translate(439.989 262.965)">
|
||||
<path id="Path_299" data-name="Path 299" d="M1040.82,611.12q-1.74,3.75-3.47,7.4-2.7,5.67-5.33,11.12c-.78,1.61-1.56,3.19-2.32,4.77-8.6,17.57-16.63,33.11-23.45,45.89A73.21,73.21,0,0,1,942.44,719l-151.65,1.65h-1.6l-13,.14-11.12.12-34.1.37h-1.38l-17.36.19h-.53l-107,1.16-95.51,1-11.11.12-69,.75H429l-44.75.48h-.48l-141.5,1.53-42.33.46a87.991,87.991,0,0,1-10.79-.54h0c-1.22-.14-2.44-.3-3.65-.49a87.38,87.38,0,0,1-51.29-27.54C116,678.37,102.75,655,93.85,629.64q-1.93-5.49-3.6-11.12C59.44,514.37,97,380,164.6,290.08q4.25-5.64,8.64-11l.07-.08c20.79-25.52,44.1-46.84,68.93-62,44-26.91,92.75-34.49,140.7-11.9,40.57,19.12,78.45,28.11,115.17,30.55,3.71.24,7.42.42,11.11.53,84.23,2.65,163.17-27.7,255.87-47.29,3.69-.78,7.39-1.55,11.12-2.28,66.13-13.16,139.49-20.1,226.73-5.51a189.089,189.089,0,0,1,26.76,6.4q5.77,1.86,11.12,4c41.64,16.94,64.35,48.24,74,87.46q1.37,5.46,2.37,11.11C1134.3,384.41,1084.19,518.23,1040.82,611.12Z" transform="translate(-79.34 -172.91)" fill="#f2f2f2"/>
|
||||
<path id="Path_300" data-name="Path 300" d="M576.36,618.52a95.21,95.21,0,0,1-1.87,11.12h93.7V618.52Zm-78.25,62.81,11.11-.09V653.77c-3.81-.17-7.52-.34-11.11-.52ZM265.19,618.52v11.12h198.5V618.52ZM1114.87,279h-74V191.51q-5.35-2.17-11.12-4V279H776.21V186.58c-3.73.73-7.43,1.5-11.12,2.28V279H509.22V236.15c-3.69-.11-7.4-.29-11.11-.53V279H242.24V217c-24.83,15.16-48.14,36.48-68.93,62h-.07v.08q-4.4,5.4-8.64,11h8.64V618.52h-83q1.66,5.63,3.6,11.12h79.39v93.62a87,87,0,0,0,12.2,2.79c1.21.19,2.43.35,3.65.49h0a87.991,87.991,0,0,0,10.79.54l42.33-.46v-97H498.11v94.21l11.11-.12V629.64H765.09V721l11.12-.12V629.64H1029.7v4.77c.76-1.58,1.54-3.16,2.32-4.77q2.63-5.45,5.33-11.12,1.73-3.64,3.47-7.4v-321h76.42Q1116.23,284.43,1114.87,279ZM242.24,618.52V290.08H498.11V618.52Zm267,0V290.08H765.09V618.52Zm520.48,0H776.21V290.08H1029.7Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_301" data-name="Path 301" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" fill="#65617d"/>
|
||||
<path id="Path_302" data-name="Path 302" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" opacity="0.2"/>
|
||||
<path id="Path_303" data-name="Path 303" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
<path id="Path_304" data-name="Path 304" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_305" data-name="Path 305" d="M377.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_137" data-name="Rectangle 137" width="47.17" height="31.5" transform="translate(680.92 483.65)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_138" data-name="Rectangle 138" width="47.17" height="31.5" transform="translate(680.92 483.65)" opacity="0.1"/>
|
||||
<rect id="Rectangle_139" data-name="Rectangle 139" width="47.17" height="31.5" transform="translate(678.92 483.65)" fill="#3f3d56"/>
|
||||
<path id="Path_306" data-name="Path 306" d="M298.09,483.65v4.97l-47.17,1.26v-6.23Z" opacity="0.1"/>
|
||||
<path id="Path_307" data-name="Path 307" d="M460.69,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6a4,4,0,0,1,3.95,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
|
||||
<path id="Path_308" data-name="Path 308" d="M265.19,481.32v181.2h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_309" data-name="Path 309" d="M194.59,319.15h177.5V467.4l-177.5,4Z" fill="#39374d"/>
|
||||
<path id="Path_310" data-name="Path 310" d="M726.09,483.65v6.41l-47.17-1.26v-5.15Z" opacity="0.1"/>
|
||||
<path id="Path_311" data-name="Path 311" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0L672,657.42a4,4,0,0,1-3.85-3.95V485.27a4,4,0,0,1,3.95-3.95H863.7a4,4,0,0,1,3.99,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
|
||||
<path id="Path_312" data-name="Path 312" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0V481.32h0a4,4,0,0,1,4,3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_313" data-name="Path 313" d="M775.59,319.15H598.09V467.4l177.5,4Z" fill="#39374d"/>
|
||||
<path id="Path_314" data-name="Path 314" d="M663.19,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h0a4,4,0,0,1-4-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6A4,4,0,0,1,663.19,485.27Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
|
||||
<path id="Path_315" data-name="Path 315" d="M397.09,319.15h177.5V467.4l-177.5,4Z" fill="#4267b2"/>
|
||||
<path id="Path_316" data-name="Path 316" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l202.51-1.33h.48l40.99-.28h.19l283.08-1.87h.29l.17-.01h.47l4.79-.03h1.46l74.49-.5,4.4-.02.98-.01Z" opacity="0.1"/>
|
||||
<circle id="Ellipse_111" data-name="Ellipse 111" cx="51.33" cy="51.33" r="51.33" transform="translate(435.93 246.82)" fill="#fbbebe"/>
|
||||
<path id="Path_317" data-name="Path 317" d="M617.94,550.07s-99.5,12-90,0c3.44-4.34,4.39-17.2,4.2-31.85-.06-4.45-.22-9.06-.45-13.65-1.1-22-3.75-43.5-3.75-43.5s87-41,77-8.5c-4,13.13-2.69,31.57.35,48.88.89,5.05,1.92,10,3,14.7a344.66,344.66,0,0,0,9.65,33.92Z" transform="translate(-79.34 -172.91)" fill="#fbbebe"/>
|
||||
<path id="Path_318" data-name="Path 318" d="M585.47,546c11.51-2.13,23.7-6,34.53-1.54,2.85,1.17,5.47,2.88,8.39,3.86s6.12,1.22,9.16,1.91c10.68,2.42,19.34,10.55,24.9,20s8.44,20.14,11.26,30.72l6.9,25.83c6,22.45,12,45.09,13.39,68.3a2437.506,2437.506,0,0,1-250.84,1.43c5.44-10.34,11-21.31,10.54-33s-7.19-23.22-4.76-34.74c1.55-7.34,6.57-13.39,9.64-20.22,8.75-19.52,1.94-45.79,17.32-60.65,6.92-6.68,17-9.21,26.63-8.89,12.28.41,24.85,4.24,37,6.11C555.09,547.48,569.79,548.88,585.47,546Z" transform="translate(-79.34 -172.91)" fill="#ff6584"/>
|
||||
<path id="Path_319" data-name="Path 319" d="M716.37,657.17l-.1,1.43v.1l-.17,2.3-1.33,18.51-1.61,22.3-.46,6.28-1,13.44v.17l-107,1-175.59,1.9v.84h-.14v-1.12l.45-14.36.86-28.06.74-23.79.07-2.37a10.53,10.53,0,0,1,11.42-10.17c4.72.4,10.85.89,18.18,1.41l3,.22c42.33,2.94,120.56,6.74,199.5,2,1.66-.09,3.33-.19,5-.31,12.24-.77,24.47-1.76,36.58-3a10.53,10.53,0,0,1,11.6,11.23Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_320" data-name="Path 320" d="M429.08,725.44v-.84l175.62-1.91,107-1h.3v-.17l1-13.44.43-6,1.64-22.61,1.29-17.9v-.44a10.617,10.617,0,0,0-.11-2.47.3.3,0,0,0,0-.1,10.391,10.391,0,0,0-2-4.64,10.54,10.54,0,0,0-9.42-4c-12.11,1.24-24.34,2.23-36.58,3-1.67.12-3.34.22-5,.31-78.94,4.69-157.17.89-199.5-2l-3-.22c-7.33-.52-13.46-1-18.18-1.41a10.54,10.54,0,0,0-11.24,8.53,11,11,0,0,0-.18,1.64l-.68,22.16L429.54,710l-.44,14.36v1.12Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
<path id="Path_321" data-name="Path 321" d="M716.67,664.18l-1.23,15.33-1.83,22.85-.46,5.72-1,12.81-.06.64v.17h0l-.15,1.48.11-1.48h-.29l-107,1-175.65,1.9v-.28l.49-14.36,1-28.06.64-18.65A6.36,6.36,0,0,1,434.3,658a6.25,6.25,0,0,1,3.78-.9c2.1.17,4.68.37,7.69.59,4.89.36,10.92.78,17.94,1.22,13,.82,29.31,1.7,48,2.42,52,2,122.2,2.67,188.88-3.17,3-.26,6.1-.55,9.13-.84a6.26,6.26,0,0,1,3.48.66,5.159,5.159,0,0,1,.86.54,6.14,6.14,0,0,1,2,2.46,3.564,3.564,0,0,1,.25.61A6.279,6.279,0,0,1,716.67,664.18Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_322" data-name="Path 322" d="M377.44,677.87v3.19a6.13,6.13,0,0,1-3.5,5.54l-40.1.77a6.12,6.12,0,0,1-3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_323" data-name="Path 323" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
|
||||
<path id="Path_324" data-name="Path 324" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" opacity="0.1"/>
|
||||
<path id="Path_325" data-name="Path 325" d="M300.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
|
||||
<path id="Path_326" data-name="Path 326" d="M758.56,679.87v3.19a6.13,6.13,0,0,0,3.5,5.54l40.1.77a6.12,6.12,0,0,0,3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_327" data-name="Path 327" d="M678.72,517.57l52.25,1V509.9l-52.25-1Z" opacity="0.1"/>
|
||||
<path id="Path_328" data-name="Path 328" d="M676.72,517.57l52.25,1V509.9l-52.25-1Z" fill="#3f3d56"/>
|
||||
<path id="Path_329" data-name="Path 329" d="M534.13,486.79c.08,7-3.16,13.6-5.91,20.07a163.491,163.491,0,0,0-12.66,74.71c.73,11,2.58,22,.73,32.9s-8.43,21.77-19,24.9c17.53,10.45,41.26,9.35,57.76-2.66,8.79-6.4,15.34-15.33,21.75-24.11a97.86,97.86,0,0,1-13.31,44.75A103.43,103.43,0,0,0,637,616.53c4.31-5.81,8.06-12.19,9.72-19.23,3.09-13-1.22-26.51-4.51-39.5a266.055,266.055,0,0,1-6.17-33c-.43-3.56-.78-7.22.1-10.7,1-4.07,3.67-7.51,5.64-11.22,5.6-10.54,5.73-23.3,2.86-34.88s-8.49-22.26-14.06-32.81c-4.46-8.46-9.3-17.31-17.46-22.28-5.1-3.1-11-4.39-16.88-5.64l-25.37-5.43c-5.55-1.19-11.26-2.38-16.87-1.51-9.47,1.48-16.14,8.32-22,15.34-4.59,5.46-15.81,15.71-16.6,22.86-.72,6.59,5.1,17.63,6.09,24.58,1.3,9,2.22,6,7.3,11.52C532,478.05,534.07,482,534.13,486.79Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
</g>
|
||||
<g id="docusaurus_keytar" transform="translate(670.271 615.768)">
|
||||
<path id="Path_40" data-name="Path 40" d="M99,52h43.635V69.662H99Z" transform="translate(-49.132 -33.936)" fill="#fff" fill-rule="evenodd"/>
|
||||
<path id="Path_41" data-name="Path 41" d="M13.389,158.195A10.377,10.377,0,0,1,4.4,153a10.377,10.377,0,0,0,8.988,15.584H23.779V158.195Z" transform="translate(-3 -82.47)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_42" data-name="Path 42" d="M66.967,38.083l36.373-2.273V30.615A10.389,10.389,0,0,0,92.95,20.226H46.2l-1.3-2.249a1.5,1.5,0,0,0-2.6,0L41,20.226l-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-.034,0-2.152-2.151a1.5,1.5,0,0,0-2.508.672L25.21,21.4l-2.7-.723a1.5,1.5,0,0,0-1.836,1.837l.722,2.7-2.65.71a1.5,1.5,0,0,0-.673,2.509l2.152,2.152c0,.011,0,.022,0,.033l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6L20.226,41l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3A10.389,10.389,0,0,0,30.615,103.34H92.95A10.389,10.389,0,0,0,103.34,92.95V51.393L66.967,49.12a5.53,5.53,0,0,1,0-11.038" transform="translate(-9.836 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_43" data-name="Path 43" d="M143,163.779h15.584V143H143Z" transform="translate(-70.275 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_44" data-name="Path 44" d="M173.779,148.389a2.582,2.582,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-75.08 -75.262)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_45" data-name="Path 45" d="M153,113.389h15.584V103H153Z" transform="translate(-75.08 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_46" data-name="Path 46" d="M183.389,108.944a1.3,1.3,0,1,0,0-2.6,1.336,1.336,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.337,1.337,0,0,0,.166.017" transform="translate(-84.691 -57.894)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_47" data-name="Path 47" d="M52.188,48.292a1.3,1.3,0,0,1-1.3-1.3,3.9,3.9,0,0,0-7.792,0,1.3,1.3,0,1,1-2.6,0,6.493,6.493,0,0,1,12.987,0,1.3,1.3,0,0,1-1.3,1.3" transform="translate(-21.02 -28.41)" fill-rule="evenodd"/>
|
||||
<path id="Path_48" data-name="Path 48" d="M103,139.752h31.168a10.389,10.389,0,0,0,10.389-10.389V93H113.389A10.389,10.389,0,0,0,103,103.389Z" transform="translate(-51.054 -53.638)" fill="#ffff50" fill-rule="evenodd"/>
|
||||
<path id="Path_49" data-name="Path 49" d="M141.1,94.017H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0-25.877H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.293H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m7.782-47.993c-.006,0-.011,0-.018,0-1.605.055-2.365,1.66-3.035,3.077-.7,1.48-1.24,2.443-2.126,2.414-.981-.035-1.542-1.144-2.137-2.317-.683-1.347-1.462-2.876-3.1-2.819-1.582.054-2.344,1.451-3.017,2.684-.715,1.313-1.2,2.112-2.141,2.075-1-.036-1.533-.938-2.149-1.981-.686-1.162-1.479-2.467-3.084-2.423-1.555.053-2.319,1.239-2.994,2.286-.713,1.106-1.213,1.781-2.164,1.741-1.025-.036-1.554-.784-2.167-1.65-.688-.973-1.463-2.074-3.062-2.021a3.815,3.815,0,0,0-2.959,1.879c-.64.812-1.14,1.456-2.2,1.415a.52.52,0,0,0-.037,1.039,3.588,3.588,0,0,0,3.05-1.811c.611-.777,1.139-1.448,2.178-1.483,1-.043,1.47.579,2.179,1.582.674.953,1.438,2.033,2.977,2.089,1.612.054,2.387-1.151,3.074-2.217.614-.953,1.144-1.775,2.156-1.81.931-.035,1.438.7,2.153,1.912.674,1.141,1.437,2.434,3.006,2.491,1.623.056,2.407-1.361,3.09-2.616.592-1.085,1.15-2.109,2.14-2.143.931-.022,1.417.829,2.135,2.249.671,1.326,1.432,2.828,3.026,2.886l.088,0c1.592,0,2.347-1.6,3.015-3.01.592-1.252,1.152-2.431,2.113-2.479Z" transform="translate(-55.378 -38.552)" fill-rule="evenodd"/>
|
||||
<path id="Path_50" data-name="Path 50" d="M83,163.779h20.779V143H83Z" transform="translate(-41.443 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 51.971, 43.3)">
|
||||
<rect id="Rectangle_3" data-name="Rectangle 3" width="43.906" height="17.333" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
|
||||
<g id="Group_2" data-name="Group 2" transform="translate(0.728 10.948)">
|
||||
<rect id="Rectangle_4" data-name="Rectangle 4" width="2.537" height="2.537" rx="1" transform="translate(7.985 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_5" data-name="Rectangle 5" width="2.537" height="2.537" rx="1" transform="translate(10.991 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_6" data-name="Rectangle 6" width="2.537" height="2.537" rx="1" transform="translate(13.997 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_7" data-name="Rectangle 7" width="2.537" height="2.537" rx="1" transform="translate(17.003 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_8" data-name="Rectangle 8" width="2.537" height="2.537" rx="1" transform="translate(20.009 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_9" data-name="Rectangle 9" width="2.537" height="2.537" rx="1" transform="translate(23.015 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_10" data-name="Rectangle 10" width="2.537" height="2.537" rx="1" transform="translate(26.021 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_11" data-name="Rectangle 11" width="2.537" height="2.537" rx="1" transform="translate(29.028 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_12" data-name="Rectangle 12" width="2.537" height="2.537" rx="1" transform="translate(32.034 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_51" data-name="Path 51" d="M.519,0H6.9A.519.519,0,0,1,7.421.52v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0ZM35.653,0h6.383a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H35.652a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,35.652,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_3" data-name="Group 3" transform="translate(0.728 4.878)">
|
||||
<path id="Path_52" data-name="Path 52" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_13" data-name="Rectangle 13" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_14" data-name="Rectangle 14" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_15" data-name="Rectangle 15" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_16" data-name="Rectangle 16" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_17" data-name="Rectangle 17" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_18" data-name="Rectangle 18" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_19" data-name="Rectangle 19" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_20" data-name="Rectangle 20" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_21" data-name="Rectangle 21" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_22" data-name="Rectangle 22" width="2.537" height="2.537" rx="1" transform="translate(31 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_23" data-name="Rectangle 23" width="2.537" height="2.537" rx="1" transform="translate(34.006 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_24" data-name="Rectangle 24" width="2.537" height="2.537" rx="1" transform="translate(37.012 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_25" data-name="Rectangle 25" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_4" data-name="Group 4" transform="translate(43.283 4.538) rotate(180)">
|
||||
<path id="Path_53" data-name="Path 53" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_26" data-name="Rectangle 26" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_27" data-name="Rectangle 27" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_28" data-name="Rectangle 28" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_29" data-name="Rectangle 29" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_30" data-name="Rectangle 30" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_31" data-name="Rectangle 31" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_32" data-name="Rectangle 32" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_33" data-name="Rectangle 33" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_34" data-name="Rectangle 34" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_35" data-name="Rectangle 35" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_36" data-name="Rectangle 36" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_37" data-name="Rectangle 37" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_38" data-name="Rectangle 38" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_39" data-name="Rectangle 39" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_40" data-name="Rectangle 40" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_41" data-name="Rectangle 41" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_42" data-name="Rectangle 42" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_43" data-name="Rectangle 43" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_44" data-name="Rectangle 44" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_45" data-name="Rectangle 45" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_46" data-name="Rectangle 46" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_47" data-name="Rectangle 47" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_48" data-name="Rectangle 48" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_49" data-name="Rectangle 49" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_50" data-name="Rectangle 50" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_51" data-name="Rectangle 51" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_6" data-name="Group 6" transform="translate(0.728 7.883)">
|
||||
<path id="Path_54" data-name="Path 54" d="M.519,0h3.47a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<g id="Group_5" data-name="Group 5" transform="translate(5.073 0)">
|
||||
<rect id="Rectangle_52" data-name="Rectangle 52" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_53" data-name="Rectangle 53" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_54" data-name="Rectangle 54" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_55" data-name="Rectangle 55" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_56" data-name="Rectangle 56" width="2.537" height="2.537" rx="1" transform="translate(12.025 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_57" data-name="Rectangle 57" width="2.537" height="2.537" rx="1" transform="translate(15.031 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_58" data-name="Rectangle 58" width="2.537" height="2.537" rx="1" transform="translate(18.037 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_59" data-name="Rectangle 59" width="2.537" height="2.537" rx="1" transform="translate(21.042 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_60" data-name="Rectangle 60" width="2.537" height="2.537" rx="1" transform="translate(24.049 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_61" data-name="Rectangle 61" width="2.537" height="2.537" rx="1" transform="translate(27.055 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_62" data-name="Rectangle 62" width="2.537" height="2.537" rx="1" transform="translate(30.061 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<path id="Path_55" data-name="Path 55" d="M.52,0H3.8a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(38.234 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_7" data-name="Group 7" transform="translate(0.728 14.084)">
|
||||
<rect id="Rectangle_63" data-name="Rectangle 63" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_64" data-name="Rectangle 64" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_65" data-name="Rectangle 65" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_66" data-name="Rectangle 66" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_56" data-name="Path 56" d="M.519,0H14.981A.519.519,0,0,1,15.5.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.018V.519A.519.519,0,0,1,.519,0Zm15.97,0h1.874a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H16.489a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,16.489,0Z" transform="translate(12.024 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_67" data-name="Rectangle 67" width="2.537" height="2.537" rx="1" transform="translate(31.376 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_68" data-name="Rectangle 68" width="2.537" height="2.537" rx="1" transform="translate(34.382 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_69" data-name="Rectangle 69" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_57" data-name="Path 57" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(39.736 1.08) rotate(180)" fill="#4a4a4a"/>
|
||||
<path id="Path_58" data-name="Path 58" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(37.2 1.456)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<rect id="Rectangle_70" data-name="Rectangle 70" width="42.273" height="1.127" rx="0.564" transform="translate(0.915 0.556)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_71" data-name="Rectangle 71" width="2.37" height="0.752" rx="0.376" transform="translate(1.949 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_72" data-name="Rectangle 72" width="2.37" height="0.752" rx="0.376" transform="translate(5.193 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_73" data-name="Rectangle 73" width="2.37" height="0.752" rx="0.376" transform="translate(7.688 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_74" data-name="Rectangle 74" width="2.37" height="0.752" rx="0.376" transform="translate(10.183 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_75" data-name="Rectangle 75" width="2.37" height="0.752" rx="0.376" transform="translate(12.679 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_76" data-name="Rectangle 76" width="2.37" height="0.752" rx="0.376" transform="translate(15.797 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_77" data-name="Rectangle 77" width="2.37" height="0.752" rx="0.376" transform="translate(18.292 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_78" data-name="Rectangle 78" width="2.37" height="0.752" rx="0.376" transform="translate(20.788 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_79" data-name="Rectangle 79" width="2.37" height="0.752" rx="0.376" transform="translate(23.283 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_80" data-name="Rectangle 80" width="2.37" height="0.752" rx="0.376" transform="translate(26.402 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_81" data-name="Rectangle 81" width="2.37" height="0.752" rx="0.376" transform="translate(28.897 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_82" data-name="Rectangle 82" width="2.37" height="0.752" rx="0.376" transform="translate(31.393 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_83" data-name="Rectangle 83" width="2.37" height="0.752" rx="0.376" transform="translate(34.512 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_84" data-name="Rectangle 84" width="2.37" height="0.752" rx="0.376" transform="translate(37.007 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_85" data-name="Rectangle 85" width="2.37" height="0.752" rx="0.376" transform="translate(39.502 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
</g>
|
||||
<path id="Path_59" data-name="Path 59" d="M123.779,148.389a2.583,2.583,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-51.054 -75.262)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_60" data-name="Path 60" d="M83,113.389h20.779V103H83Z" transform="translate(-41.443 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_61" data-name="Path 61" d="M123.389,108.944a1.3,1.3,0,1,0,0-2.6,1.338,1.338,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.335,1.335,0,0,0,.166.017" transform="translate(-55.859 -57.894)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_62" data-name="Path 62" d="M141.8,38.745a1.41,1.41,0,0,1-.255-.026,1.309,1.309,0,0,1-.244-.073,1.349,1.349,0,0,1-.224-.119,1.967,1.967,0,0,1-.2-.161,1.52,1.52,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.41,1.41,0,0,1,.026-.255,1.5,1.5,0,0,1,.072-.244,1.364,1.364,0,0,1,.12-.223,1.252,1.252,0,0,1,.358-.358,1.349,1.349,0,0,1,.224-.119,1.309,1.309,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.968,1.968,0,0,1,.2.161,1.908,1.908,0,0,1,.161.2,1.322,1.322,0,0,1,.12.223,1.361,1.361,0,0,1,.1.5,1.317,1.317,0,0,1-.379.919,1.968,1.968,0,0,1-.2.161,1.346,1.346,0,0,1-.223.119,1.332,1.332,0,0,1-.5.1m10.389-.649a1.326,1.326,0,0,1-.92-.379,1.979,1.979,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.326,1.326,0,0,1,.379-.919,1.967,1.967,0,0,1,.2-.161,1.351,1.351,0,0,1,.224-.119,1.308,1.308,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.967,1.967,0,0,1,.2.161,1.326,1.326,0,0,1,.379.919,1.461,1.461,0,0,1-.026.255,1.323,1.323,0,0,1-.073.244,1.847,1.847,0,0,1-.119.223,1.911,1.911,0,0,1-.161.2,1.967,1.967,0,0,1-.2.161,1.294,1.294,0,0,1-.722.218" transform="translate(-69.074 -26.006)" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="React-icon" transform="translate(906.3 541.56)">
|
||||
<path id="Path_330" data-name="Path 330" d="M263.668,117.179c0-5.827-7.3-11.35-18.487-14.775,2.582-11.4,1.434-20.477-3.622-23.382a7.861,7.861,0,0,0-4.016-1v4a4.152,4.152,0,0,1,2.044.466c2.439,1.4,3.5,6.724,2.672,13.574-.2,1.685-.52,3.461-.914,5.272a86.9,86.9,0,0,0-11.386-1.954,87.469,87.469,0,0,0-7.459-8.965c5.845-5.433,11.332-8.41,15.062-8.41V78h0c-4.931,0-11.386,3.514-17.913,9.611-6.527-6.061-12.982-9.539-17.913-9.539v4c3.712,0,9.216,2.959,15.062,8.356a84.687,84.687,0,0,0-7.405,8.947,83.732,83.732,0,0,0-11.4,1.972c-.412-1.793-.717-3.532-.932-5.2-.843-6.85.2-12.175,2.618-13.592a3.991,3.991,0,0,1,2.062-.466v-4h0a8,8,0,0,0-4.052,1c-5.039,2.9-6.168,11.96-3.568,23.328-11.153,3.443-18.415,8.947-18.415,14.757,0,5.828,7.3,11.35,18.487,14.775-2.582,11.4-1.434,20.477,3.622,23.382a7.882,7.882,0,0,0,4.034,1c4.931,0,11.386-3.514,17.913-9.611,6.527,6.061,12.982,9.539,17.913,9.539a8,8,0,0,0,4.052-1c5.039-2.9,6.168-11.96,3.568-23.328C256.406,128.511,263.668,122.988,263.668,117.179Zm-23.346-11.96c-.663,2.313-1.488,4.7-2.421,7.083-.735-1.434-1.506-2.869-2.349-4.3-.825-1.434-1.7-2.833-2.582-4.2C235.517,104.179,237.974,104.645,240.323,105.219Zm-8.212,19.1c-1.4,2.421-2.833,4.716-4.321,6.85-2.672.233-5.379.359-8.1.359-2.708,0-5.415-.126-8.069-.341q-2.232-3.2-4.339-6.814-2.044-3.523-3.73-7.136c1.112-2.4,2.367-4.805,3.712-7.154,1.4-2.421,2.833-4.716,4.321-6.85,2.672-.233,5.379-.359,8.1-.359,2.708,0,5.415.126,8.069.341q2.232,3.2,4.339,6.814,2.044,3.523,3.73,7.136C234.692,119.564,233.455,121.966,232.11,124.315Zm5.792-2.331c.968,2.4,1.793,4.805,2.474,7.136-2.349.574-4.823,1.058-7.387,1.434.879-1.381,1.757-2.8,2.582-4.25C236.4,124.871,237.167,123.419,237.9,121.984ZM219.72,141.116a73.921,73.921,0,0,1-4.985-5.738c1.614.072,3.263.126,4.931.126,1.685,0,3.353-.036,4.985-.126A69.993,69.993,0,0,1,219.72,141.116ZM206.38,130.555c-2.546-.377-5-.843-7.352-1.417.663-2.313,1.488-4.7,2.421-7.083.735,1.434,1.506,2.869,2.349,4.3S205.5,129.192,206.38,130.555ZM219.63,93.241a73.924,73.924,0,0,1,4.985,5.738c-1.614-.072-3.263-.126-4.931-.126-1.686,0-3.353.036-4.985.126A69.993,69.993,0,0,1,219.63,93.241ZM206.362,103.8c-.879,1.381-1.757,2.8-2.582,4.25-.825,1.434-1.6,2.869-2.331,4.3-.968-2.4-1.793-4.805-2.474-7.136C201.323,104.663,203.8,104.179,206.362,103.8Zm-16.227,22.449c-6.348-2.708-10.454-6.258-10.454-9.073s4.106-6.383,10.454-9.073c1.542-.663,3.228-1.255,4.967-1.811a86.122,86.122,0,0,0,4.034,10.92,84.9,84.9,0,0,0-3.981,10.866C193.38,127.525,191.694,126.915,190.134,126.252Zm9.647,25.623c-2.439-1.4-3.5-6.724-2.672-13.574.2-1.686.52-3.461.914-5.272a86.9,86.9,0,0,0,11.386,1.954,87.465,87.465,0,0,0,7.459,8.965c-5.845,5.433-11.332,8.41-15.062,8.41A4.279,4.279,0,0,1,199.781,151.875Zm42.532-13.663c.843,6.85-.2,12.175-2.618,13.592a3.99,3.99,0,0,1-2.062.466c-3.712,0-9.216-2.959-15.062-8.356a84.689,84.689,0,0,0,7.405-8.947,83.731,83.731,0,0,0,11.4-1.972A50.194,50.194,0,0,1,242.313,138.212Zm6.9-11.96c-1.542.663-3.228,1.255-4.967,1.811a86.12,86.12,0,0,0-4.034-10.92,84.9,84.9,0,0,0,3.981-10.866c1.775.556,3.461,1.165,5.039,1.829,6.348,2.708,10.454,6.258,10.454,9.073C259.67,119.994,255.564,123.562,249.216,126.252Z" fill="#61dafb"/>
|
||||
<path id="Path_331" data-name="Path 331" d="M320.8,78.4Z" transform="translate(-119.082 -0.328)" fill="#61dafb"/>
|
||||
<circle id="Ellipse_112" data-name="Ellipse 112" cx="8.194" cy="8.194" r="8.194" transform="translate(211.472 108.984)" fill="#61dafb"/>
|
||||
<path id="Path_332" data-name="Path 332" d="M520.5,78.1Z" transform="translate(-282.975 -0.082)" fill="#61dafb"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 35 KiB |
40
docs/static/img/undraw_docusaurus_tree.svg
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1129" height="663" viewBox="0 0 1129 663">
|
||||
<title>Focus on What Matters</title>
|
||||
<circle cx="321" cy="321" r="321" fill="#f2f2f2" />
|
||||
<ellipse cx="559" cy="635.49998" rx="514" ry="27.50002" fill="#3f3d56" />
|
||||
<ellipse cx="558" cy="627" rx="460" ry="22" opacity="0.2" />
|
||||
<rect x="131" y="152.5" width="840" height="50" fill="#3f3d56" />
|
||||
<path d="M166.5,727.3299A21.67009,21.67009,0,0,0,188.1701,749H984.8299A21.67009,21.67009,0,0,0,1006.5,727.3299V296h-840Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
|
||||
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
|
||||
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" opacity="0.2" />
|
||||
<circle cx="181" cy="147.5" r="13" fill="#3f3d56" />
|
||||
<circle cx="217" cy="147.5" r="13" fill="#3f3d56" />
|
||||
<circle cx="253" cy="147.5" r="13" fill="#3f3d56" />
|
||||
<rect x="168" y="213.5" width="337" height="386" rx="5.33505" fill="#606060" />
|
||||
<rect x="603" y="272.5" width="284" height="22" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="352.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="396.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="440.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="484.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="865" y="552.5" width="88" height="26" rx="7.02756" fill="#3ecc5f" />
|
||||
<path d="M1088.60287,624.61594a30.11371,30.11371,0,0,0,3.98291-15.266c0-13.79652-8.54358-24.98081-19.08256-24.98081s-19.08256,11.18429-19.08256,24.98081a30.11411,30.11411,0,0,0,3.98291,15.266,31.248,31.248,0,0,0,0,30.53213,31.248,31.248,0,0,0,0,30.53208,31.248,31.248,0,0,0,0,30.53208,30.11408,30.11408,0,0,0-3.98291,15.266c0,13.79652,8.54353,24.98081,19.08256,24.98081s19.08256-11.18429,19.08256-24.98081a30.11368,30.11368,0,0,0-3.98291-15.266,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53213Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
|
||||
<ellipse cx="1038.00321" cy="460.31783" rx="19.08256" ry="24.9808" fill="#3f3d56" />
|
||||
<ellipse cx="1038.00321" cy="429.78574" rx="19.08256" ry="24.9808" fill="#3f3d56" />
|
||||
<path d="M1144.93871,339.34489a91.61081,91.61081,0,0,0,7.10658-10.46092l-50.141-8.23491,54.22885.4033a91.566,91.566,0,0,0,1.74556-72.42605l-72.75449,37.74139,67.09658-49.32086a91.41255,91.41255,0,1,0-150.971,102.29805,91.45842,91.45842,0,0,0-10.42451,16.66946l65.0866,33.81447-69.40046-23.292a91.46011,91.46011,0,0,0,14.73837,85.83669,91.40575,91.40575,0,1,0,143.68892,0,91.41808,91.41808,0,0,0,0-113.02862Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M981.6885,395.8592a91.01343,91.01343,0,0,0,19.56129,56.51431,91.40575,91.40575,0,1,0,143.68892,0C1157.18982,436.82067,981.6885,385.60008,981.6885,395.8592Z" transform="translate(-35.5 -118.5)" opacity="0.1" />
|
||||
<path d="M365.62,461.43628H477.094v45.12043H365.62Z" transform="translate(-35.5 -118.5)" fill="#fff" fill-rule="evenodd" />
|
||||
<path d="M264.76252,608.74122a26.50931,26.50931,0,0,1-22.96231-13.27072,26.50976,26.50976,0,0,0,22.96231,39.81215H291.304V608.74122Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M384.17242,468.57061l92.92155-5.80726V449.49263a26.54091,26.54091,0,0,0-26.54143-26.54143H331.1161l-3.31768-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622-3.31767-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622L301.257,417.205a3.83043,3.83043,0,0,0-6.63536,0L291.304,422.9512c-.02919,0-.05573.004-.08625.004l-5.49674-5.49541a3.8293,3.8293,0,0,0-6.4071,1.71723l-1.81676,6.77338L270.607,424.1031a3.82993,3.82993,0,0,0-4.6912,4.69253l1.84463,6.89148-6.77072,1.81411a3.8315,3.8315,0,0,0-1.71988,6.40975l5.49673,5.49673c0,.02787-.004.05574-.004.08493l-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74621,3.31768L259.0163,466.081a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768L259.0163,558.976a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768A26.54091,26.54091,0,0,0,291.304,635.28265H450.55254A26.5409,26.5409,0,0,0,477.094,608.74122V502.5755l-92.92155-5.80727a14.12639,14.12639,0,0,1,0-28.19762" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M424.01111,635.28265h39.81214V582.19979H424.01111Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M490.36468,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15393-.59852A6.62668,6.62668,0,1,0,482.80568,590.21q-.2203-.22491-.44457-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39414-.10218-.59056-.15262a6.63957,6.63957,0,1,0-13.10086,0c-.1964.05042-.39414.09687-.59056.15262a6.62767,6.62767,0,1,0-11.39688,6.56369,26.52754,26.52754,0,1,0,44.23127,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M437.28182,555.65836H477.094V529.11693H437.28182Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M490.36468,545.70532a3.31768,3.31768,0,0,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M317.84538,466.081a3.31768,3.31768,0,0,1-3.31767-3.31768,9.953,9.953,0,1,0-19.90608,0,3.31768,3.31768,0,1,1-6.63535,0,16.58839,16.58839,0,1,1,33.17678,0,3.31768,3.31768,0,0,1-3.31768,3.31768" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
|
||||
<path d="M370.92825,635.28265h79.62429A26.5409,26.5409,0,0,0,477.094,608.74122v-92.895H397.46968a26.54091,26.54091,0,0,0-26.54143,26.54143Z" transform="translate(-35.5 -118.5)" fill="#ffff50" fill-rule="evenodd" />
|
||||
<path d="M457.21444,556.98543H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0-66.10674H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.29459H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414M477.094,474.19076c-.01592,0-.0292-.008-.04512-.00663-4.10064.13934-6.04083,4.24132-7.75274,7.86024-1.78623,3.78215-3.16771,6.24122-5.43171,6.16691-2.50685-.09024-3.94007-2.92222-5.45825-5.91874-1.74377-3.44243-3.73438-7.34667-7.91333-7.20069-4.04227.138-5.98907,3.70784-7.70631,6.857-1.82738,3.35484-3.07084,5.39455-5.46887,5.30033-2.55727-.09289-3.91619-2.39536-5.48877-5.06013-1.75306-2.96733-3.77951-6.30359-7.8775-6.18946-3.97326.13669-5.92537,3.16507-7.64791,5.83912-1.82207,2.82666-3.09872,4.5492-5.52725,4.447-2.61832-.09289-3.9706-2.00388-5.53522-4.21611-1.757-2.4856-3.737-5.299-7.82308-5.16231-3.88567.13271-5.83779,2.61434-7.559,4.80135-1.635,2.07555-2.9116,3.71846-5.61218,3.615a1.32793,1.32793,0,1,0-.09555,2.65414c4.00377.134,6.03154-2.38873,7.79257-4.6275,1.562-1.9853,2.91027-3.69855,5.56441-3.78879,2.55594-.10882,3.75429,1.47968,5.56707,4.04093,1.7212,2.43385,3.67465,5.19416,7.60545,5.33616,4.11789.138,6.09921-2.93946,7.8536-5.66261,1.56861-2.43385,2.92221-4.53461,5.50734-4.62352,2.37944-.08892,3.67466,1.79154,5.50072,4.885,1.72121,2.91557,3.67069,6.21865,7.67977,6.36463,4.14709.14332,6.14965-3.47693,7.89475-6.68181,1.51155-2.77092,2.93814-5.38791,5.46621-5.4755,2.37944-.05573,3.62025,2.11668,5.45558,5.74622,1.71459,3.388,3.65875,7.22591,7.73019,7.37321l.22429.004c4.06614,0,5.99571-4.08074,7.70364-7.68905,1.51154-3.19825,2.94211-6.21069,5.3972-6.33411Z" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
|
||||
<path d="M344.38682,635.28265h53.08286V582.19979H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M424.01111,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15394-.59852A6.62667,6.62667,0,1,0,416.45211,590.21q-.2203-.22491-.44458-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39413-.10218-.59054-.15262a6.63957,6.63957,0,1,0-13.10084,0c-.19641.05042-.39414.09687-.59055.15262a6.62767,6.62767,0,1,0-11.39689,6.56369,26.52755,26.52755,0,1,0,44.2313,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M344.38682,555.65836h53.08286V529.11693H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M410.74039,545.70532a3.31768,3.31768,0,1,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M424.01111,447.8338a3.60349,3.60349,0,0,1-.65028-.06636,3.34415,3.34415,0,0,1-.62372-.18579,3.44679,3.44679,0,0,1-.572-.30522,5.02708,5.02708,0,0,1-.50429-.4114,3.88726,3.88726,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.60248,3.60248,0,0,1,.06636-.65027,3.82638,3.82638,0,0,1,.18447-.62373,3.48858,3.48858,0,0,1,.30656-.57064,3.197,3.197,0,0,1,.91436-.91568,3.44685,3.44685,0,0,1,.572-.30523,3.344,3.344,0,0,1,.62372-.18578,3.06907,3.06907,0,0,1,1.30053,0,3.22332,3.22332,0,0,1,1.19436.491,5.02835,5.02835,0,0,1,.50429.41139,4.8801,4.8801,0,0,1,.41139.50429,3.38246,3.38246,0,0,1,.30522.57064,3.47806,3.47806,0,0,1,.25215,1.274A3.36394,3.36394,0,0,1,426.36,446.865a5.02708,5.02708,0,0,1-.50429.4114,3.3057,3.3057,0,0,1-1.84463.55737m26.54143-1.65884a3.38754,3.38754,0,0,1-2.35024-.96877,5.04185,5.04185,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.38659,3.38659,0,0,1,.96744-2.34892,5.02559,5.02559,0,0,1,.50429-.41139,3.44685,3.44685,0,0,1,.572-.30523,3.3432,3.3432,0,0,1,.62373-.18579,3.06952,3.06952,0,0,1,1.30052,0,3.22356,3.22356,0,0,1,1.19436.491,5.02559,5.02559,0,0,1,.50429.41139,3.38792,3.38792,0,0,1,.96876,2.34892,3.72635,3.72635,0,0,1-.06636.65026,3.37387,3.37387,0,0,1-.18579.62373,4.71469,4.71469,0,0,1-.30522.57064,4.8801,4.8801,0,0,1-.41139.50429,5.02559,5.02559,0,0,1-.50429.41139,3.30547,3.30547,0,0,1-1.84463.55737" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
|
||||
</svg>
|
After Width: | Height: | Size: 12 KiB |
7
docs/tsconfig.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// This file is not used in compilation. It is here just for a nice editor experience.
|
||||
"extends": "@tsconfig/docusaurus/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
}
|
||||
}
|
|
@ -1,12 +1,7 @@
|
|||
import * as functions from 'firebase-functions'
|
||||
import * as admin from 'firebase-admin'
|
||||
|
||||
import {
|
||||
Contract,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
} from '../../common/contract'
|
||||
import { Contract } from '../../common/contract'
|
||||
import { User } from '../../common/user'
|
||||
import { getNewMultiBetInfo } from '../../common/new-bet'
|
||||
import { Answer, MAX_ANSWER_LENGTH } from '../../common/answer'
|
||||
|
@ -96,12 +91,7 @@ export const createAnswer = functions.runWith({ minInstances: 1 }).https.onCall(
|
|||
const loanAmount = 0
|
||||
|
||||
const { newBet, newPool, newTotalShares, newTotalBets } =
|
||||
getNewMultiBetInfo(
|
||||
answerId,
|
||||
amount,
|
||||
contract as FullContract<DPM, FreeResponse>,
|
||||
loanAmount
|
||||
)
|
||||
getNewMultiBetInfo(answerId, amount, contract, loanAmount)
|
||||
|
||||
const newBalance = user.balance - amount
|
||||
const betDoc = firestore.collection(`contracts/${contractId}/bets`).doc()
|
||||
|
|
|
@ -2,16 +2,13 @@ import * as admin from 'firebase-admin'
|
|||
import { z } from 'zod'
|
||||
|
||||
import {
|
||||
Binary,
|
||||
CPMMBinaryContract,
|
||||
Contract,
|
||||
CPMM,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
FreeResponseContract,
|
||||
MAX_DESCRIPTION_LENGTH,
|
||||
MAX_QUESTION_LENGTH,
|
||||
MAX_TAG_LENGTH,
|
||||
Numeric,
|
||||
NumericContract,
|
||||
OUTCOME_TYPES,
|
||||
} from '../../common/contract'
|
||||
import { slugify } from '../../common/util/slugify'
|
||||
|
@ -22,7 +19,6 @@ import { APIError, newEndpoint, validate, zTimestamp } from './api'
|
|||
|
||||
import {
|
||||
FIXED_ANTE,
|
||||
getAnteBets,
|
||||
getCpmmInitialLiquidity,
|
||||
getFreeAnswerAnte,
|
||||
getNumericAnte,
|
||||
|
@ -72,7 +68,7 @@ export const createContract = newEndpoint(['POST'], async (req, [user, _]) => {
|
|||
|
||||
// Uses utc time on server:
|
||||
const today = new Date()
|
||||
let freeMarketResetTime = today.setUTCHours(16, 0, 0, 0)
|
||||
let freeMarketResetTime = new Date().setUTCHours(16, 0, 0, 0)
|
||||
if (today.getTime() < freeMarketResetTime) {
|
||||
freeMarketResetTime = freeMarketResetTime - 24 * 60 * 60 * 1000
|
||||
}
|
||||
|
@ -122,30 +118,14 @@ export const createContract = newEndpoint(['POST'], async (req, [user, _]) => {
|
|||
|
||||
const providerId = isFree ? HOUSE_LIQUIDITY_PROVIDER_ID : user.id
|
||||
|
||||
if (outcomeType === 'BINARY' && contract.mechanism === 'dpm-2') {
|
||||
const yesBetDoc = firestore
|
||||
.collection(`contracts/${contract.id}/bets`)
|
||||
.doc()
|
||||
|
||||
const noBetDoc = firestore.collection(`contracts/${contract.id}/bets`).doc()
|
||||
|
||||
const { yesBet, noBet } = getAnteBets(
|
||||
user,
|
||||
contract as FullContract<DPM, Binary>,
|
||||
yesBetDoc.id,
|
||||
noBetDoc.id
|
||||
)
|
||||
|
||||
await yesBetDoc.set(yesBet)
|
||||
await noBetDoc.set(noBet)
|
||||
} else if (outcomeType === 'BINARY') {
|
||||
if (outcomeType === 'BINARY') {
|
||||
const liquidityDoc = firestore
|
||||
.collection(`contracts/${contract.id}/liquidity`)
|
||||
.doc()
|
||||
|
||||
const lp = getCpmmInitialLiquidity(
|
||||
providerId,
|
||||
contract as FullContract<CPMM, Binary>,
|
||||
contract as CPMMBinaryContract,
|
||||
liquidityDoc.id,
|
||||
ante
|
||||
)
|
||||
|
@ -165,7 +145,7 @@ export const createContract = newEndpoint(['POST'], async (req, [user, _]) => {
|
|||
|
||||
const anteBet = getFreeAnswerAnte(
|
||||
providerId,
|
||||
contract as FullContract<DPM, FreeResponse>,
|
||||
contract as FreeResponseContract,
|
||||
anteBetDoc.id
|
||||
)
|
||||
await anteBetDoc.set(anteBet)
|
||||
|
@ -176,7 +156,7 @@ export const createContract = newEndpoint(['POST'], async (req, [user, _]) => {
|
|||
|
||||
const anteBet = getNumericAnte(
|
||||
providerId,
|
||||
contract as FullContract<DPM, Numeric>,
|
||||
contract as NumericContract,
|
||||
ante,
|
||||
anteBetDoc.id
|
||||
)
|
||||
|
|
172
functions/src/create-notification.ts
Normal file
|
@ -0,0 +1,172 @@
|
|||
import * as admin from 'firebase-admin'
|
||||
import {
|
||||
Notification,
|
||||
notification_reason_types,
|
||||
notification_source_types,
|
||||
} from '../../common/notification'
|
||||
import { User } from '../../common/user'
|
||||
import { Contract } from '../../common/contract'
|
||||
import { getValues } from './utils'
|
||||
import { Comment } from '../../common/comment'
|
||||
import { uniq } from 'lodash'
|
||||
import { Bet } from '../../common/bet'
|
||||
import { Answer } from '../../common/answer'
|
||||
const firestore = admin.firestore()
|
||||
|
||||
type user_to_reason_texts = {
|
||||
[userId: string]: { text: string; reason: notification_reason_types }
|
||||
}
|
||||
|
||||
export const createNotification = async (
|
||||
sourceId: string,
|
||||
sourceType: notification_source_types,
|
||||
reason: notification_reason_types,
|
||||
sourceContract: Contract,
|
||||
sourceUser: User,
|
||||
idempotencyKey: string
|
||||
) => {
|
||||
const shouldGetNotification = (
|
||||
userId: string,
|
||||
userToReasonTexts: user_to_reason_texts
|
||||
) => {
|
||||
return (
|
||||
sourceUser.id != userId &&
|
||||
!Object.keys(userToReasonTexts).includes(userId)
|
||||
)
|
||||
}
|
||||
|
||||
const createUsersNotifications = async (
|
||||
userToReasonTexts: user_to_reason_texts
|
||||
) => {
|
||||
await Promise.all(
|
||||
Object.keys(userToReasonTexts).map(async (userId) => {
|
||||
const notificationRef = firestore
|
||||
.collection(`/users/${userId}/notifications`)
|
||||
.doc(idempotencyKey)
|
||||
const notification: Notification = {
|
||||
id: idempotencyKey,
|
||||
userId,
|
||||
reasonText: userToReasonTexts[userId].text,
|
||||
reason: userToReasonTexts[userId].reason,
|
||||
createdTime: Date.now(),
|
||||
isSeen: false,
|
||||
sourceId,
|
||||
sourceType,
|
||||
sourceContractId: sourceContract.id,
|
||||
sourceUserName: sourceUser.name,
|
||||
sourceUserUsername: sourceUser.username,
|
||||
sourceUserAvatarUrl: sourceUser.avatarUrl,
|
||||
}
|
||||
await notificationRef.set(notification)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Update for liquidity.
|
||||
// TODO: Find tagged users.
|
||||
// TODO: Find replies to comments.
|
||||
// TODO: Filter bets for only open bets
|
||||
if (
|
||||
sourceType === 'comment' ||
|
||||
sourceType === 'answer' ||
|
||||
sourceType === 'contract'
|
||||
) {
|
||||
let reasonTextPretext = getReasonTextFromReason(sourceType, reason)
|
||||
|
||||
const notifyContractCreator = async (
|
||||
userToReasonTexts: user_to_reason_texts
|
||||
) => {
|
||||
if (shouldGetNotification(sourceContract.creatorId, userToReasonTexts))
|
||||
userToReasonTexts[sourceContract.creatorId] = {
|
||||
text: `${reasonTextPretext} your question`,
|
||||
reason,
|
||||
}
|
||||
}
|
||||
|
||||
const notifyOtherAnswerersOnContract = async (
|
||||
userToReasonTexts: user_to_reason_texts
|
||||
) => {
|
||||
const answers = await getValues<Answer>(
|
||||
firestore
|
||||
.collection('contracts')
|
||||
.doc(sourceContract.id)
|
||||
.collection('answers')
|
||||
)
|
||||
const recipientUserIds = uniq(answers.map((answer) => answer.userId))
|
||||
recipientUserIds.forEach((userId) => {
|
||||
if (shouldGetNotification(userId, userToReasonTexts))
|
||||
userToReasonTexts[userId] = {
|
||||
text: `${reasonTextPretext} a question you submitted an answer to`,
|
||||
reason,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const notifyOtherCommentersOnContract = async (
|
||||
userToReasonTexts: user_to_reason_texts
|
||||
) => {
|
||||
const comments = await getValues<Comment>(
|
||||
firestore
|
||||
.collection('contracts')
|
||||
.doc(sourceContract.id)
|
||||
.collection('comments')
|
||||
)
|
||||
const recipientUserIds = uniq(comments.map((comment) => comment.userId))
|
||||
recipientUserIds.forEach((userId) => {
|
||||
if (shouldGetNotification(userId, userToReasonTexts))
|
||||
userToReasonTexts[userId] = {
|
||||
text: `${reasonTextPretext} a question you commented on`,
|
||||
reason,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const notifyOtherBettorsOnContract = async (
|
||||
userToReasonTexts: user_to_reason_texts
|
||||
) => {
|
||||
const betsSnap = await firestore
|
||||
.collection(`contracts/${sourceContract.id}/bets`)
|
||||
.get()
|
||||
const bets = betsSnap.docs.map((doc) => doc.data() as Bet)
|
||||
const recipientUserIds = uniq(bets.map((bet) => bet.userId))
|
||||
recipientUserIds.forEach((userId) => {
|
||||
if (shouldGetNotification(userId, userToReasonTexts))
|
||||
userToReasonTexts[userId] = {
|
||||
text: `${reasonTextPretext} a question you bet on`,
|
||||
reason,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getUsersToNotify = async () => {
|
||||
const userToReasonTexts: user_to_reason_texts = {}
|
||||
// The following functions modify the userToReasonTexts object in place.
|
||||
await notifyContractCreator(userToReasonTexts)
|
||||
await notifyOtherAnswerersOnContract(userToReasonTexts)
|
||||
await notifyOtherCommentersOnContract(userToReasonTexts)
|
||||
await notifyOtherBettorsOnContract(userToReasonTexts)
|
||||
return userToReasonTexts
|
||||
}
|
||||
|
||||
const userToReasonTexts = await getUsersToNotify()
|
||||
await createUsersNotifications(userToReasonTexts)
|
||||
}
|
||||
}
|
||||
|
||||
function getReasonTextFromReason(
|
||||
source: notification_source_types,
|
||||
reason: notification_reason_types
|
||||
) {
|
||||
// TODO: Find tagged users.
|
||||
// TODO: Find replies to comments.
|
||||
switch (source) {
|
||||
case 'comment':
|
||||
return 'commented on'
|
||||
case 'contract':
|
||||
return reason
|
||||
case 'answer':
|
||||
return 'answered'
|
||||
default:
|
||||
throw new Error('Invalid notification reason')
|
||||
}
|
||||
}
|
|
@ -110,9 +110,7 @@ const toDisplayResolution = (
|
|||
getValueFromBucket(resolution, contract).toString()
|
||||
)
|
||||
|
||||
const answer = (contract as FreeResponseContract).answers?.find(
|
||||
(a) => a.id === resolution
|
||||
)
|
||||
const answer = contract.answers.find((a) => a.id === resolution)
|
||||
if (answer) return answer.text
|
||||
return `#${resolution}`
|
||||
}
|
||||
|
|
|
@ -27,3 +27,5 @@ export * from './backup-db'
|
|||
export * from './change-user-info'
|
||||
export * from './market-close-emails'
|
||||
export * from './add-liquidity'
|
||||
export * from './on-create-answer'
|
||||
export * from './on-update-contract'
|
||||
|
|
32
functions/src/on-create-answer.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import * as functions from 'firebase-functions'
|
||||
import { getContract, getUser } from './utils'
|
||||
import { createNotification } from './create-notification'
|
||||
import { Answer } from '../../common/answer'
|
||||
|
||||
export const onCreateAnswer = functions.firestore
|
||||
.document('contracts/{contractId}/answers/{answerNumber}')
|
||||
.onCreate(async (change, context) => {
|
||||
const { contractId } = context.params as {
|
||||
contractId: string
|
||||
}
|
||||
const { eventId } = context
|
||||
const contract = await getContract(contractId)
|
||||
if (!contract)
|
||||
throw new Error('Could not find contract corresponding with answer')
|
||||
|
||||
const answer = change.data() as Answer
|
||||
// Ignore ante answer.
|
||||
if (answer.number === 0) return
|
||||
|
||||
const answerCreator = await getUser(answer.userId)
|
||||
if (!answerCreator) throw new Error('Could not find answer creator')
|
||||
|
||||
await createNotification(
|
||||
answer.id,
|
||||
'answer',
|
||||
'created',
|
||||
contract,
|
||||
answerCreator,
|
||||
eventId
|
||||
)
|
||||
})
|
|
@ -7,6 +7,7 @@ import { Comment } from '../../common/comment'
|
|||
import { sendNewCommentEmail } from './emails'
|
||||
import { Bet } from '../../common/bet'
|
||||
import { Answer } from '../../common/answer'
|
||||
import { createNotification } from './create-notification'
|
||||
|
||||
const firestore = admin.firestore()
|
||||
|
||||
|
@ -16,6 +17,7 @@ export const onCreateComment = functions.firestore
|
|||
const { contractId } = context.params as {
|
||||
contractId: string
|
||||
}
|
||||
const { eventId } = context
|
||||
|
||||
const contract = await getContract(contractId)
|
||||
if (!contract)
|
||||
|
@ -25,7 +27,16 @@ export const onCreateComment = functions.firestore
|
|||
const lastCommentTime = comment.createdTime
|
||||
|
||||
const commentCreator = await getUser(comment.userId)
|
||||
if (!commentCreator) throw new Error('Could not find contract creator')
|
||||
if (!commentCreator) throw new Error('Could not find comment creator')
|
||||
|
||||
await createNotification(
|
||||
comment.id,
|
||||
'comment',
|
||||
'created',
|
||||
contract,
|
||||
commentCreator,
|
||||
eventId
|
||||
)
|
||||
|
||||
await firestore
|
||||
.collection('contracts')
|
||||
|
|
38
functions/src/on-update-contract.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import * as functions from 'firebase-functions'
|
||||
import { getUser } from './utils'
|
||||
import { createNotification } from './create-notification'
|
||||
import { Contract } from '../../common/contract'
|
||||
|
||||
export const onUpdateContract = functions.firestore
|
||||
.document('contracts/{contractId}')
|
||||
.onUpdate(async (change, context) => {
|
||||
const contract = change.after.data() as Contract
|
||||
const { eventId } = context
|
||||
|
||||
const contractUpdater = await getUser(contract.creatorId)
|
||||
if (!contractUpdater) throw new Error('Could not find contract updater')
|
||||
|
||||
const previousValue = change.before.data() as Contract
|
||||
if (previousValue.isResolved !== contract.isResolved) {
|
||||
await createNotification(
|
||||
contract.id,
|
||||
'contract',
|
||||
'resolved',
|
||||
contract,
|
||||
contractUpdater,
|
||||
eventId
|
||||
)
|
||||
} else if (
|
||||
previousValue.closeTime !== contract.closeTime ||
|
||||
previousValue.description !== contract.description
|
||||
) {
|
||||
await createNotification(
|
||||
contract.id,
|
||||
'contract',
|
||||
'updated',
|
||||
contract,
|
||||
contractUpdater,
|
||||
eventId
|
||||
)
|
||||
}
|
||||
})
|
|
@ -4,7 +4,7 @@ import { partition, sumBy } from 'lodash'
|
|||
import { Bet } from '../../common/bet'
|
||||
import { getProbability } from '../../common/calculate'
|
||||
|
||||
import { Binary, CPMM, FullContract } from '../../common/contract'
|
||||
import { Contract } from '../../common/contract'
|
||||
import { noFees } from '../../common/fees'
|
||||
import { User } from '../../common/user'
|
||||
|
||||
|
@ -15,7 +15,7 @@ export const redeemShares = async (userId: string, contractId: string) => {
|
|||
if (!contractSnap.exists)
|
||||
return { status: 'error', message: 'Invalid contract' }
|
||||
|
||||
const contract = contractSnap.data() as FullContract<CPMM, Binary>
|
||||
const contract = contractSnap.data() as Contract
|
||||
if (contract.outcomeType !== 'BINARY' || contract.mechanism !== 'cpmm-1')
|
||||
return { status: 'success' }
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@ initAdmin()
|
|||
|
||||
import { Bet } from '../../../common/bet'
|
||||
import { getDpmProbability } from '../../../common/calculate-dpm'
|
||||
import { Binary, Contract, DPM, FullContract } from '../../../common/contract'
|
||||
import { DPMBinaryContract } from '../../../common/contract'
|
||||
|
||||
type DocRef = admin.firestore.DocumentReference
|
||||
const firestore = admin.firestore()
|
||||
|
||||
async function migrateContract(
|
||||
contractRef: DocRef,
|
||||
contract: FullContract<DPM, Binary>
|
||||
contract: DPMBinaryContract
|
||||
) {
|
||||
const bets = await contractRef
|
||||
.collection('bets')
|
||||
|
@ -34,9 +34,7 @@ async function migrateContract(
|
|||
|
||||
async function migrateContracts() {
|
||||
const snapshot = await firestore.collection('contracts').get()
|
||||
const contracts = snapshot.docs.map(
|
||||
(doc) => doc.data() as FullContract<DPM, Binary>
|
||||
)
|
||||
const contracts = snapshot.docs.map((doc) => doc.data() as DPMBinaryContract)
|
||||
|
||||
console.log('Loaded contracts', contracts.length)
|
||||
|
||||
|
|
|
@ -5,18 +5,15 @@ import { initAdmin } from './script-init'
|
|||
initAdmin()
|
||||
|
||||
import {
|
||||
Binary,
|
||||
Contract,
|
||||
CPMM,
|
||||
DPM,
|
||||
FullContract,
|
||||
DPMBinaryContract,
|
||||
CPMMBinaryContract,
|
||||
} from '../../../common/contract'
|
||||
import { Bet } from '../../../common/bet'
|
||||
import {
|
||||
calculateDpmPayout,
|
||||
getDpmProbability,
|
||||
} from '../../../common/calculate-dpm'
|
||||
import { User } from '../../../common/user'
|
||||
import { getCpmmInitialLiquidity } from '../../../common/antes'
|
||||
import { noFees } from '../../../common/fees'
|
||||
import { addObjects } from '../../../common/util/object'
|
||||
|
@ -28,7 +25,7 @@ const firestore = admin.firestore()
|
|||
async function recalculateContract(contractRef: DocRef, isCommit = false) {
|
||||
await firestore.runTransaction(async (transaction) => {
|
||||
const contractDoc = await transaction.get(contractRef)
|
||||
const contract = contractDoc.data() as FullContract<DPM, Binary>
|
||||
const contract = contractDoc.data() as DPMBinaryContract
|
||||
|
||||
if (!contract?.slug) {
|
||||
console.log('missing slug; id=', contractRef.id)
|
||||
|
@ -110,7 +107,7 @@ async function recalculateContract(contractRef: DocRef, isCommit = false) {
|
|||
{
|
||||
...contract,
|
||||
...contractUpdate,
|
||||
} as FullContract<CPMM, Binary>,
|
||||
} as CPMMBinaryContract,
|
||||
liquidityDocRef.id,
|
||||
ante
|
||||
)
|
||||
|
|
|
@ -4,7 +4,7 @@ import { sortBy, sumBy } from 'lodash'
|
|||
import { initAdmin } from './script-init'
|
||||
initAdmin()
|
||||
|
||||
import { Binary, Contract, DPM, FullContract } from '../../../common/contract'
|
||||
import { Contract, DPMBinaryContract } from '../../../common/contract'
|
||||
import { Bet } from '../../../common/bet'
|
||||
import {
|
||||
calculateDpmShares,
|
||||
|
@ -32,7 +32,7 @@ async function recalculateContract(
|
|||
|
||||
await firestore.runTransaction(async (transaction) => {
|
||||
const contractDoc = await transaction.get(contractRef)
|
||||
const contract = contractDoc.data() as FullContract<DPM, Binary>
|
||||
const contract = contractDoc.data() as DPMBinaryContract
|
||||
|
||||
const betDocs = await transaction.get(contractRef.collection('bets'))
|
||||
const bets = sortBy(
|
||||
|
|
|
@ -2,7 +2,7 @@ import { partition, sumBy } from 'lodash'
|
|||
import * as admin from 'firebase-admin'
|
||||
import * as functions from 'firebase-functions'
|
||||
|
||||
import { Binary, CPMM, FullContract } from '../../common/contract'
|
||||
import { BinaryContract } from '../../common/contract'
|
||||
import { User } from '../../common/user'
|
||||
import { getCpmmSellBetInfo } from '../../common/sell-bet'
|
||||
import { addObjects, removeUndefinedProps } from '../../common/util/object'
|
||||
|
@ -35,7 +35,7 @@ export const sellShares = functions.runWith({ minInstances: 1 }).https.onCall(
|
|||
const contractSnap = await transaction.get(contractDoc)
|
||||
if (!contractSnap.exists)
|
||||
return { status: 'error', message: 'Invalid contract' }
|
||||
const contract = contractSnap.data() as FullContract<CPMM, Binary>
|
||||
const contract = contractSnap.data() as BinaryContract
|
||||
const { closeTime, mechanism, collectedFees, volume } = contract
|
||||
|
||||
if (mechanism !== 'cpmm-1')
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"private": true,
|
||||
"workspaces": [
|
||||
"common",
|
||||
"docs",
|
||||
"functions",
|
||||
"web"
|
||||
],
|
||||
|
@ -16,5 +17,7 @@
|
|||
"prettier": "2.5.0",
|
||||
"typescript": "4.6.4"
|
||||
},
|
||||
"version": "1.0.0"
|
||||
"resolutions": {
|
||||
"@types/react": "17.0.43"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||
import { XIcon } from '@heroicons/react/solid'
|
||||
|
||||
import { Answer } from 'common/answer'
|
||||
import { DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { FreeResponseContract } from 'common/contract'
|
||||
import { BuyAmountInput } from '../amount-input'
|
||||
import { Col } from '../layout/col'
|
||||
import { APIError, placeBet } from 'web/lib/firebase/api-call'
|
||||
|
@ -27,7 +27,7 @@ import { Bet } from 'common/bet'
|
|||
|
||||
export function AnswerBetPanel(props: {
|
||||
answer: Answer
|
||||
contract: FullContract<DPM, FreeResponse>
|
||||
contract: FreeResponseContract
|
||||
closePanel: () => void
|
||||
className?: string
|
||||
isModal?: boolean
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import clsx from 'clsx'
|
||||
|
||||
import { Answer } from 'common/answer'
|
||||
import { DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { FreeResponseContract } from 'common/contract'
|
||||
import { Col } from '../layout/col'
|
||||
import { Row } from '../layout/row'
|
||||
import { Avatar } from '../avatar'
|
||||
|
@ -13,7 +13,7 @@ import { Linkify } from '../linkify'
|
|||
|
||||
export function AnswerItem(props: {
|
||||
answer: Answer
|
||||
contract: FullContract<DPM, FreeResponse>
|
||||
contract: FreeResponseContract
|
||||
showChoice: 'radio' | 'checkbox' | undefined
|
||||
chosenProb: number | undefined
|
||||
totalChosenProb?: number
|
||||
|
|
|
@ -2,7 +2,7 @@ import clsx from 'clsx'
|
|||
import { sum, mapValues } from 'lodash'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { Contract, FreeResponse } from 'common/contract'
|
||||
import { Col } from '../layout/col'
|
||||
import { resolveMarket } from 'web/lib/firebase/fn-call'
|
||||
import { Row } from '../layout/row'
|
||||
|
@ -11,7 +11,7 @@ import { ResolveConfirmationButton } from '../confirmation-button'
|
|||
import { removeUndefinedProps } from 'common/util/object'
|
||||
|
||||
export function AnswerResolvePanel(props: {
|
||||
contract: FullContract<DPM, FreeResponse>
|
||||
contract: Contract & FreeResponse
|
||||
resolveOption: 'CHOOSE' | 'CHOOSE_MULTIPLE' | 'CANCEL' | undefined
|
||||
setResolveOption: (
|
||||
option: 'CHOOSE' | 'CHOOSE_MULTIPLE' | 'CANCEL' | undefined
|
||||
|
|
|
@ -5,7 +5,7 @@ import { groupBy, sortBy, sumBy } from 'lodash'
|
|||
import { memo } from 'react'
|
||||
|
||||
import { Bet } from 'common/bet'
|
||||
import { DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { FreeResponseContract } from 'common/contract'
|
||||
import { getOutcomeProbability } from 'common/calculate'
|
||||
import { useBets } from 'web/hooks/use-bets'
|
||||
import { useWindowSize } from 'web/hooks/use-window-size'
|
||||
|
@ -13,7 +13,7 @@ import { useWindowSize } from 'web/hooks/use-window-size'
|
|||
const NUM_LINES = 6
|
||||
|
||||
export const AnswersGraph = memo(function AnswersGraph(props: {
|
||||
contract: FullContract<DPM, FreeResponse>
|
||||
contract: FreeResponseContract
|
||||
bets: Bet[]
|
||||
height?: number
|
||||
}) {
|
||||
|
@ -161,10 +161,7 @@ function formatTime(time: number, includeTime: boolean) {
|
|||
return dayjs(time).format('MMM D')
|
||||
}
|
||||
|
||||
const computeProbsByOutcome = (
|
||||
bets: Bet[],
|
||||
contract: FullContract<DPM, FreeResponse>
|
||||
) => {
|
||||
const computeProbsByOutcome = (bets: Bet[], contract: FreeResponseContract) => {
|
||||
const { totalBets } = contract
|
||||
|
||||
const betsByOutcome = groupBy(bets, (bet) => bet.outcome)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { sortBy, partition, sum, uniq } from 'lodash'
|
||||
import { useLayoutEffect, useState } from 'react'
|
||||
|
||||
import { DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { FreeResponseContract } from 'common/contract'
|
||||
import { Col } from '../layout/col'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { getDpmOutcomeProbability } from 'common/calculate-dpm'
|
||||
|
@ -25,9 +25,7 @@ import { UserLink } from 'web/components/user-page'
|
|||
import { Linkify } from 'web/components/linkify'
|
||||
import { BuyButton } from 'web/components/yes-no-selector'
|
||||
|
||||
export function AnswersPanel(props: {
|
||||
contract: FullContract<DPM, FreeResponse>
|
||||
}) {
|
||||
export function AnswersPanel(props: { contract: FreeResponseContract }) {
|
||||
const { contract } = props
|
||||
const { creatorId, resolution, resolutions, totalBets } = contract
|
||||
|
||||
|
@ -154,7 +152,7 @@ export function AnswersPanel(props: {
|
|||
}
|
||||
|
||||
function getAnswerItems(
|
||||
contract: FullContract<DPM, FreeResponse>,
|
||||
contract: FreeResponseContract,
|
||||
answers: Answer[],
|
||||
user: User | undefined | null
|
||||
) {
|
||||
|
@ -182,7 +180,7 @@ function getAnswerItems(
|
|||
}
|
||||
|
||||
function OpenAnswer(props: {
|
||||
contract: FullContract<any, FreeResponse>
|
||||
contract: FreeResponseContract
|
||||
answer: Answer
|
||||
items: ActivityItem[]
|
||||
type: string
|
||||
|
|
|
@ -2,7 +2,7 @@ import clsx from 'clsx'
|
|||
import { useState } from 'react'
|
||||
import Textarea from 'react-expanding-textarea'
|
||||
|
||||
import { DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { FreeResponseContract } from 'common/contract'
|
||||
import { BuyAmountInput } from '../amount-input'
|
||||
import { Col } from '../layout/col'
|
||||
import { createAnswer } from 'web/lib/firebase/fn-call'
|
||||
|
@ -23,9 +23,7 @@ import { firebaseLogin } from 'web/lib/firebase/users'
|
|||
import { Bet } from 'common/bet'
|
||||
import { MAX_ANSWER_LENGTH } from 'common/answer'
|
||||
|
||||
export function CreateAnswerPanel(props: {
|
||||
contract: FullContract<DPM, FreeResponse>
|
||||
}) {
|
||||
export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
|
||||
const { contract } = props
|
||||
const user = useUser()
|
||||
const [text, setText] = useState('')
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'
|
|||
import { partition, sumBy } from 'lodash'
|
||||
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { Binary, CPMM, DPM, FullContract } from 'common/contract'
|
||||
import { BinaryContract, CPMMBinaryContract } from 'common/contract'
|
||||
import { Col } from './layout/col'
|
||||
import { Row } from './layout/row'
|
||||
import { Spacer } from './layout/spacer'
|
||||
|
@ -39,7 +39,7 @@ import { useSaveShares } from './use-save-shares'
|
|||
import { SignUpPrompt } from './sign-up-prompt'
|
||||
|
||||
export function BetPanel(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
className?: string
|
||||
}) {
|
||||
const { contract, className } = props
|
||||
|
@ -78,7 +78,7 @@ export function BetPanel(props: {
|
|||
}
|
||||
|
||||
export function BetPanelSwitcher(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
className?: string
|
||||
title?: string // Set if BetPanel is on a feed modal
|
||||
selected?: 'YES' | 'NO'
|
||||
|
@ -157,16 +157,19 @@ export function BetPanelSwitcher(props: {
|
|||
text={tradeType === 'BUY' ? title ?? 'Place a trade' : 'Sell shares'}
|
||||
/>
|
||||
|
||||
{tradeType === 'SELL' && user && sharesOutcome && (
|
||||
<SellPanel
|
||||
contract={contract as FullContract<CPMM, Binary>}
|
||||
shares={yesShares || noShares}
|
||||
sharesOutcome={sharesOutcome}
|
||||
user={user}
|
||||
userBets={userBets ?? []}
|
||||
onSellSuccess={onBetSuccess}
|
||||
/>
|
||||
)}
|
||||
{tradeType === 'SELL' &&
|
||||
mechanism == 'cpmm-1' &&
|
||||
user &&
|
||||
sharesOutcome && (
|
||||
<SellPanel
|
||||
contract={contract}
|
||||
shares={yesShares || noShares}
|
||||
sharesOutcome={sharesOutcome}
|
||||
user={user}
|
||||
userBets={userBets ?? []}
|
||||
onSellSuccess={onBetSuccess}
|
||||
/>
|
||||
)}
|
||||
|
||||
{tradeType === 'BUY' && (
|
||||
<BuyPanel
|
||||
|
@ -184,7 +187,7 @@ export function BetPanelSwitcher(props: {
|
|||
}
|
||||
|
||||
function BuyPanel(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
user: User | null | undefined
|
||||
selected?: 'YES' | 'NO'
|
||||
onBuySuccess?: () => void
|
||||
|
@ -374,7 +377,7 @@ function BuyPanel(props: {
|
|||
}
|
||||
|
||||
export function SellPanel(props: {
|
||||
contract: FullContract<CPMM, Binary>
|
||||
contract: CPMMBinaryContract
|
||||
userBets: Bet[]
|
||||
shares: number
|
||||
sharesOutcome: 'YES' | 'NO'
|
||||
|
|
|
@ -3,7 +3,7 @@ import clsx from 'clsx'
|
|||
|
||||
import { BetPanelSwitcher } from './bet-panel'
|
||||
import { YesNoSelector } from './yes-no-selector'
|
||||
import { Binary, CPMM, DPM, FullContract } from 'common/contract'
|
||||
import { BinaryContract } from 'common/contract'
|
||||
import { Modal } from './layout/modal'
|
||||
import { SellButton } from './sell-button'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
|
@ -12,7 +12,7 @@ import { useSaveShares } from './use-save-shares'
|
|||
|
||||
// Inline version of a bet panel. Opens BetPanel in a new modal.
|
||||
export default function BetRow(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
className?: string
|
||||
btnClassName?: string
|
||||
betPanelClassName?: string
|
||||
|
|
|
@ -48,13 +48,24 @@ import {
|
|||
import { useTimeSinceFirstRender } from 'web/hooks/use-time-since-first-render'
|
||||
import { trackLatency } from 'web/lib/firebase/tracking'
|
||||
import { NumericContract } from 'common/contract'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { SellSharesModal } from './sell-modal'
|
||||
|
||||
type BetSort = 'newest' | 'profit' | 'closeTime' | 'value'
|
||||
type BetFilter = 'open' | 'sold' | 'closed' | 'resolved' | 'all'
|
||||
|
||||
export function BetsList(props: { user: User }) {
|
||||
const { user } = props
|
||||
const bets = useUserBets(user.id, { includeRedemptions: true })
|
||||
export function BetsList(props: { user: User; hideBetsBefore?: number }) {
|
||||
const { user, hideBetsBefore } = props
|
||||
|
||||
const signedInUser = useUser()
|
||||
const isYourBets = user.id === signedInUser?.id
|
||||
|
||||
const allBets = useUserBets(user.id, { includeRedemptions: true })
|
||||
// Hide bets before 06-01-2022 if this isn't your own profile
|
||||
// NOTE: This means public profits also begin on 06-01-2022 as well.
|
||||
const bets = allBets?.filter(
|
||||
(bet) => bet.createdTime >= (hideBetsBefore ?? 0)
|
||||
)
|
||||
const [contracts, setContracts] = useState<Contract[] | undefined>()
|
||||
|
||||
const [sort, setSort] = useState<BetSort>('newest')
|
||||
|
@ -75,7 +86,8 @@ export function BetsList(props: { user: User }) {
|
|||
disposed = true
|
||||
}
|
||||
}
|
||||
}, [bets])
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [allBets, hideBetsBefore])
|
||||
|
||||
const getTime = useTimeSinceFirstRender()
|
||||
useEffect(() => {
|
||||
|
@ -210,11 +222,12 @@ export function BetsList(props: { user: User }) {
|
|||
<NoBets />
|
||||
) : (
|
||||
displayedContracts.map((contract) => (
|
||||
<MyContractBets
|
||||
<ContractBets
|
||||
key={contract.id}
|
||||
contract={contract}
|
||||
bets={contractBets[contract.id] ?? []}
|
||||
metric={sort === 'profit' ? 'profit' : 'value'}
|
||||
isYourBets={isYourBets}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
@ -234,12 +247,13 @@ const NoBets = () => {
|
|||
)
|
||||
}
|
||||
|
||||
function MyContractBets(props: {
|
||||
function ContractBets(props: {
|
||||
contract: Contract
|
||||
bets: Bet[]
|
||||
metric: 'profit' | 'value'
|
||||
isYourBets: boolean
|
||||
}) {
|
||||
const { bets, contract, metric } = props
|
||||
const { bets, contract, metric, isYourBets } = props
|
||||
const { resolution, outcomeType } = contract
|
||||
|
||||
const resolutionValue = (contract as NumericContract).resolutionValue
|
||||
|
@ -247,7 +261,6 @@ function MyContractBets(props: {
|
|||
const [collapsed, setCollapsed] = useState(true)
|
||||
|
||||
const isBinary = outcomeType === 'BINARY'
|
||||
const probPercent = getBinaryProbPercent(contract)
|
||||
|
||||
const { payout, profit, profitPercent, invested } = getContractBetMetrics(
|
||||
contract,
|
||||
|
@ -257,12 +270,14 @@ function MyContractBets(props: {
|
|||
<div
|
||||
tabIndex={0}
|
||||
className={clsx(
|
||||
'collapse collapse-arrow relative cursor-pointer bg-white p-4 pr-6',
|
||||
'collapse collapse-arrow relative bg-white p-4 pr-6',
|
||||
collapsed ? 'collapse-close' : 'collapse-open pb-2'
|
||||
)}
|
||||
onClick={() => setCollapsed((collapsed) => !collapsed)}
|
||||
>
|
||||
<Row className="flex-wrap gap-2">
|
||||
<Row
|
||||
className="cursor-pointer flex-wrap gap-2"
|
||||
onClick={() => setCollapsed((collapsed) => !collapsed)}
|
||||
>
|
||||
<Col className="flex-[2] gap-1">
|
||||
<Row className="mr-2 max-w-lg">
|
||||
<Link href={contractPath(contract)}>
|
||||
|
@ -282,24 +297,27 @@ function MyContractBets(props: {
|
|||
</Row>
|
||||
|
||||
<Row className="flex-1 items-center gap-2 text-sm text-gray-500">
|
||||
{(isBinary || resolution) && (
|
||||
{resolution ? (
|
||||
<>
|
||||
{resolution ? (
|
||||
<div>
|
||||
Resolved{' '}
|
||||
<OutcomeLabel
|
||||
outcome={resolution}
|
||||
value={resolutionValue}
|
||||
contract={contract}
|
||||
truncate="short"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-primary text-lg">{probPercent}</div>
|
||||
)}
|
||||
<div>
|
||||
Resolved{' '}
|
||||
<OutcomeLabel
|
||||
outcome={resolution}
|
||||
value={resolutionValue}
|
||||
contract={contract}
|
||||
truncate="short"
|
||||
/>
|
||||
</div>
|
||||
<div>•</div>
|
||||
</>
|
||||
)}
|
||||
) : isBinary ? (
|
||||
<>
|
||||
<div className="text-primary text-lg">
|
||||
{getBinaryProbPercent(contract)}
|
||||
</div>
|
||||
<div>•</div>
|
||||
</>
|
||||
) : null}
|
||||
<UserLink
|
||||
name={contract.creatorName}
|
||||
username={contract.creatorUsername}
|
||||
|
@ -325,26 +343,32 @@ function MyContractBets(props: {
|
|||
>
|
||||
<Spacer h={8} />
|
||||
|
||||
<MyBetsSummary
|
||||
<BetsSummary
|
||||
className="mr-5 flex-1 sm:mr-8"
|
||||
contract={contract}
|
||||
bets={bets}
|
||||
isYourBets={isYourBets}
|
||||
/>
|
||||
|
||||
<Spacer h={8} />
|
||||
|
||||
<ContractBetsTable contract={contract} bets={bets} />
|
||||
<ContractBetsTable
|
||||
contract={contract}
|
||||
bets={bets}
|
||||
isYourBets={isYourBets}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function MyBetsSummary(props: {
|
||||
export function BetsSummary(props: {
|
||||
contract: Contract
|
||||
bets: Bet[]
|
||||
isYourBets: boolean
|
||||
className?: string
|
||||
}) {
|
||||
const { contract, className } = props
|
||||
const { contract, isYourBets, className } = props
|
||||
const { resolution, outcomeType, mechanism } = contract
|
||||
const isBinary = outcomeType === 'BINARY'
|
||||
const isCpmm = mechanism === 'cpmm-1'
|
||||
|
@ -360,10 +384,11 @@ export function MyBetsSummary(props: {
|
|||
const noWinnings = sumBy(excludeSalesAndAntes, (bet) =>
|
||||
calculatePayout(contract, bet, 'NO')
|
||||
)
|
||||
const { invested, profitPercent, payout, profit } = getContractBetMetrics(
|
||||
contract,
|
||||
bets
|
||||
)
|
||||
const { invested, profitPercent, payout, profit, totalShares } =
|
||||
getContractBetMetrics(contract, bets)
|
||||
|
||||
const [showSellModal, setShowSellModal] = useState(false)
|
||||
const user = useUser()
|
||||
|
||||
return (
|
||||
<Row className={clsx('flex-wrap gap-4 sm:flex-nowrap sm:gap-6', className)}>
|
||||
|
@ -419,6 +444,31 @@ export function MyBetsSummary(props: {
|
|||
<div className="whitespace-nowrap text-sm text-gray-500">Profit</div>
|
||||
<div className="whitespace-nowrap">
|
||||
{formatMoney(profit)} <ProfitBadge profitPercent={profitPercent} />
|
||||
{isYourBets &&
|
||||
isCpmm &&
|
||||
isBinary &&
|
||||
!resolution &&
|
||||
invested > 0 &&
|
||||
user && (
|
||||
<>
|
||||
<button
|
||||
className="btn btn-sm ml-2"
|
||||
onClick={() => setShowSellModal(true)}
|
||||
>
|
||||
Sell
|
||||
</button>
|
||||
{showSellModal && (
|
||||
<SellSharesModal
|
||||
contract={contract}
|
||||
user={user}
|
||||
userBets={bets}
|
||||
shares={totalShares.YES || totalShares.NO}
|
||||
sharesOutcome={totalShares.YES ? 'YES' : 'NO'}
|
||||
setOpen={setShowSellModal}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -429,9 +479,10 @@ export function MyBetsSummary(props: {
|
|||
export function ContractBetsTable(props: {
|
||||
contract: Contract
|
||||
bets: Bet[]
|
||||
isYourBets: boolean
|
||||
className?: string
|
||||
}) {
|
||||
const { contract, className } = props
|
||||
const { contract, className, isYourBets } = props
|
||||
|
||||
const bets = props.bets.filter((b) => !b.isAnte)
|
||||
|
||||
|
@ -500,6 +551,7 @@ export function ContractBetsTable(props: {
|
|||
bet={bet}
|
||||
saleBet={salesDict[bet.id]}
|
||||
contract={contract}
|
||||
isYourBet={isYourBets}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
|
@ -508,8 +560,13 @@ export function ContractBetsTable(props: {
|
|||
)
|
||||
}
|
||||
|
||||
function BetRow(props: { bet: Bet; contract: Contract; saleBet?: Bet }) {
|
||||
const { bet, saleBet, contract } = props
|
||||
function BetRow(props: {
|
||||
bet: Bet
|
||||
contract: Contract
|
||||
saleBet?: Bet
|
||||
isYourBet: boolean
|
||||
}) {
|
||||
const { bet, saleBet, contract, isYourBet } = props
|
||||
const {
|
||||
amount,
|
||||
outcome,
|
||||
|
@ -550,7 +607,8 @@ function BetRow(props: { bet: Bet; contract: Contract; saleBet?: Bet }) {
|
|||
return (
|
||||
<tr>
|
||||
<td className="text-neutral">
|
||||
{!isCPMM &&
|
||||
{isYourBet &&
|
||||
!isCPMM &&
|
||||
!isResolved &&
|
||||
!isClosed &&
|
||||
!isSold &&
|
||||
|
|
|
@ -16,7 +16,7 @@ export function Donation(props: { txn: Txn }) {
|
|||
return (
|
||||
<div className="mb-2 flow-root pr-2 md:pr-0">
|
||||
<div className="relative flex items-center space-x-3">
|
||||
<Avatar username={user.name} avatarUrl={user.avatarUrl} size="sm" />
|
||||
<Avatar username={user.username} avatarUrl={user.avatarUrl} size="sm" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="mt-0.5 text-sm text-gray-500">
|
||||
<UserLink
|
||||
|
|
|
@ -115,7 +115,6 @@ export function ContractSearch(props: {
|
|||
{showCategorySelector && (
|
||||
<CategorySelector
|
||||
className="mb-2"
|
||||
user={user}
|
||||
category={category}
|
||||
setCategory={setCategory}
|
||||
/>
|
||||
|
|
|
@ -2,19 +2,12 @@ import clsx from 'clsx'
|
|||
import Link from 'next/link'
|
||||
import { Row } from '../layout/row'
|
||||
import { formatLargeNumber, formatPercent } from 'common/util/format'
|
||||
import {
|
||||
Contract,
|
||||
contractPath,
|
||||
getBinaryProbPercent,
|
||||
} from 'web/lib/firebase/contracts'
|
||||
import { contractPath, getBinaryProbPercent } from 'web/lib/firebase/contracts'
|
||||
import { Col } from '../layout/col'
|
||||
import {
|
||||
Binary,
|
||||
CPMM,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
Contract,
|
||||
BinaryContract,
|
||||
FreeResponseContract,
|
||||
FullContract,
|
||||
NumericContract,
|
||||
} from 'common/contract'
|
||||
import {
|
||||
|
@ -65,9 +58,7 @@ export function ContractCard(props: {
|
|||
<Col className="relative flex-1 gap-3 pr-1">
|
||||
<div
|
||||
className={clsx(
|
||||
'peer absolute -left-6 -top-4 -bottom-4 z-10',
|
||||
// Hack: Extend the clickable area for closed markets
|
||||
showQuickBet ? 'right-0' : 'right-[-6.5rem]'
|
||||
'peer absolute -left-6 -top-4 -bottom-4 right-0 z-10'
|
||||
)}
|
||||
>
|
||||
<Link href={contractPath(contract)}>
|
||||
|
@ -85,15 +76,12 @@ export function ContractCard(props: {
|
|||
{outcomeType === 'FREE_RESPONSE' &&
|
||||
(resolution ? (
|
||||
<FreeResponseOutcomeLabel
|
||||
contract={contract as FreeResponseContract}
|
||||
contract={contract}
|
||||
resolution={resolution}
|
||||
truncate={'long'}
|
||||
/>
|
||||
) : (
|
||||
<FreeResponseTopAnswer
|
||||
contract={contract as FullContract<DPM, FreeResponse>}
|
||||
truncate="long"
|
||||
/>
|
||||
<FreeResponseTopAnswer contract={contract} truncate="long" />
|
||||
))}
|
||||
|
||||
<MiscDetails
|
||||
|
@ -116,14 +104,14 @@ export function ContractCard(props: {
|
|||
{outcomeType === 'NUMERIC' && (
|
||||
<NumericResolutionOrExpectation
|
||||
className="items-center"
|
||||
contract={contract as NumericContract}
|
||||
contract={contract}
|
||||
/>
|
||||
)}
|
||||
|
||||
{outcomeType === 'FREE_RESPONSE' && (
|
||||
<FreeResponseResolutionOrChance
|
||||
className="self-end text-gray-600"
|
||||
contract={contract as FullContract<DPM, FreeResponse>}
|
||||
contract={contract}
|
||||
truncate="long"
|
||||
/>
|
||||
)}
|
||||
|
@ -137,7 +125,7 @@ export function ContractCard(props: {
|
|||
}
|
||||
|
||||
export function BinaryResolutionOrChance(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
large?: boolean
|
||||
className?: string
|
||||
}) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Contract, tradingAllowed } from 'web/lib/firebase/contracts'
|
||||
import { tradingAllowed } from 'web/lib/firebase/contracts'
|
||||
import { Col } from '../layout/col'
|
||||
import { Spacer } from '../layout/spacer'
|
||||
import { ContractProbGraph } from './contract-prob-graph'
|
||||
|
@ -16,12 +16,7 @@ import { Bet } from 'common/bet'
|
|||
import { Comment } from 'common/comment'
|
||||
import BetRow from '../bet-row'
|
||||
import { AnswersGraph } from '../answers/answers-graph'
|
||||
import {
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
NumericContract,
|
||||
} from 'common/contract'
|
||||
import { Contract } from 'common/contract'
|
||||
import { ContractDescription } from './contract-description'
|
||||
import { ContractDetails } from './contract-details'
|
||||
import { ShareMarket } from '../share-market'
|
||||
|
@ -58,7 +53,7 @@ export const ContractOverview = (props: {
|
|||
|
||||
{outcomeType === 'NUMERIC' && (
|
||||
<NumericResolutionOrExpectation
|
||||
contract={contract as NumericContract}
|
||||
contract={contract}
|
||||
className="hidden items-end xl:flex"
|
||||
/>
|
||||
)}
|
||||
|
@ -82,9 +77,7 @@ export const ContractOverview = (props: {
|
|||
|
||||
{outcomeType === 'NUMERIC' && (
|
||||
<Row className="items-center justify-between gap-4 xl:hidden">
|
||||
<NumericResolutionOrExpectation
|
||||
contract={contract as NumericContract}
|
||||
/>
|
||||
<NumericResolutionOrExpectation contract={contract} />
|
||||
</Row>
|
||||
)}
|
||||
|
||||
|
@ -97,14 +90,9 @@ export const ContractOverview = (props: {
|
|||
<Spacer h={4} />
|
||||
{isBinary && <ContractProbGraph contract={contract} bets={bets} />}{' '}
|
||||
{outcomeType === 'FREE_RESPONSE' && (
|
||||
<AnswersGraph
|
||||
contract={contract as FullContract<DPM, FreeResponse>}
|
||||
bets={bets}
|
||||
/>
|
||||
)}
|
||||
{outcomeType === 'NUMERIC' && (
|
||||
<NumericGraph contract={contract as NumericContract} />
|
||||
<AnswersGraph contract={contract} bets={bets} />
|
||||
)}
|
||||
{outcomeType === 'NUMERIC' && <NumericGraph contract={contract} />}
|
||||
{(contract.description || isCreator) && <Spacer h={6} />}
|
||||
{isCreator && <ShareMarket className="px-2" contract={contract} />}
|
||||
<ContractDescription
|
||||
|
|
|
@ -4,12 +4,12 @@ import dayjs from 'dayjs'
|
|||
import { memo } from 'react'
|
||||
import { Bet } from 'common/bet'
|
||||
import { getInitialProbability } from 'common/calculate'
|
||||
import { Binary, CPMM, DPM, FullContract } from 'common/contract'
|
||||
import { BinaryContract } from 'common/contract'
|
||||
import { useBetsWithoutAntes } from 'web/hooks/use-bets'
|
||||
import { useWindowSize } from 'web/hooks/use-window-size'
|
||||
|
||||
export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
bets: Bet[]
|
||||
height?: number
|
||||
}) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Comment } from 'web/lib/firebase/comments'
|
|||
import { User } from 'common/user'
|
||||
import { useBets } from 'web/hooks/use-bets'
|
||||
import { ContractActivity } from '../feed/contract-activity'
|
||||
import { ContractBetsTable, MyBetsSummary } from '../bets-list'
|
||||
import { ContractBetsTable, BetsSummary } from '../bets-list'
|
||||
import { Spacer } from '../layout/spacer'
|
||||
import { Tabs } from '../layout/tabs'
|
||||
import { Col } from '../layout/col'
|
||||
|
@ -67,13 +67,14 @@ export function ContractTabs(props: {
|
|||
|
||||
const yourTrades = (
|
||||
<div>
|
||||
<MyBetsSummary
|
||||
<BetsSummary
|
||||
className="px-2"
|
||||
contract={contract}
|
||||
bets={userBets ?? []}
|
||||
isYourBets
|
||||
/>
|
||||
<Spacer h={6} />
|
||||
<ContractBetsTable contract={contract} bets={userBets ?? []} />
|
||||
<ContractBetsTable contract={contract} bets={userBets ?? []} isYourBets />
|
||||
<Spacer h={12} />
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -5,17 +5,8 @@ import {
|
|||
getTopAnswer,
|
||||
} from 'common/calculate'
|
||||
import { getExpectedValue } from 'common/calculate-dpm'
|
||||
import {
|
||||
Contract,
|
||||
FullContract,
|
||||
CPMM,
|
||||
DPM,
|
||||
Binary,
|
||||
NumericContract,
|
||||
FreeResponseContract,
|
||||
resolution,
|
||||
} from 'common/contract'
|
||||
import { User } from 'common/user'
|
||||
import { Contract, NumericContract, resolution } from 'common/contract'
|
||||
import {
|
||||
formatLargeNumber,
|
||||
formatMoney,
|
||||
|
@ -40,12 +31,12 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
|||
const userBets = useUserContractBets(user.id, contract.id)
|
||||
const topAnswer =
|
||||
contract.outcomeType === 'FREE_RESPONSE'
|
||||
? getTopAnswer(contract as FreeResponseContract)
|
||||
? getTopAnswer(contract)
|
||||
: undefined
|
||||
|
||||
// TODO: yes/no from useSaveShares doesn't work on numeric contracts
|
||||
const { yesFloorShares, noFloorShares } = useSaveShares(
|
||||
contract as FullContract<DPM | CPMM, Binary | FreeResponseContract>,
|
||||
contract,
|
||||
userBets,
|
||||
topAnswer?.number.toString() || undefined
|
||||
)
|
||||
|
@ -227,10 +218,10 @@ function QuickOutcomeView(props: {
|
|||
display = getBinaryProbPercent(contract)
|
||||
break
|
||||
case 'NUMERIC':
|
||||
display = formatLargeNumber(getExpectedValue(contract as NumericContract))
|
||||
display = formatLargeNumber(getExpectedValue(contract))
|
||||
break
|
||||
case 'FREE_RESPONSE': {
|
||||
const topAnswer = getTopAnswer(contract as FreeResponseContract)
|
||||
const topAnswer = getTopAnswer(contract)
|
||||
display =
|
||||
topAnswer &&
|
||||
formatPercent(getOutcomeProbability(contract, topAnswer.id))
|
||||
|
@ -258,7 +249,7 @@ function getProb(contract: Contract) {
|
|||
: outcomeType === 'FREE_RESPONSE'
|
||||
? getOutcomeProbability(contract, getTopAnswer(contract)?.id || '')
|
||||
: outcomeType === 'NUMERIC'
|
||||
? getNumericScale(contract as NumericContract)
|
||||
? getNumericScale(contract)
|
||||
: 1 // Should not happen
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Answer } from 'common/answer'
|
|||
import { Bet } from 'common/bet'
|
||||
import { getOutcomeProbability } from 'common/calculate'
|
||||
import { Comment } from 'common/comment'
|
||||
import { Contract, DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { Contract, FreeResponseContract } from 'common/contract'
|
||||
import { User } from 'common/user'
|
||||
import { mapCommentsByBetId } from 'web/lib/firebase/comments'
|
||||
|
||||
|
@ -188,7 +188,7 @@ function groupBets(
|
|||
}
|
||||
|
||||
function getAnswerGroups(
|
||||
contract: FullContract<DPM, FreeResponse>,
|
||||
contract: FreeResponseContract,
|
||||
bets: Bet[],
|
||||
comments: Comment[],
|
||||
user: User | undefined | null,
|
||||
|
@ -269,7 +269,7 @@ function getAnswerGroups(
|
|||
}
|
||||
|
||||
function getAnswerAndCommentInputGroups(
|
||||
contract: FullContract<DPM, FreeResponse>,
|
||||
contract: FreeResponseContract,
|
||||
bets: Bet[],
|
||||
comments: Comment[],
|
||||
user: User | undefined | null
|
||||
|
@ -493,17 +493,11 @@ export function getRecentContractActivityItems(
|
|||
const items = []
|
||||
if (contract.outcomeType === 'FREE_RESPONSE') {
|
||||
items.push(
|
||||
...getAnswerGroups(
|
||||
contract as FullContract<DPM, FreeResponse>,
|
||||
bets,
|
||||
comments,
|
||||
user,
|
||||
{
|
||||
sortByProb: false,
|
||||
abbreviated: true,
|
||||
reversed: true,
|
||||
}
|
||||
)
|
||||
...getAnswerGroups(contract, bets, comments, user, {
|
||||
sortByProb: false,
|
||||
abbreviated: true,
|
||||
reversed: true,
|
||||
})
|
||||
)
|
||||
} else {
|
||||
items.push(
|
||||
|
@ -587,7 +581,7 @@ export function getSpecificContractActivityItems(
|
|||
case 'free-response-comment-answer-groups':
|
||||
items.push(
|
||||
...getAnswerAndCommentInputGroups(
|
||||
contract as FullContract<DPM, FreeResponse>,
|
||||
contract as FreeResponseContract,
|
||||
bets,
|
||||
comments,
|
||||
user
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
import clsx from 'clsx'
|
||||
|
||||
import { User } from '../../../common/user'
|
||||
import { Row } from '../layout/row'
|
||||
import { CATEGORIES, CATEGORY_LIST } from '../../../common/categories'
|
||||
|
||||
export function CategorySelector(props: {
|
||||
user: User | null | undefined
|
||||
category: string
|
||||
setCategory: (category: string) => void
|
||||
className?: string
|
||||
}) {
|
||||
const { className, user, category, setCategory } = props
|
||||
const { className, category, setCategory } = props
|
||||
|
||||
return (
|
||||
<Row
|
||||
|
@ -24,8 +22,7 @@ export function CategorySelector(props: {
|
|||
key="all"
|
||||
category="All"
|
||||
isFollowed={category === 'all'}
|
||||
toggle={async () => {
|
||||
if (!user?.id) return
|
||||
toggle={() => {
|
||||
setCategory('all')
|
||||
}}
|
||||
/>
|
||||
|
@ -35,8 +32,7 @@ export function CategorySelector(props: {
|
|||
key={cat}
|
||||
category={CATEGORIES[cat].split(' ')[0]}
|
||||
isFollowed={cat === category}
|
||||
toggle={async () => {
|
||||
if (!user?.id) return
|
||||
toggle={() => {
|
||||
setCategory(cat)
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { FreeResponse, FullContract } from 'common/contract'
|
||||
import { Answer } from 'common/answer'
|
||||
import { ActivityItem } from 'web/components/feed/activity-items'
|
||||
import { Bet } from 'common/bet'
|
||||
|
@ -26,7 +25,7 @@ import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-ti
|
|||
import { useRouter } from 'next/router'
|
||||
|
||||
export function FeedAnswerCommentGroup(props: {
|
||||
contract: FullContract<any, FreeResponse>
|
||||
contract: any
|
||||
answer: Answer
|
||||
items: ActivityItem[]
|
||||
type: string
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
import clsx from 'clsx'
|
||||
import { CSSProperties, Ref, ReactNode } from 'react'
|
||||
|
||||
export function Col(props: {
|
||||
children?: ReactNode
|
||||
className?: string
|
||||
style?: CSSProperties
|
||||
ref?: Ref<HTMLDivElement>
|
||||
}) {
|
||||
const { children, className, style, ref } = props
|
||||
export function Col(props: JSX.IntrinsicElements['div']) {
|
||||
const { children, className, ...rest } = props
|
||||
|
||||
return (
|
||||
<div className={clsx(className, 'flex flex-col')} style={style} ref={ref}>
|
||||
<div className={clsx(className, 'flex flex-col')} {...rest}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
import clsx from 'clsx'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
export function Row(props: {
|
||||
children?: ReactNode
|
||||
className?: string
|
||||
id?: string
|
||||
}) {
|
||||
const { children, className, id } = props
|
||||
export function Row(props: JSX.IntrinsicElements['div']) {
|
||||
const { children, className, ...rest } = props
|
||||
|
||||
return (
|
||||
<div className={clsx(className, 'flex flex-row')} id={id}>
|
||||
<div className={clsx(className, 'flex flex-row')} {...rest}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
MenuAlt3Icon,
|
||||
PresentationChartLineIcon,
|
||||
SearchIcon,
|
||||
ChatAltIcon,
|
||||
XIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import { Transition, Dialog } from '@headlessui/react'
|
||||
|
@ -16,17 +15,22 @@ import { formatMoney } from 'common/util/format'
|
|||
import { Avatar } from '../avatar'
|
||||
import clsx from 'clsx'
|
||||
import { useRouter } from 'next/router'
|
||||
import NotificationsIcon from 'web/components/notifications-icon'
|
||||
import { useIsIframe } from 'web/hooks/use-is-iframe'
|
||||
|
||||
function getNavigation(username: string) {
|
||||
return [
|
||||
{ name: 'Home', href: '/home', icon: HomeIcon },
|
||||
{ name: 'Activity', href: '/activity', icon: ChatAltIcon },
|
||||
{
|
||||
name: 'Portfolio',
|
||||
href: `/${username}/bets`,
|
||||
icon: PresentationChartLineIcon,
|
||||
},
|
||||
{
|
||||
name: 'Notifications',
|
||||
href: `/notifications`,
|
||||
icon: NotificationsIcon,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -4,29 +4,6 @@ import { formatMoney } from 'common/util/format'
|
|||
import { Avatar } from '../avatar'
|
||||
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
|
||||
|
||||
export function getNavigationOptions(user?: User | null) {
|
||||
if (IS_PRIVATE_MANIFOLD) {
|
||||
return [{ name: 'Leaderboards', href: '/leaderboards' }]
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return [
|
||||
{ name: 'Leaderboards', href: '/leaderboards' },
|
||||
{ name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
|
||||
{ name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' },
|
||||
]
|
||||
}
|
||||
|
||||
return [
|
||||
{ name: 'Add funds', href: '/add-funds' },
|
||||
{ name: 'Leaderboards', href: '/leaderboards' },
|
||||
{ name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
|
||||
{ name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' },
|
||||
{ name: 'About', href: 'https://docs.manifold.markets' },
|
||||
{ name: 'Sign out', href: '#', onClick: () => firebaseLogout() },
|
||||
]
|
||||
}
|
||||
|
||||
export function ProfileSummary(props: { user: User }) {
|
||||
const { user } = props
|
||||
return (
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
PresentationChartLineIcon,
|
||||
ChatAltIcon,
|
||||
SparklesIcon,
|
||||
NewspaperIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import clsx from 'clsx'
|
||||
import { sortBy } from 'lodash'
|
||||
|
@ -16,16 +17,18 @@ import Link from 'next/link'
|
|||
import { useRouter } from 'next/router'
|
||||
import { useFollowedFolds } from 'web/hooks/use-fold'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { firebaseLogin, firebaseLogout } from 'web/lib/firebase/users'
|
||||
import { firebaseLogin, firebaseLogout, User } from 'web/lib/firebase/users'
|
||||
import { ManifoldLogo } from './manifold-logo'
|
||||
import { MenuButton } from './menu'
|
||||
import { getNavigationOptions, ProfileSummary } from './profile-menu'
|
||||
import { ProfileSummary } from './profile-menu'
|
||||
import {
|
||||
getUtcFreeMarketResetTime,
|
||||
useHasCreatedContractToday,
|
||||
} from 'web/hooks/use-has-created-contract-today'
|
||||
import { Row } from '../layout/row'
|
||||
import NotificationsIcon from 'web/components/notifications-icon'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
|
||||
|
||||
// Create an icon from the url of an image
|
||||
function IconFromUrl(url: string): React.ComponentType<{ className?: string }> {
|
||||
|
@ -37,16 +40,45 @@ function IconFromUrl(url: string): React.ComponentType<{ className?: string }> {
|
|||
function getNavigation(username: string) {
|
||||
return [
|
||||
{ name: 'Home', href: '/home', icon: HomeIcon },
|
||||
{ name: 'Activity', href: '/activity', icon: ChatAltIcon },
|
||||
{
|
||||
name: 'Portfolio',
|
||||
href: `/${username}/bets`,
|
||||
icon: PresentationChartLineIcon,
|
||||
},
|
||||
{
|
||||
name: 'Notifications',
|
||||
href: `/notifications`,
|
||||
icon: NotificationsIcon,
|
||||
},
|
||||
|
||||
{ name: 'Charity', href: '/charity', icon: HeartIcon },
|
||||
]
|
||||
}
|
||||
|
||||
function getMoreNavigation(user?: User | null) {
|
||||
if (IS_PRIVATE_MANIFOLD) {
|
||||
return [{ name: 'Leaderboards', href: '/leaderboards' }]
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return [
|
||||
{ name: 'Leaderboards', href: '/leaderboards' },
|
||||
{ name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
|
||||
{ name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' },
|
||||
]
|
||||
}
|
||||
|
||||
return [
|
||||
{ name: 'Add funds', href: '/add-funds' },
|
||||
{ name: 'Leaderboards', href: '/leaderboards' },
|
||||
{ name: 'Blog', href: 'https://news.manifold.markets' },
|
||||
{ name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
|
||||
{ name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' },
|
||||
{ name: 'About', href: 'https://docs.manifold.markets' },
|
||||
{ name: 'Sign out', href: '#', onClick: () => firebaseLogout() },
|
||||
]
|
||||
}
|
||||
|
||||
const signedOutNavigation = [
|
||||
{ name: 'Home', href: '/home', icon: HomeIcon },
|
||||
{ name: 'Explore', href: '/markets', icon: SearchIcon },
|
||||
|
@ -57,6 +89,7 @@ const signedOutNavigation = [
|
|||
const signedOutMobileNavigation = [
|
||||
{ name: 'Charity', href: '/charity', icon: HeartIcon },
|
||||
{ name: 'Leaderboards', href: '/leaderboards', icon: CakeIcon },
|
||||
{ name: 'Blog', href: 'https://news.manifold.markets', icon: NewspaperIcon },
|
||||
{
|
||||
name: 'Discord',
|
||||
href: 'https://discord.gg/eHQBNBqXuh',
|
||||
|
@ -127,7 +160,7 @@ export default function Sidebar(props: { className?: string }) {
|
|||
const currentPage = router.pathname
|
||||
const [countdown, setCountdown] = useState('...')
|
||||
useEffect(() => {
|
||||
const nextUtcResetTime = getUtcFreeMarketResetTime(false)
|
||||
const nextUtcResetTime = getUtcFreeMarketResetTime({ previousTime: false })
|
||||
const interval = setInterval(() => {
|
||||
const now = new Date().getTime()
|
||||
const timeUntil = nextUtcResetTime - now
|
||||
|
@ -193,7 +226,7 @@ export default function Sidebar(props: { className?: string }) {
|
|||
))}
|
||||
|
||||
<MenuButton
|
||||
menuItems={getNavigationOptions(user)}
|
||||
menuItems={getMoreNavigation(user)}
|
||||
buttonContent={<MoreButton />}
|
||||
/>
|
||||
</div>
|
||||
|
|
36
web/components/notifications-icon.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { BellIcon } from '@heroicons/react/outline'
|
||||
import clsx from 'clsx'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Notification } from 'common/notification'
|
||||
import { listenForNotifications } from 'web/lib/firebase/notifications'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export default function NotificationsIcon(props: { className?: string }) {
|
||||
const user = useUser()
|
||||
const [notifications, setNotifications] = useState<
|
||||
Notification[] | undefined
|
||||
>()
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
if (router.pathname.endsWith('notifications')) return setNotifications([])
|
||||
}, [router.pathname])
|
||||
|
||||
useEffect(() => {
|
||||
if (user) return listenForNotifications(user.id, setNotifications, true)
|
||||
}, [user])
|
||||
|
||||
return (
|
||||
<Row className={clsx('justify-center')}>
|
||||
<div className={'relative'}>
|
||||
{notifications && notifications.length > 0 && (
|
||||
<div className="-mt-0.75 absolute ml-3.5 min-w-[15px] rounded-full bg-indigo-500 p-[2px] text-center text-[10px] leading-3 text-white lg:-mt-1 lg:ml-2">
|
||||
{notifications.length}
|
||||
</div>
|
||||
)}
|
||||
<BellIcon className={clsx(props.className)} />
|
||||
</div>
|
||||
</Row>
|
||||
)
|
||||
}
|
|
@ -3,17 +3,7 @@ import { ReactNode } from 'react'
|
|||
import { Answer } from 'common/answer'
|
||||
import { getProbability } from 'common/calculate'
|
||||
import { getValueFromBucket } from 'common/calculate-dpm'
|
||||
import {
|
||||
Binary,
|
||||
Contract,
|
||||
CPMM,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FreeResponseContract,
|
||||
FullContract,
|
||||
NumericContract,
|
||||
resolution,
|
||||
} from 'common/contract'
|
||||
import { BinaryContract, Contract, FreeResponseContract, resolution } from 'common/contract'
|
||||
import { formatPercent } from 'common/util/format'
|
||||
import { ClientRender } from './client-render'
|
||||
|
||||
|
@ -31,13 +21,13 @@ export function OutcomeLabel(props: {
|
|||
if (contract.outcomeType === 'NUMERIC')
|
||||
return (
|
||||
<span className="text-blue-500">
|
||||
{value ?? getValueFromBucket(outcome, contract as NumericContract)}
|
||||
{value ?? getValueFromBucket(outcome, contract)}
|
||||
</span>
|
||||
)
|
||||
|
||||
return (
|
||||
<FreeResponseOutcomeLabel
|
||||
contract={contract as FullContract<DPM, FreeResponse>}
|
||||
contract={contract}
|
||||
resolution={outcome}
|
||||
truncate={truncate}
|
||||
answerClassName={'font-bold text-base-400'}
|
||||
|
@ -57,7 +47,7 @@ export function BinaryOutcomeLabel(props: {
|
|||
}
|
||||
|
||||
export function BinaryContractOutcomeLabel(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
resolution: resolution
|
||||
}) {
|
||||
const { contract, resolution } = props
|
||||
|
@ -81,8 +71,7 @@ export function FreeResponseOutcomeLabel(props: {
|
|||
if (resolution === 'CANCEL') return <CancelLabel />
|
||||
if (resolution === 'MKT') return <MultiLabel />
|
||||
|
||||
const { answers } = contract
|
||||
const chosen = answers?.find((answer) => answer.id === resolution)
|
||||
const chosen = contract.answers.find((answer) => answer.id === resolution)
|
||||
if (!chosen) return <AnswerNumberLabel number={resolution} />
|
||||
return (
|
||||
<FreeResponseAnswerToolTip text={chosen.text}>
|
||||
|
|
|
@ -10,12 +10,12 @@ import { resolveMarket } from 'web/lib/firebase/fn-call'
|
|||
import { ProbabilitySelector } from './probability-selector'
|
||||
import { DPM_CREATOR_FEE } from 'common/fees'
|
||||
import { getProbability } from 'common/calculate'
|
||||
import { Binary, CPMM, DPM, FullContract, resolution } from 'common/contract'
|
||||
import { BinaryContract, resolution } from 'common/contract'
|
||||
import { formatMoney } from 'common/util/format'
|
||||
|
||||
export function ResolutionPanel(props: {
|
||||
creator: User
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
className?: string
|
||||
}) {
|
||||
useEffect(() => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Binary, CPMM, DPM, FullContract } from 'common/contract'
|
||||
import { BinaryContract } from 'common/contract'
|
||||
import { User } from 'common/user'
|
||||
import { useUserContractBets } from 'web/hooks/use-user-bets'
|
||||
import { useState } from 'react'
|
||||
|
@ -7,7 +7,7 @@ import clsx from 'clsx'
|
|||
import { SellSharesModal } from './sell-modal'
|
||||
|
||||
export function SellButton(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
user: User | null | undefined
|
||||
sharesOutcome: 'YES' | 'NO' | undefined
|
||||
shares: number
|
||||
|
@ -40,7 +40,7 @@ export function SellButton(props: {
|
|||
{showSellModal && (
|
||||
<SellSharesModal
|
||||
className={panelClassName}
|
||||
contract={contract as FullContract<CPMM, Binary>}
|
||||
contract={contract}
|
||||
user={user}
|
||||
userBets={userBets ?? []}
|
||||
shares={shares}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Binary, CPMM, FullContract } from 'common/contract'
|
||||
import { CPMMBinaryContract } from 'common/contract'
|
||||
import { Bet } from 'common/bet'
|
||||
import { User } from 'common/user'
|
||||
import { Modal } from './layout/modal'
|
||||
|
@ -11,7 +11,7 @@ import clsx from 'clsx'
|
|||
|
||||
export function SellSharesModal(props: {
|
||||
className?: string
|
||||
contract: FullContract<CPMM, Binary>
|
||||
contract: CPMMBinaryContract
|
||||
userBets: Bet[]
|
||||
shares: number
|
||||
sharesOutcome: 'YES' | 'NO'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Binary, CPMM, DPM, FullContract } from 'common/contract'
|
||||
import { BinaryContract } from 'common/contract'
|
||||
import { User } from 'common/user'
|
||||
import { useState } from 'react'
|
||||
import { Col } from './layout/col'
|
||||
|
@ -10,7 +10,7 @@ import { useSaveShares } from './use-save-shares'
|
|||
import { SellSharesModal } from './sell-modal'
|
||||
|
||||
export function SellRow(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
contract: BinaryContract
|
||||
user: User | null | undefined
|
||||
className?: string
|
||||
}) {
|
||||
|
@ -61,7 +61,7 @@ export function SellRow(props: {
|
|||
</Col>
|
||||
{showSellModal && (
|
||||
<SellSharesModal
|
||||
contract={contract as FullContract<CPMM, Binary>}
|
||||
contract={contract}
|
||||
user={user}
|
||||
userBets={userBets ?? []}
|
||||
shares={yesShares || noShares}
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
import {
|
||||
Binary,
|
||||
CPMM,
|
||||
DPM,
|
||||
FreeResponseContract,
|
||||
FullContract,
|
||||
} from 'common/contract'
|
||||
import { Contract } from 'common/contract'
|
||||
import { Bet } from 'common/bet'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { partition, sumBy } from 'lodash'
|
||||
import { safeLocalStorage } from 'web/lib/util/local'
|
||||
|
||||
export const useSaveShares = (
|
||||
contract: FullContract<CPMM | DPM, Binary | FreeResponseContract>,
|
||||
contract: Contract,
|
||||
userBets: Bet[] | undefined,
|
||||
freeResponseAnswerOutcome?: string
|
||||
) => {
|
||||
|
|
|
@ -44,6 +44,7 @@ export function UserLink(props: {
|
|||
}
|
||||
|
||||
export const TAB_IDS = ['markets', 'comments', 'bets']
|
||||
const JUNE_1_2022 = new Date('2022-06-01T00:00:00.000Z').valueOf()
|
||||
|
||||
export function UserPage(props: {
|
||||
user: User
|
||||
|
@ -229,14 +230,27 @@ export function UserPage(props: {
|
|||
title: 'Bets',
|
||||
content: (
|
||||
<div>
|
||||
<AlertBox
|
||||
title="Bets are becoming publicly visible on 2022-06-01"
|
||||
text="Bettor identities have always been traceable through the Manifold API.
|
||||
However, our interface implied that they were private.
|
||||
As we develop new features such as leaderboards and bet history, it won't be technically feasible to keep this info private.
|
||||
For more context, or if you'd like to wipe your bet history, see: https://manifold.markets/Austin/will-all-bets-on-manifold-be-public"
|
||||
{isCurrentUser && (
|
||||
<AlertBox
|
||||
title="Bets after 2022-06-01 are publicly visible by default."
|
||||
text="Note that all historical bets are also publicly accessible through the API.
|
||||
See: https://manifold.markets/Austin/will-all-bets-on-manifold-be-public"
|
||||
/>
|
||||
)}
|
||||
<BetsList
|
||||
user={user}
|
||||
hideBetsBefore={isCurrentUser ? 0 : JUNE_1_2022}
|
||||
/>
|
||||
{isCurrentUser && <BetsList user={user} />}
|
||||
{!isCurrentUser && (
|
||||
<>
|
||||
<Spacer h={4} />
|
||||
<AlertBox
|
||||
title="Bets before 2022-06-01 are hidden by default."
|
||||
text="Note that all historical bets are also publicly accessible through the API.
|
||||
See: https://manifold.markets/Austin/will-all-bets-on-manifold-be-public"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
tabIcon: (
|
||||
|
|
|
@ -5,9 +5,8 @@ import dayjs from 'dayjs'
|
|||
import utc from 'dayjs/plugin/utc'
|
||||
dayjs.extend(utc)
|
||||
|
||||
let sessionCreatedContractToday = true
|
||||
|
||||
export function getUtcFreeMarketResetTime(previous: boolean) {
|
||||
export function getUtcFreeMarketResetTime(options: { previousTime: boolean }) {
|
||||
const { previousTime } = options
|
||||
const localTimeNow = new Date()
|
||||
const utc4pmToday = dayjs()
|
||||
.utc()
|
||||
|
@ -18,7 +17,7 @@ export function getUtcFreeMarketResetTime(previous: boolean) {
|
|||
|
||||
// if it's after 4pm UTC today
|
||||
if (localTimeNow.getTime() > utc4pmToday.valueOf()) {
|
||||
return previous
|
||||
return previousTime
|
||||
? // Return it as it is
|
||||
utc4pmToday.valueOf()
|
||||
: // Or add 24 hours to get the next 4pm UTC time:
|
||||
|
@ -26,7 +25,7 @@ export function getUtcFreeMarketResetTime(previous: boolean) {
|
|||
}
|
||||
|
||||
// 4pm UTC today is coming up
|
||||
return previous
|
||||
return previousTime
|
||||
? // Subtract 24 hours to get the previous 4pm UTC time:
|
||||
utc4pmToday.valueOf() - 24 * 60 * 60 * 1000
|
||||
: // Return it as it is
|
||||
|
@ -39,9 +38,12 @@ export const useHasCreatedContractToday = (user: User | null | undefined) => {
|
|||
>('loading')
|
||||
|
||||
useEffect(() => {
|
||||
const nextUtcResetTime = getUtcFreeMarketResetTime({ previousTime: false })
|
||||
setHasCreatedContractToday('loading')
|
||||
const previousResetTime = getUtcFreeMarketResetTime(true)
|
||||
async function listUserContractsForToday() {
|
||||
const previousResetTime = getUtcFreeMarketResetTime({
|
||||
previousTime: true,
|
||||
})
|
||||
if (!user) return
|
||||
|
||||
const contracts = await listContracts(user.id)
|
||||
|
@ -49,11 +51,15 @@ export const useHasCreatedContractToday = (user: User | null | undefined) => {
|
|||
(contract) => contract.createdTime > previousResetTime
|
||||
)
|
||||
|
||||
sessionCreatedContractToday = todayContracts.length > 0
|
||||
setHasCreatedContractToday(sessionCreatedContractToday)
|
||||
setHasCreatedContractToday(todayContracts.length > 0)
|
||||
}
|
||||
|
||||
const timeoutUntilNextFreeMarket = setTimeout(() => {
|
||||
setHasCreatedContractToday(false)
|
||||
}, nextUtcResetTime - Date.now())
|
||||
|
||||
listUserContractsForToday()
|
||||
return () => clearTimeout(timeoutUntilNextFreeMarket)
|
||||
}, [user])
|
||||
|
||||
return hasCreatedContractToday
|
||||
|
|
|
@ -17,7 +17,7 @@ import { range, sortBy, sum } from 'lodash'
|
|||
|
||||
import { app } from './init'
|
||||
import { getValues, listenForValue, listenForValues } from './utils'
|
||||
import { Binary, Contract, FullContract } from 'common/contract'
|
||||
import { BinaryContract, Contract } from 'common/contract'
|
||||
import { getDpmProbability } from 'common/calculate-dpm'
|
||||
import { createRNG, shuffle } from 'common/util/random'
|
||||
import { getCpmmProbability } from 'common/calculate-cpmm'
|
||||
|
@ -64,18 +64,18 @@ export function contractPool(contract: Contract) {
|
|||
: 'Empty pool'
|
||||
}
|
||||
|
||||
export function getBinaryProb(contract: FullContract<any, Binary>) {
|
||||
const { totalShares, pool, p, resolutionProbability, mechanism } = contract
|
||||
export function getBinaryProb(contract: BinaryContract) {
|
||||
const { pool, resolutionProbability, mechanism } = contract
|
||||
|
||||
return (
|
||||
resolutionProbability ??
|
||||
(mechanism === 'cpmm-1'
|
||||
? getCpmmProbability(pool, p)
|
||||
: getDpmProbability(totalShares))
|
||||
? getCpmmProbability(pool, contract.p)
|
||||
: getDpmProbability(contract.totalShares))
|
||||
)
|
||||
}
|
||||
|
||||
export function getBinaryProbPercent(contract: FullContract<any, Binary>) {
|
||||
export function getBinaryProbPercent(contract: BinaryContract) {
|
||||
return formatPercent(getBinaryProb(contract))
|
||||
}
|
||||
|
||||
|
|
24
web/lib/firebase/notifications.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { collection, query, where } from 'firebase/firestore'
|
||||
import { Notification } from 'common/notification'
|
||||
import { db } from 'web/lib/firebase/init'
|
||||
import { getValues, listenForValues } from 'web/lib/firebase/utils'
|
||||
|
||||
function getNotificationsQuery(userId: string, unseenOnly?: boolean) {
|
||||
const notifsCollection = collection(db, `/users/${userId}/notifications`)
|
||||
if (unseenOnly) return query(notifsCollection, where('isSeen', '==', false))
|
||||
return query(notifsCollection)
|
||||
}
|
||||
|
||||
export function listenForNotifications(
|
||||
userId: string,
|
||||
setNotifications: (notifs: Notification[]) => void,
|
||||
unseenOnly?: boolean
|
||||
) {
|
||||
return listenForValues<Notification>(
|
||||
getNotificationsQuery(userId, unseenOnly),
|
||||
(notifs) => {
|
||||
notifs.sort((n1, n2) => n2.createdTime - n1.createdTime)
|
||||
setNotifications(notifs)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -30,13 +30,6 @@ import { formatMoney } from 'common/util/format'
|
|||
import { useUserById } from 'web/hooks/use-users'
|
||||
import { ContractTabs } from 'web/components/contract/contract-tabs'
|
||||
import { FirstArgument } from 'common/util/types'
|
||||
import {
|
||||
BinaryContract,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
NumericContract,
|
||||
} from 'common/contract'
|
||||
import { contractTextDetails } from 'web/components/contract/contract-details'
|
||||
import { useWindowSize } from 'web/hooks/use-window-size'
|
||||
import Confetti from 'react-confetti'
|
||||
|
@ -143,24 +136,15 @@ export function ContractPageContent(props: FirstArgument<typeof ContractPage>) {
|
|||
<Col className="gap-4">
|
||||
{allowTrade &&
|
||||
(isNumeric ? (
|
||||
<NumericBetPanel
|
||||
className="hidden xl:flex"
|
||||
contract={contract as NumericContract}
|
||||
/>
|
||||
<NumericBetPanel className="hidden xl:flex" contract={contract} />
|
||||
) : (
|
||||
<BetPanel className="hidden xl:flex" contract={contract} />
|
||||
))}
|
||||
{allowResolve &&
|
||||
(isNumeric ? (
|
||||
<NumericResolutionPanel
|
||||
creator={user}
|
||||
contract={contract as NumericContract}
|
||||
/>
|
||||
<NumericResolutionPanel creator={user} contract={contract} />
|
||||
) : (
|
||||
<ResolutionPanel
|
||||
creator={user}
|
||||
contract={contract as BinaryContract}
|
||||
/>
|
||||
<ResolutionPanel creator={user} contract={contract} />
|
||||
))}
|
||||
</Col>
|
||||
) : null
|
||||
|
@ -205,18 +189,13 @@ export function ContractPageContent(props: FirstArgument<typeof ContractPage>) {
|
|||
{outcomeType === 'FREE_RESPONSE' && (
|
||||
<>
|
||||
<Spacer h={4} />
|
||||
<AnswersPanel
|
||||
contract={contract as FullContract<DPM, FreeResponse>}
|
||||
/>
|
||||
<AnswersPanel contract={contract} />
|
||||
<Spacer h={4} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{isNumeric && (
|
||||
<NumericBetPanel
|
||||
className="xl:hidden"
|
||||
contract={contract as NumericContract}
|
||||
/>
|
||||
<NumericBetPanel className="xl:hidden" contract={contract} />
|
||||
)}
|
||||
|
||||
{isResolved && (
|
||||
|
|
|
@ -39,11 +39,7 @@ export default function Activity() {
|
|||
<>
|
||||
<Page assertUser="signed-in" suspend={!!contract}>
|
||||
<Col className="mx-auto w-full max-w-[700px]">
|
||||
<CategorySelector
|
||||
user={user}
|
||||
category={category}
|
||||
setCategory={setCategory}
|
||||
/>
|
||||
<CategorySelector category={category} setCategory={setCategory} />
|
||||
<Spacer h={1} />
|
||||
{feed ? (
|
||||
<ActivityFeed
|
||||
|
|
|
@ -15,6 +15,8 @@ import { getDailyBets } from 'web/lib/firebase/bets'
|
|||
import { getDailyComments } from 'web/lib/firebase/comments'
|
||||
import { getDailyContracts } from 'web/lib/firebase/contracts'
|
||||
import { getDailyNewUsers } from 'web/lib/firebase/users'
|
||||
import { SiteLink } from 'web/components/site-link'
|
||||
import { Linkify } from 'web/components/linkify'
|
||||
|
||||
export const getStaticProps = fromPropz(getStaticPropz)
|
||||
export async function getStaticPropz() {
|
||||
|
@ -192,9 +194,22 @@ export default function Analytics(props: {
|
|||
}
|
||||
return (
|
||||
<Page>
|
||||
<CustomAnalytics {...props} />
|
||||
<Spacer h={8} />
|
||||
{!IS_PRIVATE_MANIFOLD && <FirebaseAnalytics />}
|
||||
<Tabs
|
||||
tabs={[
|
||||
{
|
||||
title: 'Activity',
|
||||
content: <CustomAnalytics {...props} />,
|
||||
},
|
||||
{
|
||||
title: 'Market Stats',
|
||||
content: <WasabiCharts />,
|
||||
},
|
||||
{
|
||||
title: 'Google Analytics',
|
||||
content: <FirebaseAnalytics />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
@ -431,7 +446,6 @@ export function FirebaseAnalytics() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Title text="Google Analytics" />
|
||||
<p className="text-gray-500">
|
||||
Less accurate; includes all viewers (not just signed-in users).
|
||||
</p>
|
||||
|
@ -447,3 +461,28 @@ export function FirebaseAnalytics() {
|
|||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function WasabiCharts() {
|
||||
return (
|
||||
<>
|
||||
<p className="text-gray-500">
|
||||
Courtesy of <Linkify text="@wasabipesto" />; originally found{' '}
|
||||
<SiteLink
|
||||
className="font-bold"
|
||||
href="https://wasabipesto.com/jupyter/manifold/"
|
||||
>
|
||||
here.
|
||||
</SiteLink>
|
||||
</p>
|
||||
<Spacer h={4} />
|
||||
<iframe
|
||||
className="w-full"
|
||||
height={12000}
|
||||
src="https://wasabipesto.com/jupyter/manifold/"
|
||||
frameBorder="0"
|
||||
style={{ border: 0 }}
|
||||
allowFullScreen
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -57,9 +57,8 @@ export default function Charity(props: {
|
|||
<Col className="max-w-xl gap-2">
|
||||
<Title className="!mt-0" text="Manifold for Good" />
|
||||
<div className="mb-6 text-gray-500">
|
||||
Donate your winnings to charity! Through the month of May, every{' '}
|
||||
{formatMoney(100)} you contribute turns into $1 USD sent to your
|
||||
chosen charity.
|
||||
Donate your winnings to charity! Every {formatMoney(100)} you give
|
||||
turns into $1 USD we send to your chosen charity.
|
||||
<Spacer h={5} />
|
||||
Together we've donated over ${Math.floor(totalRaised / 100)} USD so
|
||||
far!
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
import { Bet } from 'common/bet'
|
||||
import {
|
||||
BinaryContract,
|
||||
Contract,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
NumericContract,
|
||||
} from 'common/contract'
|
||||
import { Contract } from 'common/contract'
|
||||
import { DOMAIN } from 'common/envs/constants'
|
||||
import { AnswersGraph } from 'web/components/answers/answers-graph'
|
||||
import BetRow from 'web/components/bet-row'
|
||||
|
@ -117,10 +110,7 @@ function ContractEmbed(props: { contract: Contract; bets: Bet[] }) {
|
|||
|
||||
{isBinary && (
|
||||
<Row className="items-center gap-4">
|
||||
<BetRow
|
||||
contract={contract as BinaryContract}
|
||||
betPanelClassName="scale-75"
|
||||
/>
|
||||
<BetRow contract={contract as any} betPanelClassName="scale-75" />
|
||||
<BinaryResolutionOrChance contract={contract} />
|
||||
</Row>
|
||||
)}
|
||||
|
@ -133,9 +123,7 @@ function ContractEmbed(props: { contract: Contract; bets: Bet[] }) {
|
|||
)}
|
||||
|
||||
{outcomeType === 'NUMERIC' && (
|
||||
<NumericResolutionOrExpectation
|
||||
contract={contract as NumericContract}
|
||||
/>
|
||||
<NumericResolutionOrExpectation contract={contract} />
|
||||
)}
|
||||
</Row>
|
||||
|
||||
|
@ -152,18 +140,11 @@ function ContractEmbed(props: { contract: Contract; bets: Bet[] }) {
|
|||
)}
|
||||
|
||||
{outcomeType === 'FREE_RESPONSE' && (
|
||||
<AnswersGraph
|
||||
contract={contract as FullContract<DPM, FreeResponse>}
|
||||
bets={bets}
|
||||
height={graphHeight}
|
||||
/>
|
||||
<AnswersGraph contract={contract} bets={bets} height={graphHeight} />
|
||||
)}
|
||||
|
||||
{outcomeType === 'NUMERIC' && (
|
||||
<NumericGraph
|
||||
contract={contract as NumericContract}
|
||||
height={graphHeight}
|
||||
/>
|
||||
<NumericGraph contract={contract} height={graphHeight} />
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useState } from 'react'
|
|||
import Textarea from 'react-expanding-textarea'
|
||||
|
||||
import { getProbability } from 'common/calculate'
|
||||
import { Binary, CPMM, DPM, FullContract } from 'common/contract'
|
||||
import { BinaryContract } from 'common/contract'
|
||||
import { parseWordsAsTags } from 'common/util/parse'
|
||||
import { BuyAmountInput } from 'web/components/amount-input'
|
||||
import { InfoTooltip } from 'web/components/info-tooltip'
|
||||
|
@ -26,7 +26,7 @@ type Prediction = {
|
|||
createdUrl?: string
|
||||
}
|
||||
|
||||
function toPrediction(contract: FullContract<DPM | CPMM, Binary>): Prediction {
|
||||
function toPrediction(contract: BinaryContract): Prediction {
|
||||
const startProb = getProbability(contract)
|
||||
return {
|
||||
question: contract.question,
|
||||
|
@ -102,9 +102,7 @@ export default function MakePredictions() {
|
|||
const [description, setDescription] = useState('')
|
||||
const [tags, setTags] = useState('')
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [createdContracts, setCreatedContracts] = useState<
|
||||
FullContract<DPM | CPMM, Binary>[]
|
||||
>([])
|
||||
const [createdContracts, setCreatedContracts] = useState<BinaryContract[]>([])
|
||||
|
||||
const [ante, setAnte] = useState<number | undefined>(100)
|
||||
const [anteError, setAnteError] = useState<string | undefined>()
|
||||
|
@ -155,7 +153,7 @@ ${TEST_VALUE}
|
|||
ante,
|
||||
closeTime,
|
||||
tags: parseWordsAsTags(tags),
|
||||
})) as FullContract<DPM | CPMM, Binary>
|
||||
})) as BinaryContract
|
||||
|
||||
setCreatedContracts((prev) => [...prev, contract])
|
||||
}
|
||||
|
|
187
web/pages/notifications.tsx
Normal file
|
@ -0,0 +1,187 @@
|
|||
import { Tabs } from 'web/components/layout/tabs'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Notification } from 'common/notification'
|
||||
import { listenForNotifications } from 'web/lib/firebase/notifications'
|
||||
import { Avatar } from 'web/components/avatar'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
import { Page } from 'web/components/page'
|
||||
import { Title } from 'web/components/title'
|
||||
import { doc, updateDoc } from 'firebase/firestore'
|
||||
import { db } from 'web/lib/firebase/init'
|
||||
import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time'
|
||||
import { Answer } from 'common/answer'
|
||||
import { Comment } from 'web/lib/firebase/comments'
|
||||
import { getValue } from 'web/lib/firebase/utils'
|
||||
import Custom404 from 'web/pages/404'
|
||||
import { UserLink } from 'web/components/user-page'
|
||||
import { Linkify } from 'web/components/linkify'
|
||||
import { User } from 'common/user'
|
||||
import { useContract } from 'web/hooks/use-contract'
|
||||
|
||||
export default function Notifications() {
|
||||
const user = useUser()
|
||||
const [notifications, setNotifications] = useState<
|
||||
Notification[] | undefined
|
||||
>()
|
||||
|
||||
useEffect(() => {
|
||||
if (user) return listenForNotifications(user.id, setNotifications)
|
||||
}, [user])
|
||||
|
||||
if (!user) {
|
||||
// TODO: return sign in page
|
||||
return <Custom404 />
|
||||
}
|
||||
|
||||
// TODO: use infinite scroll
|
||||
return (
|
||||
<Page>
|
||||
<div className={'p-4'}>
|
||||
<Title text={'Notifications'} />
|
||||
<Tabs
|
||||
className={'pb-2 pt-1 '}
|
||||
defaultIndex={0}
|
||||
tabs={[
|
||||
{
|
||||
title: 'All Notifications',
|
||||
content: (
|
||||
<div className={''}>
|
||||
{notifications &&
|
||||
notifications.map((notification) => (
|
||||
<Notification
|
||||
currentUser={user}
|
||||
notification={notification}
|
||||
key={notification.id}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
function Notification(props: {
|
||||
currentUser: User
|
||||
notification: Notification
|
||||
}) {
|
||||
const { notification, currentUser } = props
|
||||
const {
|
||||
sourceType,
|
||||
sourceContractId,
|
||||
sourceId,
|
||||
userId,
|
||||
id,
|
||||
sourceUserName,
|
||||
sourceUserAvatarUrl,
|
||||
reasonText,
|
||||
sourceUserUsername,
|
||||
createdTime,
|
||||
} = notification
|
||||
const [subText, setSubText] = useState<string>('')
|
||||
const contract = useContract(sourceContractId ?? '')
|
||||
|
||||
useEffect(() => {
|
||||
if (!contract) return
|
||||
if (sourceType === 'contract') {
|
||||
setSubText(contract.question)
|
||||
}
|
||||
}, [contract, sourceType])
|
||||
|
||||
useEffect(() => {
|
||||
if (!sourceContractId || !sourceId) return
|
||||
|
||||
if (sourceType === 'answer') {
|
||||
getValue<Answer>(
|
||||
doc(db, `contracts/${sourceContractId}/answers/`, sourceId)
|
||||
).then((answer) => {
|
||||
setSubText(answer?.text || '')
|
||||
})
|
||||
} else if (sourceType === 'comment') {
|
||||
getValue<Comment>(
|
||||
doc(db, `contracts/${sourceContractId}/comments/`, sourceId)
|
||||
).then((comment) => {
|
||||
setSubText(comment?.text || '')
|
||||
})
|
||||
}
|
||||
}, [sourceContractId, sourceId, sourceType])
|
||||
|
||||
useEffect(() => {
|
||||
if (!contract || !notification || notification.isSeen) return
|
||||
updateDoc(doc(db, `users/${currentUser.id}/notifications/`, id), {
|
||||
...notification,
|
||||
isSeen: true,
|
||||
viewTime: new Date(),
|
||||
})
|
||||
}, [notification, contract, currentUser, id, userId])
|
||||
|
||||
function getSourceUrl(sourceId?: string) {
|
||||
if (!contract) return ''
|
||||
return `/${contract.creatorUsername}/${
|
||||
contract.slug
|
||||
}#${getSourceIdForLinkComponent(sourceId ?? '')}`
|
||||
}
|
||||
|
||||
function getSourceIdForLinkComponent(sourceId: string) {
|
||||
switch (sourceType) {
|
||||
case 'answer':
|
||||
return `answer-${sourceId}`
|
||||
case 'comment':
|
||||
return sourceId
|
||||
case 'contract':
|
||||
return ''
|
||||
default:
|
||||
return sourceId
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={' bg-white px-4 pt-6'}>
|
||||
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
||||
<Avatar
|
||||
avatarUrl={sourceUserAvatarUrl}
|
||||
size={'sm'}
|
||||
className={'mr-2'}
|
||||
username={sourceUserName}
|
||||
/>
|
||||
<div className={'flex-1'}>
|
||||
<UserLink
|
||||
name={sourceUserName || ''}
|
||||
username={sourceUserUsername || ''}
|
||||
className={'mr-0 flex-shrink-0'}
|
||||
/>
|
||||
<a href={getSourceUrl(sourceId)} className={'flex-1 pl-1'}>
|
||||
{reasonText}
|
||||
{contract && sourceId && (
|
||||
<div className={'inline'}>
|
||||
<CopyLinkDateTimeComponent
|
||||
contract={contract}
|
||||
createdTime={createdTime}
|
||||
elementId={getSourceIdForLinkComponent(sourceId)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</a>
|
||||
</div>
|
||||
</Row>
|
||||
<a href={getSourceUrl(sourceId)}>
|
||||
<div className={'ml-4 mt-1'}>
|
||||
{' '}
|
||||
{contract && subText === contract.question ? (
|
||||
<div className={'text-md text-indigo-700 hover:underline'}>
|
||||
{subText}
|
||||
</div>
|
||||
) : (
|
||||
<Linkify text={subText} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={'mt-6 border-b border-gray-300'} />
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|