aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrandon Millman <brandon@0xproject.com>2019-01-08 04:41:10 +0800
committerGitHub <noreply@github.com>2019-01-08 04:41:10 +0800
commit17f087870781e3b89fa9a63c81c9f41bb6bc9bf8 (patch)
tree1fbeb09a27cbde3dbaeb67c3240e3433ad66580d
parent3cdb85606a6904370e26ae883559631350f8bd82 (diff)
parente4bb6a4652a202d38308ec705144dc07833e9d36 (diff)
downloaddexon-sol-tools-17f087870781e3b89fa9a63c81c9f41bb6bc9bf8.tar
dexon-sol-tools-17f087870781e3b89fa9a63c81c9f41bb6bc9bf8.tar.gz
dexon-sol-tools-17f087870781e3b89fa9a63c81c9f41bb6bc9bf8.tar.bz2
dexon-sol-tools-17f087870781e3b89fa9a63c81c9f41bb6bc9bf8.tar.lz
dexon-sol-tools-17f087870781e3b89fa9a63c81c9f41bb6bc9bf8.tar.xz
dexon-sol-tools-17f087870781e3b89fa9a63c81c9f41bb6bc9bf8.tar.zst
dexon-sol-tools-17f087870781e3b89fa9a63c81c9f41bb6bc9bf8.zip
Merge pull request #1487 from 0xProject/feature/website/market-maker-page
[website] Add market making grant program page
-rw-r--r--packages/website/ts/components/button.tsx9
-rw-r--r--packages/website/ts/components/definition.tsx2
-rw-r--r--packages/website/ts/components/dropdowns/dropdown_developers.tsx4
-rw-r--r--packages/website/ts/components/modals/modal_contact.tsx239
-rw-r--r--packages/website/ts/index.tsx6
-rw-r--r--packages/website/ts/pages/market_maker.tsx134
6 files changed, 282 insertions, 112 deletions
diff --git a/packages/website/ts/components/button.tsx b/packages/website/ts/components/button.tsx
index c9785e48c..569038ae0 100644
--- a/packages/website/ts/components/button.tsx
+++ b/packages/website/ts/components/button.tsx
@@ -8,6 +8,7 @@ import { colors } from 'ts/style/colors';
interface ButtonInterface {
bgColor?: string;
+ borderColor?: string;
color?: string;
children?: React.ReactNode | string;
isTransparent?: boolean;
@@ -26,7 +27,7 @@ interface ButtonInterface {
shouldUseAnchorTag?: boolean;
}
-export const Button = (props: ButtonInterface) => {
+export const Button: React.StatelessComponent<ButtonInterface> = (props: ButtonInterface) => {
const { children, href, isWithArrow, to, shouldUseAnchorTag, target } = props;
let linkElem;
@@ -53,6 +54,10 @@ export const Button = (props: ButtonInterface) => {
);
};
+Button.defaultProps = {
+ borderColor: 'rgba(255, 255, 255, .4)',
+};
+
const ButtonBase =
styled.button <
ButtonInterface >
@@ -62,7 +67,7 @@ const ButtonBase =
display: inline-block;
background-color: ${props => props.bgColor || colors.brandLight};
background-color: ${props => (props.isTransparent || props.isWithArrow) && 'transparent'};
- border-color: ${props => props.isTransparent && !props.isWithArrow && 'rgba(255, 255, 255, .4)'};
+ border-color: ${props => props.isTransparent && !props.isWithArrow && props.borderColor};
color: ${props => (props.isAccentColor ? props.theme.linkColor : props.color || props.theme.textColor)};
padding: ${props => !props.isNoPadding && !props.isWithArrow && '18px 30px'};
white-space: ${props => props.isWithArrow && 'nowrap'};
diff --git a/packages/website/ts/components/definition.tsx b/packages/website/ts/components/definition.tsx
index c7fac5177..92fe54f38 100644
--- a/packages/website/ts/components/definition.tsx
+++ b/packages/website/ts/components/definition.tsx
@@ -5,7 +5,7 @@ import { Button } from 'ts/components/button';
import { Icon } from 'ts/components/icon';
import { Heading, Paragraph } from 'ts/components/text';
-interface Action {
+export interface Action {
label: string;
url?: string;
onClick?: () => void;
diff --git a/packages/website/ts/components/dropdowns/dropdown_developers.tsx b/packages/website/ts/components/dropdowns/dropdown_developers.tsx
index dc6b70d21..590d2ead9 100644
--- a/packages/website/ts/components/dropdowns/dropdown_developers.tsx
+++ b/packages/website/ts/components/dropdowns/dropdown_developers.tsx
@@ -37,6 +37,10 @@ const introData: LinkConfig[] = [
label: 'Use networked liquidity',
url: `${WebsitePaths.Wiki}#Find,-Submit,-Fill-Order-From-Relayer`,
},
+ {
+ label: 'Market making',
+ url: `${WebsitePaths.MarketMaker}`,
+ },
];
const docsData: LinkConfig[] = [
diff --git a/packages/website/ts/components/modals/modal_contact.tsx b/packages/website/ts/components/modals/modal_contact.tsx
index d9c276584..a3a1f13f5 100644
--- a/packages/website/ts/components/modals/modal_contact.tsx
+++ b/packages/website/ts/components/modals/modal_contact.tsx
@@ -13,10 +13,16 @@ import { Input, InputWidth } from 'ts/components/modals/input';
import { Heading, Paragraph } from 'ts/components/text';
import { GlobalStyle } from 'ts/constants/globalStyle';
+export enum ModalContactType {
+ General = 'GENERAL',
+ MarketMaker = 'MARKET_MAKER',
+}
+
interface Props {
theme?: GlobalStyle;
isOpen?: boolean;
onDismiss?: () => void;
+ modalContactType: ModalContactType;
}
interface FormProps {
@@ -39,23 +45,30 @@ interface ErrorProps {
}
export class ModalContact extends React.Component<Props> {
+ public static defaultProps = {
+ modalContactType: ModalContactType.General,
+ };
public state = {
isSubmitting: false,
isSuccessful: false,
errors: {},
};
+ // shared fields
public nameRef: React.RefObject<HTMLInputElement> = React.createRef();
public emailRef: React.RefObject<HTMLInputElement> = React.createRef();
public companyProjectRef: React.RefObject<HTMLInputElement> = React.createRef();
- public linkRef: React.RefObject<HTMLInputElement> = React.createRef();
public commentsRef: React.RefObject<HTMLInputElement> = React.createRef();
+ // general lead fields
+ public linkRef: React.RefObject<HTMLInputElement> = React.createRef();
+ // market maker lead fields
+ public countryRef: React.RefObject<HTMLInputElement> = React.createRef();
+ public fundSizeRef: React.RefObject<HTMLInputElement> = React.createRef();
public constructor(props: Props) {
super(props);
}
public render(): React.ReactNode {
const { isOpen, onDismiss } = this.props;
const { isSuccessful, errors } = this.state;
-
return (
<>
<DialogOverlay
@@ -68,58 +81,7 @@ export class ModalContact extends React.Component<Props> {
<Heading color={colors.textDarkPrimary} size={34} asElement="h2">
Contact the 0x Core Team
</Heading>
- <Paragraph isMuted={true} color={colors.textDarkPrimary}>
- If you're considering building on 0x, we're happy to answer your questions. Fill out the
- form so we can connect you with the right person to help you get started.
- </Paragraph>
- <InputRow>
- <Input
- name="name"
- label="Your name"
- type="text"
- width={InputWidth.Half}
- ref={this.nameRef}
- required={true}
- errors={errors}
- />
- <Input
- name="email"
- label="Your email"
- type="email"
- ref={this.emailRef}
- required={true}
- errors={errors}
- width={InputWidth.Half}
- />
- </InputRow>
- <InputRow>
- <Input
- name="companyOrProject"
- label="Name of your project / company"
- type="text"
- ref={this.companyProjectRef}
- required={true}
- errors={errors}
- />
- </InputRow>
- <InputRow>
- <Input
- name="link"
- label="Do you have any documentation or a website?"
- type="text"
- ref={this.linkRef}
- errors={errors}
- />
- </InputRow>
- <InputRow>
- <Input
- name="comments"
- label="Anything else?"
- type="textarea"
- ref={this.commentsRef}
- errors={errors}
- />
- </InputRow>
+ {this._renderFormContent(errors)}
<ButtonRow>
<Button
color="#5C5C5C"
@@ -149,28 +111,182 @@ export class ModalContact extends React.Component<Props> {
</>
);
}
+ public _renderFormContent(errors: ErrorProps): React.ReactNode {
+ switch (this.props.modalContactType) {
+ case ModalContactType.MarketMaker:
+ return this._renderMarketMakerFormContent(errors);
+ case ModalContactType.General:
+ default:
+ return this._renderGeneralFormContent(errors);
+ }
+ }
+ private _renderMarketMakerFormContent(errors: ErrorProps): React.ReactNode {
+ return (
+ <>
+ <Paragraph isMuted={true} color={colors.textDarkPrimary}>
+ If you’re considering market making on 0x, we’re happy to answer your questions. Fill out the form
+ so we can connect you with the right person to help you get started.
+ </Paragraph>
+ <InputRow>
+ <Input
+ name="name"
+ label="Your name"
+ type="text"
+ width={InputWidth.Half}
+ ref={this.nameRef}
+ required={true}
+ errors={errors}
+ />
+ <Input
+ name="email"
+ label="Your email"
+ type="email"
+ ref={this.emailRef}
+ required={true}
+ errors={errors}
+ width={InputWidth.Half}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="country"
+ label="Country of Location"
+ type="text"
+ ref={this.countryRef}
+ required={true}
+ errors={errors}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="fundSize"
+ label="Fund Size"
+ type="text"
+ ref={this.fundSizeRef}
+ required={true}
+ errors={errors}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="companyOrProject"
+ label="Name of your project / company"
+ type="text"
+ ref={this.companyProjectRef}
+ required={false}
+ errors={errors}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="comments"
+ label="What is prompting you to reach out?"
+ type="textarea"
+ ref={this.commentsRef}
+ required={false}
+ errors={errors}
+ />
+ </InputRow>
+ </>
+ );
+ }
+ private _renderGeneralFormContent(errors: ErrorProps): React.ReactNode {
+ return (
+ <>
+ <Paragraph isMuted={true} color={colors.textDarkPrimary}>
+ If you're considering building on 0x, we're happy to answer your questions. Fill out the form so we
+ can connect you with the right person to help you get started.
+ </Paragraph>
+ <InputRow>
+ <Input
+ name="name"
+ label="Your name"
+ type="text"
+ width={InputWidth.Half}
+ ref={this.nameRef}
+ required={true}
+ errors={errors}
+ />
+ <Input
+ name="email"
+ label="Your email"
+ type="email"
+ ref={this.emailRef}
+ required={true}
+ errors={errors}
+ width={InputWidth.Half}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="companyOrProject"
+ label="Name of your project / company"
+ type="text"
+ ref={this.companyProjectRef}
+ required={true}
+ errors={errors}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="link"
+ label="Do you have any documentation or a website?"
+ type="text"
+ ref={this.linkRef}
+ errors={errors}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="comments"
+ label="Anything else?"
+ type="textarea"
+ ref={this.commentsRef}
+ errors={errors}
+ />
+ </InputRow>
+ </>
+ );
+ }
private async _onSubmitAsync(e: Event): Promise<void> {
e.preventDefault();
- const name = this.nameRef.current.value;
- const email = this.emailRef.current.value;
- const projectOrCompany = this.companyProjectRef.current.value;
- const link = this.linkRef.current.value;
- const comments = this.commentsRef.current.value;
+ let jsonBody;
+ if (this.props.modalContactType === ModalContactType.MarketMaker) {
+ jsonBody = {
+ name: this.nameRef.current.value,
+ email: this.emailRef.current.value,
+ country: this.countryRef.current.value,
+ fundSize: this.fundSizeRef.current.value,
+ projectOrCompany: this.companyProjectRef.current.value,
+ comments: this.commentsRef.current.value,
+ };
+ } else {
+ jsonBody = {
+ name: this.nameRef.current.value,
+ email: this.emailRef.current.value,
+ projectOrCompany: this.companyProjectRef.current.value,
+ link: this.linkRef.current.value,
+ comments: this.commentsRef.current.value,
+ };
+ }
this.setState({ ...this.state, errors: [], isSubmitting: true });
+ const endpoint =
+ this.props.modalContactType === ModalContactType.MarketMaker ? '/market_maker_leads' : '/leads';
+
try {
// Disabling no-unbound method b/c no reason for _.isEmpty to be bound
// tslint:disable:no-unbound-method
- const response = await fetch('https://website-api.0xproject.com/leads', {
+ const response = await fetch(`https://website-api.0xproject.com${endpoint}`, {
method: 'post',
mode: 'cors',
credentials: 'same-origin',
headers: {
'content-type': 'application/json; charset=utf-8',
},
- body: JSON.stringify(_.omitBy({ name, email, projectOrCompany, link, comments }, _.isEmpty)),
+ body: JSON.stringify(_.omitBy(jsonBody, _.isEmpty)),
});
if (!response.ok) {
@@ -201,6 +317,7 @@ export class ModalContact extends React.Component<Props> {
);
}
}
+
// Handle errors: {"errors":[{"location":"body","param":"name","msg":"Invalid value"},{"location":"body","param":"email","msg":"Invalid value"}]}
const InputRow = styled.div`
diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx
index df77e4b76..45054772c 100644
--- a/packages/website/ts/index.tsx
+++ b/packages/website/ts/index.tsx
@@ -24,6 +24,7 @@ import { NextEcosystem } from 'ts/pages/ecosystem';
import { Next0xInstant } from 'ts/pages/instant';
import { NextLanding } from 'ts/pages/landing';
import { NextLaunchKit } from 'ts/pages/launch_kit';
+import { NextMarketMaker } from 'ts/pages/market_maker';
import { NextWhy } from 'ts/pages/why';
// Check if we've introduced an update that requires us to clear the tradeHistory local storage entries
@@ -99,6 +100,11 @@ render(
{/* Next (new site) routes */}
<Route exact={true} path="/" component={NextLanding as any} />
<Route exact={true} path={WebsitePaths.Why} component={NextWhy as any} />
+ <Route
+ exact={true}
+ path={WebsitePaths.MarketMaker}
+ component={NextMarketMaker as any}
+ />
<Route exact={true} path={WebsitePaths.Instant} component={Next0xInstant as any} />
<Route exact={true} path={WebsitePaths.LaunchKit} component={NextLaunchKit as any} />
<Route exact={true} path={WebsitePaths.Ecosystem} component={NextEcosystem as any} />
diff --git a/packages/website/ts/pages/market_maker.tsx b/packages/website/ts/pages/market_maker.tsx
index 55566c798..133ced864 100644
--- a/packages/website/ts/pages/market_maker.tsx
+++ b/packages/website/ts/pages/market_maker.tsx
@@ -1,43 +1,68 @@
import * as _ from 'lodash';
+import { opacify } from 'polished';
import * as React from 'react';
import { Banner } from 'ts/components/banner';
import { Button } from 'ts/components/button';
-import { Definition } from 'ts/components/definition';
+import { Action, Definition } from 'ts/components/definition';
import { Hero } from 'ts/components/hero';
-import { ModalContact } from 'ts/components/modals/modal_contact';
+import { ModalContact, ModalContactType } from 'ts/components/modals/modal_contact';
import { Section } from 'ts/components/newLayout';
import { SiteWrap } from 'ts/components/siteWrap';
+import { colors } from 'ts/style/colors';
+import { WebsitePaths } from 'ts/types';
-const offersData = [
- {
- icon: 'supportForAllEthereumStandards',
- title: 'Comprehensive Tutorials',
- description:
- 'Stay on the bleeding edge of crypto by learning how to market make on decentralized exchanges. The network of 0x relayers provides market makers a first-mover advantage to capture larger spreads, arbitrage markets, and access a long-tail of new tokens not currently listed on centralized exchanges.',
- },
- {
- icon: 'generateRevenueForYourBusiness-large',
- title: 'Market Making Compensation',
- description: (
- <ul>
- <li>Receive an infrastructure grant of $20,000+ for completing onboarding*</li>
- <li>Earn an additional $5,000 by referring other market makers to the Program*</li>
- </ul>
- ),
- },
- {
- icon: 'getInTouch',
- title: 'Personalized Support',
- description:
- 'The 0x MM Success Manager will walk you through how to read 0x order types, spin up an Ethereum node, set up your MM bot, and execute trades on the blockchain. We are more than happy to promptly answer your questions and give you complete onboarding assistance.',
- },
-];
+interface OfferData {
+ icon: string;
+ title: string;
+ description: string;
+ links?: Action[];
+}
+export interface NextMarketMakerProps {}
-export class NextMarketMaker extends React.Component {
+export class NextMarketMaker extends React.Component<NextMarketMakerProps> {
public state = {
isContactModalOpen: false,
};
+
+ private readonly _offersData: OfferData[];
+
+ constructor(props: NextMarketMakerProps) {
+ super(props);
+ this._offersData = [
+ {
+ icon: 'supportForAllEthereumStandards',
+ title: 'Comprehensive Tutorials',
+ description:
+ 'Stay on the bleeding edge of crypto by learning how to market make on decentralized exchanges. The network of 0x relayers provides market makers a first-mover advantage to capture larger spreads, find arbitrage opportunities, and trade on new types of exchanges like prediction markets and non-fungible token marketplaces.',
+ links: [
+ {
+ label: 'Explore the Docs',
+ url: `${WebsitePaths.Wiki}#Market-Making-on-0x`,
+ },
+ ],
+ },
+ {
+ icon: 'generateRevenueForYourBusiness-large',
+ title: 'Market Making Compensation',
+ description: 'Accepted applicants can receive up to $15,000 for completing onboarding',
+ },
+ {
+ icon: 'getInTouch',
+ title: 'Dedicated Support',
+ description:
+ 'The 0x team will provide 1:1 onboarding assistance and promptly answer all your questions. They will walk you through the tutorials so that you know how to read 0x order types, spin up an Ethereum node, and execute trades on the blockchain.',
+ links: [
+ {
+ label: 'Contact Us',
+ onClick: this._onOpenContactModal,
+ shouldUseAnchorTag: true,
+ },
+ ],
+ },
+ ];
+ }
+
public render(): React.ReactNode {
return (
<SiteWrap theme="light">
@@ -47,14 +72,14 @@ export class NextMarketMaker extends React.Component {
isLargeTitle={false}
isFullWidth={false}
isCenteredMobile={false}
- title="Bring liquidity to the exchanges of the future"
- description="Market makers (MMs) are important stakeholders in the 0x ecosystem. The Market Making Program provides a set of resources that help onboard MMs bring liquidity to the 0x network. The program includes tutorials, a robust data platform, trade compensation, and 1:1 support from our MM Success Manager."
- actions={<HeroActions />}
+ title="Bring liquidity to the markets of the future"
+ description="Market makers (MMs) are important stakeholders in the 0x ecosystem. The Market Making Program provides a set of resources that help onboard MMs to bring liquidity to the 0x network. The Program includes tutorials, monetary incentives, and 1:1 support from the 0x team."
+ actions={this._renderHeroActions()}
/>
<Section bgColor="light" isFlex={true} maxWidth="1170px">
<Definition
- title="Secure"
+ title="Secure Trading"
titleSize="small"
description="Take full custody of your assets to eliminate counterparty risk"
icon="secureTrading"
@@ -74,7 +99,7 @@ export class NextMarketMaker extends React.Component {
<Definition
title="Low Cost"
titleSize="small"
- description="Pay no fees on orders except for bulk cancellations"
+ description="Pay no gas fees to make 0x orders"
icon="low-cost"
iconSize="medium"
isInline={true}
@@ -82,7 +107,7 @@ export class NextMarketMaker extends React.Component {
</Section>
<Section>
- {_.map(offersData, (item, index) => (
+ {_.map(this._offersData, (item, index) => (
<Definition
key={`offers-${index}`}
icon={item.icon}
@@ -91,34 +116,47 @@ export class NextMarketMaker extends React.Component {
isInlineIcon={true}
iconSize={240}
fontSize="medium"
+ actions={item.links}
/>
))}
</Section>
<Banner
- heading="Need more flexibility?"
- subline="Dive into our docs, or contact us if needed"
- mainCta={{ text: 'Explore the Docs', href: '/docs' }}
- secondaryCta={{ text: 'Get in Touch', onClick: this._onOpenContactModal.bind(this) }}
+ heading="Start trading today."
+ subline="Check out our Market Making tutorials to get started"
+ mainCta={{ text: 'Tutorials', href: `${WebsitePaths.Wiki}#Market-Making-on-0x` }}
+ secondaryCta={{ text: 'Get in Touch', onClick: this._onOpenContactModal }}
+ />
+ <ModalContact
+ isOpen={this.state.isContactModalOpen}
+ onDismiss={this._onDismissContactModal}
+ modalContactType={ModalContactType.MarketMaker}
/>
- <ModalContact isOpen={this.state.isContactModalOpen} onDismiss={this._onDismissContactModal} />
</SiteWrap>
);
}
- public _onOpenContactModal = (): void => {
+ private readonly _onOpenContactModal = (): void => {
this.setState({ isContactModalOpen: true });
};
- public _onDismissContactModal = (): void => {
+ private readonly _onDismissContactModal = (): void => {
this.setState({ isContactModalOpen: false });
};
-}
-const HeroActions = () => (
- <>
- <Button href="https://github.com/0xProject/0x-launch-kit" bgColor="dark" isInline={true}>
- Get Started
- </Button>
- </>
-);
+ private readonly _renderHeroActions = () => (
+ <>
+ <Button href={`${WebsitePaths.Wiki}#Market-Making-on-0x`} bgColor="dark" isInline={true}>
+ Get Started
+ </Button>
+ <Button
+ onClick={this._onOpenContactModal}
+ borderColor={opacify(0.4)(colors.brandDark)}
+ isTransparent={true}
+ isInline={true}
+ >
+ Contact Us
+ </Button>
+ </>
+ );
+}