create superbowl contracts

adapt UI to allow multiple markets & fix small issues in contract generation
create no-pair logos with imagemagick
allow only supplying usdc
This commit is contained in:
Maximilian Schneider 2021-01-13 22:18:35 +01:00
parent be4113b8b5
commit 633f0b60d2
33 changed files with 604 additions and 193 deletions

View File

@ -1,7 +1,7 @@
USDC=Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7
CLUSTER_URL="https://devnet.solana.com"
CLUSTER=devnet
KEYPAIR=~/.config/solana/id.json
MY_ADDR="$(solana address)"
export USDC=Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7
export CLUSTER_URL="https://devnet.solana.com"
export CLUSTER=devnet
export KEYPAIR=~/.config/solana/id.json
export MY_ADDR="$(solana address)"
solana config set --url $CLUSTER_URL
OMEGA_PROGRAM_ID=9sSiUH8wZ1MCT3HJDYn8vMV57YuHQEXdNjVoncB5z6Cw
export OMEGA_PROGRAM_ID=9sSiUH8wZ1MCT3HJDYn8vMV57YuHQEXdNjVoncB5z6Cw

23
cli/markets/SBOWL21/buf.sh Executable file
View File

@ -0,0 +1,23 @@
TEAMS="KC GB NO BUF BAL TB"
CONTRACT_NAME="Buffalo Bills"
OUTCOME_NAMES="BUF-YES BUF-NO"
DETAILS="None"
CONTRACT_KEYS_PATH="../ui/src/markets/buf.json"
ICON_URLS="/markets/SUPERBOWL21/buf.png"
ICON_URLS="$ICON_URLS /markets/SUPERBOWL21/buf_no.png"
cargo run -- "$CLUSTER" init-omega-contract \
--payer "$KEYPAIR" \
--omega-program-id $OMEGA_PROGRAM_ID \
--oracle $MY_ADDR \
--quote-mint $USDC \
--num-outcomes 2 \
--outcome-names $OUTCOME_NAMES \
--contract-name "$CONTRACT_NAME" \
--details "$DETAILS" \
--exp-time "2021-02-09 00:00:00" \
--auto-exp-time "2021-07-01 00:00:00" \
--contract-keys-path "$CONTRACT_KEYS_PATH" \
--icon-urls $ICON_URLS

23
cli/markets/SBOWL21/gb.sh Executable file
View File

@ -0,0 +1,23 @@
TEAMS="KC GB NO BUF BAL TB"
CONTRACT_NAME="Green Bay Packers"
OUTCOME_NAMES="GB-YES GB-NO"
DETAILS="None"
CONTRACT_KEYS_PATH="../ui/src/markets/gb.json"
ICON_URLS="/markets/SUPERBOWL21/gb.png"
ICON_URLS="$ICON_URLS /markets/SUPERBOWL21/gb_no.png"
cargo run -- "$CLUSTER" init-omega-contract \
--payer "$KEYPAIR" \
--omega-program-id $OMEGA_PROGRAM_ID \
--oracle $MY_ADDR \
--quote-mint $USDC \
--num-outcomes 2 \
--outcome-names $OUTCOME_NAMES \
--contract-name "$CONTRACT_NAME" \
--details "$DETAILS" \
--exp-time "2021-02-09 00:00:00" \
--auto-exp-time "2021-07-01 00:00:00" \
--contract-keys-path "$CONTRACT_KEYS_PATH" \
--icon-urls $ICON_URLS

23
cli/markets/SBOWL21/kc.sh Executable file
View File

@ -0,0 +1,23 @@
TEAMS="KC GB NO BUF BAL TB"
CONTRACT_NAME="Kansas City Chiefs"
OUTCOME_NAMES="KC-YES KC-NO"
DETAILS="None"
CONTRACT_KEYS_PATH="../ui/src/markets/kc.json"
ICON_URLS="/markets/SUPERBOWL21/kc.png"
ICON_URLS="$ICON_URLS /markets/SUPERBOWL21/kc_no.png"
cargo run -- "$CLUSTER" init-omega-contract \
--payer "$KEYPAIR" \
--omega-program-id $OMEGA_PROGRAM_ID \
--oracle $MY_ADDR \
--quote-mint $USDC \
--num-outcomes 2 \
--outcome-names $OUTCOME_NAMES \
--contract-name "$CONTRACT_NAME" \
--details "$DETAILS" \
--exp-time "2021-02-09 00:00:00" \
--auto-exp-time "2021-07-01 00:00:00" \
--contract-keys-path "$CONTRACT_KEYS_PATH" \
--icon-urls $ICON_URLS

23
cli/markets/SBOWL21/tb.sh Executable file
View File

@ -0,0 +1,23 @@
TEAMS="KC GB NO BUF BAL TB"
CONTRACT_NAME="Tampa Bay Buccaneers"
OUTCOME_NAMES="TB-YES TB-NO"
DETAILS="None"
CONTRACT_KEYS_PATH="../ui/src/markets/tb.json"
ICON_URLS="/markets/SUPERBOWL21/tb.png"
ICON_URLS="$ICON_URLS /markets/SUPERBOWL21/tb_no.png"
cargo run -- "$CLUSTER" init-omega-contract \
--payer "$KEYPAIR" \
--omega-program-id $OMEGA_PROGRAM_ID \
--oracle $MY_ADDR \
--quote-mint $USDC \
--num-outcomes 2 \
--outcome-names $OUTCOME_NAMES \
--contract-name "$CONTRACT_NAME" \
--details "$DETAILS" \
--exp-time "2021-02-09 00:00:00" \
--auto-exp-time "2021-07-01 00:00:00" \
--contract-keys-path "$CONTRACT_KEYS_PATH" \
--icon-urls $ICON_URLS

17
cli/render_no_logo.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
cli_dir="$(dirname "$0")"
image_dir="$(dirname "$1")"
filename="$(basename "$1")"
yes_image="$1"
no_image="$(dirname "$yes_image")/${filename%.*}_no.${filename##*.}"
overlay_image="$cli_dir/../ui/design/cross.png"
size=$(convert $yes_image -format "%wx%h" info:)
echo "cli > $cli_dir"
echo "yes > $yes_image"
echo "no > $no_image"
echo "overlay > $overlay_image"
echo "size > $size"
composite $overlay_image $yes_image -geometry $size $no_image

BIN
ui/design/cross.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@ -4,6 +4,7 @@ import { shortenAddress } from "./../utils/utils";
import { Identicon } from "./identicon";
import { useUserAccounts, useMint } from "./../utils/accounts";
import contract_keys from "../contract_keys.json";
import { markets } from "../markets";
export const AccountInfo = (props: {}) => {
const { wallet } = useWallet();
@ -29,6 +30,24 @@ export const AccountInfo = (props: {}) => {
return 0;
};
const outcomes =
Object.values(markets).map(m => m.outcomes[0]).concat(
Object.values(markets).map(m => m.outcomes[1]));
// sort in-place
outcomes.sort((a,b) => a.name.localeCompare(b.name));
let userOutcomeBalances = new Map<string, number>();
outcomes.forEach(o => {
const outcomeAccount = userAccounts?.find(a => a.info.mint.toBase58() === o.mint_pk);
if (outcomeAccount && mint) {
const balance = outcomeAccount.info.amount.toNumber() / Math.pow(10, mint.decimals - 1);
if (balance > 0)
userOutcomeBalances.set(o.name, balance);
}
});
const userYesBalance = () => {
const yesAccount = userAccounts?.find(
(t) => t.info.mint.toBase58() === contract_keys.outcomes[0].mint_pk
@ -57,10 +76,12 @@ export const AccountInfo = (props: {}) => {
return (
<div className="wallet-wrapper">
<span>
{userUiBalance().toFixed(2)} USDC {userYesBalance().toFixed(2)} YES {userNoBalance().toFixed(2)} NO
USDC: {userUiBalance().toFixed(2)}
{Array.from(userOutcomeBalances.entries()).map(([name, amount]) =>
` ${name}: ${amount.toFixed(2)}`).join('')}
</span>
<div className="wallet-key">
{shortenAddress(`${wallet.publicKey}`)}
{' ' + shortenAddress(`${wallet.publicKey}`)}
<Identicon
address={wallet.publicKey.toBase58()}
style={{ marginLeft: "0.5rem" }}

View File

@ -10,6 +10,43 @@ import "./trade/trade.less";
import { CurrencyPairProvider } from "../utils/currencyPair";
import contract_keys from "../contract_keys.json";
import { markets } from "../markets";
const MarketsView = (props: {}) => {
const colStyle = { padding: "0.5em", width: 256+64 };
return (
<>
<Row justify="center">
{ markets.map(m =>
<>
<Col>
<div style={colStyle}>
<Card>
<p>
<img src={m.outcomes[0].icon} alt={m.outcomes[0].name} width="200px" height="200px"/>
</p>
<h1>{m.contract_name}</h1>
<p>
<CurrencyPairProvider baseMintAddress={m.quote_mint_pk}
quoteMintAddress={m.outcomes[0].mint_pk} >
<BetButton label="YES" type="primary" market={m} outcome={m.outcomes[0]} />
</CurrencyPairProvider>
<CurrencyPairProvider baseMintAddress={m.quote_mint_pk}
quoteMintAddress={m.outcomes[1].mint_pk} >
<BetButton label="NO" type={undefined} market={m} outcome={m.outcomes[1]} />
</CurrencyPairProvider>
</p>
</Card>
</div>
</Col>
</>
)}
</Row>
</>
);
};
export const BetView = (props: {}) => {
@ -38,46 +75,17 @@ export const BetView = (props: {}) => {
bodyStyle={{ width: 700 }}
>
<div>
<h1>Will the Democrats win the US Senate?</h1>
<h1>Which team will win the 55th Super Bowl?</h1>
<Row justify="center">
<Col>
<div className="bet-outcome">
<div>
<h2>YES</h2>
</div>
<div>
<img src={contract_keys.outcomes[0].icon} alt="YES ICON" width="200px" height="200px" />
</div>
<div>
<CurrencyPairProvider baseMintAddress={contract_keys.quote_mint_pk}
quoteMintAddress={contract_keys.outcomes[0].mint_pk} >
<BetButton />
</CurrencyPairProvider>
</div>
</div>
</Col>
<Col>
<div className="bet-outcome">
<h2>NO</h2>
<img src={contract_keys.outcomes[1].icon} alt="NO ICON" width="200px" height="200px"/>
<div>
<CurrencyPairProvider baseMintAddress={contract_keys.quote_mint_pk}
quoteMintAddress={contract_keys.outcomes[1].mint_pk} >
<BetButton />
</CurrencyPairProvider>
</div>
</div>
</Col>
</Row>
<Row justify="center">
<Col>
<p>{contract_keys.details}</p>
<p>The winner will be resolved by February 8th 08:00 AM UTC according to the results of the championship game. Winning tokens can be redeemed by March 8th 08:00 AM UTC.</p>
<Divider />
<p><b>Disclaimer:</b> Trading on Omega is not available in the United States or other prohibited jurisdictions. If you are located in, incorporated or otherwise established in, or a resident of the United States of America, you are not permitted to trade on Omega.</p>
<p><b>Disclaimer:</b> Trading on Omega is not available in the U.S.A, EU or other prohibited jurisdictions. If you are located in, incorporated or otherwise established in, or a resident of the United States of America or any European nation, you are not permitted to trade on Omega.</p>
</Col>
</Row>
</div>
</Card>
<MarketsView></MarketsView>
</>
);
};

View File

@ -1,13 +1,115 @@
import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import { useHistory } from "react-router-dom";
import { Button } from "antd";
import { Button, Modal } from "antd";
import { PoolOperation } from "../../utils/pools";
import { useCurrencyPairState } from "../../utils/currencyPair";
import { useConnection, useSlippageConfig } from "../../utils/connection";
import { useCurrencyPairState, CurrencyPairProvider } from "../../utils/currencyPair";
import { notify } from "../../utils/notifications";
import { swap, usePoolForBasket, PoolOperation } from "../../utils/pools";
import { useWallet } from "../../utils/wallet";
import { BetInput } from "./input";
const BetModalContent = (props: {
market: { quote_mint_pk: string },
outcome: { name: string, mint_pk: string } }) => {
const connection = useConnection();
const { slippage } = useSlippageConfig();
const { connected, wallet } = useWallet();
const {
A,
B,
setLastTypedAccount,
setPoolOperation,
} = useCurrencyPairState();
const pool = usePoolForBasket([A?.mintAddress, B?.mintAddress]);
const [betAmount, setBetAmount] = useState("0");
const placeBet = async function() {
if (A.account && B.mintAddress) {
try {
//setPendingTx(true);
const components = [
{
account: A.account,
mintAddress: A.mintAddress,
amount: A.convertAmount(),
},
{
mintAddress: B.mintAddress,
amount: B.convertAmount(),
},
];
await swap(connection, wallet, components, slippage, pool);
} catch {
notify({
description:
"Please try again and approve transactions from your wallet",
message: "Trade cancelled.",
type: "error",
});
} finally {
//setPendingTx(false);
}
}
};
return (
<>
<BetInput mint={A.mintAddress}
amount={A.amount}
onInputChange={(val) => {
setPoolOperation(PoolOperation.SwapGivenInput);
setLastTypedAccount(A.mintAddress);
A.setAmount(val);
}} />
<p>Maximum Profit: {(parseFloat(B.amount) * (1 - slippage)).toFixed(2)} USDC</p>
{!connected && (
<Button
className="float-right"
type="primary"
size="large"
onClick={wallet.connect}
>
Connect Wallet
</Button>
)}
{connected && (
<Button
className="float-right"
type="primary"
size="large"
onClick={placeBet}
>
Place Bet
</Button>
)}
</>);
}
export const BetButton = (props: {
}) => {
type: "primary" | undefined,
label: string,
market: { quote_mint_pk: string },
outcome: { name: string, mint_pk: string } }) => {
const {
A,
@ -22,8 +124,6 @@ export const BetButton = (props: {
setPoolOperation(PoolOperation.SwapGivenInput);
setLastTypedAccount(A.mintAddress);
A.setAmount(epsilon.toString());
console.log('A', A.amount, A);
console.log('B', B.amount, B);
}, [A, B, setPoolOperation, setLastTypedAccount]);
const history = useHistory();
@ -33,15 +133,31 @@ export const BetButton = (props: {
odds = (100 * epsilon / parseFloat(B.amount)).toFixed(0);
}
const [visible, setVisible] = useState(false);
return (
<>
<Button
className="bet-button"
type="primary"
type={props.type}
size="large"
onClick={() => history.push('/exchange')}
onClick={() => setVisible(true)}
>
<span>{odds}¢</span>
<span>{props.label} {odds}¢</span>
</Button>
<Modal
visible={visible}
title={`Bet on ${props.outcome.name}`}
footer={null}
onCancel={() => setVisible(false)}
>
<CurrencyPairProvider baseMintAddress={props.market.quote_mint_pk}
quoteMintAddress={props.outcome.mint_pk} >
<BetModalContent market={props.market} outcome={props.outcome}/>
</CurrencyPairProvider>
</Modal>
</>
);
}

View File

@ -11,6 +11,7 @@ import { PoolInfo, TokenAccount } from "../../models";
import { getPoolName, getTokenName, isKnownMint } from "../../utils/utils";
import { useUserAccounts, useMint, useCachedPool } from "../../utils/accounts";
import { useConnectionConfig } from "../../utils/connection";
import contract_keys from "../../contract_keys.json";
const { Option } = Select;
@ -61,6 +62,10 @@ export const BetInput = (props: {
return map;
}
if (!(mint === contract_keys.quote_mint_pk)) {
return map;
}
const pool = pools.find((p) => p && p.pubkeys.mint.toBase58() === mint);
map.set(mint, (map.get(mint) || []).concat([{ account: acc, pool }]));

View File

@ -6,6 +6,7 @@ import { AppBar } from "./appBar";
import { CurrencyPairProvider } from "../utils/currencyPair";
import { SwapView } from "./swap";
import contract_keys from "../contract_keys.json";
import { markets } from "../markets";
export const ExchangeView = (props: {}) => {
@ -31,24 +32,28 @@ export const ExchangeView = (props: {}) => {
</Popover>
}
/>
<Row justify="center">
<Col>
<div style={colStyle}>
<CurrencyPairProvider baseMintAddress={contract_keys.quote_mint_pk}
quoteMintAddress={contract_keys.outcomes[0].mint_pk} >
<SwapView />
</CurrencyPairProvider>
</div>
</Col>
<Col>
<div style={colStyle}>
<CurrencyPairProvider baseMintAddress={contract_keys.quote_mint_pk}
quoteMintAddress={contract_keys.outcomes[1].mint_pk} >
<SwapView />
</CurrencyPairProvider>
</div>
</Col>
</Row>
{ markets.map((market: any) =>
<>
<Row justify="center">
<Col flex={2}>
<div style={colStyle}>
<CurrencyPairProvider baseMintAddress={market.quote_mint_pk}
quoteMintAddress={market.outcomes[0].mint_pk} >
<SwapView />
</CurrencyPairProvider>
</div>
</Col>
<Col flex={2}>
<div style={colStyle}>
<CurrencyPairProvider baseMintAddress={market.quote_mint_pk}
quoteMintAddress={market.outcomes[1].mint_pk} >
<SwapView />
</CurrencyPairProvider>
</div>
</Col>
</Row>
</>
)}
</>
);
};

View File

@ -12,12 +12,13 @@ import {TokenInstructions} from '@project-serum/serum';
import BufferLayout from 'buffer-layout';
import {AccountLayout} from '@solana/spl-token';
import { Button, Card, Popover, Col, Row } from "antd";
import { Button, Card, Dropdown, Menu, Select, Popover, Col, Row } from "antd";
import { NumericInput } from "./numericInput";
import { Settings } from "./settings";
import { SettingOutlined } from "@ant-design/icons";
import { AppBar } from "./appBar";
import contract_keys from "../contract_keys.json";
import { markets } from "../markets";
import { useMint } from '../utils/accounts';
@ -34,11 +35,9 @@ const QUOTE_CURRENCY_MINT = new PublicKey(contract_keys.quote_mint_pk);
console.log('QUOTE_CURRENCY', QUOTE_CURRENCY, QUOTE_CURRENCY_MINT.toString());
const CONTRACT_NAME = contract_keys.contract_name;
const OMEGA_CONTRACT = new PublicKey(contract_keys.omega_contract_pk);
const OMEGA_QUOTE_VAULT = new PublicKey(contract_keys.quote_vault_pk);
console.log('MARKET', CONTRACT_NAME, OMEGA_QUOTE_VAULT.toString());
markets.forEach(m => {
console.log('MARKET', m.contract_name);
});
/* INSTRUCTIONS
@ -63,11 +62,11 @@ const marketContractLayout = BufferLayout.struct([
]);
async function queryMarketContract(conn) {
const accountInfo = await conn.getParsedAccountInfo(OMEGA_CONTRACT, 'singleGossip');
async function queryMarketContract(conn, contract) {
const accountInfo = await conn.getParsedAccountInfo(contract, 'singleGossip');
const result = marketContractLayout.decode(Buffer.from(accountInfo.value.data));
console.log('QUERY', CONTRACT_NAME, result);
console.log('QUERY', contract, result);
return result;
};
@ -198,19 +197,20 @@ export const RedeemView = (props) => {
const { wallet, connected } = useWallet();
const quoteMint = useMint(contract_keys.quote_mint_pk);
const [contractData, setContractData] = useState({
exp_time: 1612137600 // 02/01/2021 00:00 UTC
exp_time: 1612137600, // 02/01/2021 00:00 UTC
decided: false
});
// TODO: test once we have a demo contract deployed
useEffect(() => {
async function fetchContractData() {
let data = await queryMarketContract(connection);
async function fetchContractData(market) {
console.log('fetchContractData', market);
let data = await queryMarketContract(connection, new PublicKey(market.omega_contract_pk));
let winner = new PublicKey(data.winner);
let zeroPubkey = new PublicKey(new Uint8Array(32));
data['decided'] = !winner.equals(zeroPubkey);
setContractData(data);
};
fetchContractData();
fetchContractData(markets[0]);
}, [connection]);
useEffect(() => {
console.log('contract.exp_time', new Date(contractData.exp_time * 1000));
@ -284,17 +284,16 @@ export const RedeemView = (props) => {
return parseFloat(amount) * Math.pow(10, quoteMint.decimals);
}
async function issueSet(amount) {
async function issueSet(market, amount) {
if (!wallet.connected) await wallet.connect();
console.log('issueSet', amount);
const accounts = await fetchAccounts();
let omegaSigner = new PublicKey(contract_keys.signer_pk);
let userQuote = await userTokenAccount(accounts, QUOTE_CURRENCY_MINT);
let outcomePks = [];
let outcomeInfos = contract_keys["outcomes"];
let outcomeInfos = market["outcomes"];
let numOutcomes = outcomeInfos.length;
for (let i = 0; i < numOutcomes; i++) {
let outcomeMint = new PublicKey(outcomeInfos[i]["mint_pk"]);
@ -303,8 +302,14 @@ export const RedeemView = (props) => {
outcomePks.push(userOutcomeWallet);
console.log(outcomeInfos[i]["name"], outcomeMint, userOutcomeWallet);
}
let issueSetInstruction = IssueSetInstruction(OMEGA_CONTRACT, wallet.publicKey, userQuote, OMEGA_QUOTE_VAULT,
omegaSigner, outcomePks, amount);
let issueSetInstruction = IssueSetInstruction(
new PublicKey(market.omega_contract_pk),
wallet.publicKey,
userQuote,
new PublicKey(market.quote_vault_pk),
new PublicKey(market.signer_pk),
outcomePks,
amount);
let transaction = new Transaction();
transaction.add(issueSetInstruction);
@ -312,15 +317,14 @@ export const RedeemView = (props) => {
console.log('success txid:', txid);
}
async function redeemSet(amount) {
async function redeemSet(market, amount) {
if (!wallet.connected) await wallet.connect();
console.log('redeemSet', amount);
const accounts = await fetchAccounts();
let omegaSigner = new PublicKey(contract_keys.signer_pk);
let userQuote = await userTokenAccount(accounts, QUOTE_CURRENCY_MINT);
let outcomePks = [];
let outcomeInfos = contract_keys["outcomes"];
let outcomeInfos = market["outcomes"];
let numOutcomes = outcomeInfos.length;
for (let i = 0; i < numOutcomes; i++) {
let outcomeMint = new PublicKey(outcomeInfos[i]["mint_pk"]);
@ -329,8 +333,14 @@ export const RedeemView = (props) => {
outcomePks.push(userOutcomeWallet);
console.log(outcomeInfos[i]["name"], outcomeMint, userOutcomeWallet);
}
let redeemSetInstruction = RedeemSetInstruction(OMEGA_CONTRACT, wallet.publicKey, userQuote, OMEGA_QUOTE_VAULT,
omegaSigner, outcomePks, amount);
let redeemSetInstruction = RedeemSetInstruction(
new PublicKey(market.omega_contract_pk),
wallet.publicKey,
userQuote,
new PublicKey(market.quote_vault_pk),
new PublicKey(market.signer_pk),
outcomePks,
amount);
let transaction = new Transaction();
transaction.add(redeemSetInstruction);
@ -338,7 +348,7 @@ export const RedeemView = (props) => {
console.log('success txid:', txid);
}
async function redeemWinner(amount) {
async function redeemWinner(market, amount) {
if (!wallet.connected) await wallet.connect();
console.log('redeemWinner', amount);
@ -354,11 +364,16 @@ export const RedeemView = (props) => {
let winnerWallet = await userTokenAccount(accounts, winner);
let userQuote = await userTokenAccount(accounts, QUOTE_CURRENCY_MINT);
let omegaSigner = new PublicKey(contract_keys.signer_pk);
let redeemWinnerInstruction = RedeemWinnerInstruction(OMEGA_CONTRACT, wallet.publicKey, userQuote,
OMEGA_QUOTE_VAULT, omegaSigner, winner, winnerWallet, amount);
let redeemWinnerInstruction = RedeemWinnerInstruction(
new PublicKey(market.omega_contract_pk),
wallet.publicKey,
userQuote,
new PublicKey(market.quote_vault_pk),
new PublicKey(market.signer_pk),
winner,
winnerWallet,
amount);
let transaction = new Transaction();
transaction.add(redeemWinnerInstruction);
@ -368,14 +383,29 @@ export const RedeemView = (props) => {
}
const colStyle = { padding: "0.5em", width: 256+128 };
const colStyle = { padding: "0.5em", width: 512 };
const [winnerAmount, setWinnerAmount] = useState("");
const [redeemAmount, setRedeemAmount] = useState("");
const [issueAmount, setIssueAmount] = useState("");
const [winnerMarket, setWinnerMarket] = useState(markets[0]);
const [redeemMarket, setRedeemMarket] = useState(markets[0]);
const [issueMarket, setIssueMarket] = useState(markets[0]);
const onSelectWinnerMarket = (val) => {
console.log(`onSelectWinnerMarket ${val}`);
setWinnerMarket(markets.find(m => m.contract_name === val));
};
const onSelectRedeemMarket = (val) => {
console.log(`onSelectRedeemMarket ${val}`);
setRedeemMarket(markets.find(m => m.contract_name === val));
};
const onSelectIssueMarket = (val) => {
console.log(`onSelectIssueMarket ${val}`);
setIssueMarket(markets.find(m => m.contract_name === val));
};
return (
<>
@ -395,90 +425,102 @@ export const RedeemView = (props) => {
/>
</Popover>
}
/>
<Row justify="center">
<Col>
<div style={colStyle}>
<Card>
<h2>Redeem Winner</h2>
<p>After the oracle has resolved the contract, you may redeem the winning token for equal quantities of USDC.</p>
<NumericInput
value={winnerAmount}
onChange={setWinnerAmount}
style={{
"margin-bottom": 10,
}}
placeholder="0.00"
disabled={!contractData.decided}
/>
/>
<Row justify="center">
<div style={colStyle}>
<Card>
<h2>Redeem Winner</h2>
<p>After the oracle has resolved the contract, you may redeem the winning token for equal quantities of USDC.</p>
<NumericInput
value={winnerAmount}
onChange={setWinnerAmount}
style={{
"margin-bottom": 10,
}}
placeholder="0.00"
disabled={!contractData.decided}
addonBefore={ <Select defaultValue={winnerMarket.contract_name} onChange={onSelectWinnerMarket}>
{ markets.map(m => <Select.Option value={m.contract_name}>{m.contract_name}</Select.Option>)}
</Select>
}
/>
<Button
className="trade-button"
type="primary"
onClick={connected ? () => redeemWinner(parseAmount(winnerAmount)) : wallet.connect}
style={{ width: "100%" }}
disabled={!contractData.decided}
>
{ connected ? "Redeem Tokens" : "Connect Wallet" }
</Button>
</Card>
</div>
</Col>
<Col>
<div style={colStyle}>
<Card>
<h2>Redeem Set</h2>
<p>Swap {contract_keys.contract_name} {contract_keys.outcomes[0].name} and {contract_keys.contract_name} {contract_keys.outcomes[1].name} for equal quantities of USDC.</p>
<NumericInput
value={redeemAmount}
onChange={setRedeemAmount}
style={{
"margin-bottom": 10,
}}
addonAfter={`${contract_keys.outcomes[0].name} & ${contract_keys.outcomes[1].name}`}
placeholder="0.00"
/>
<Button
className="trade-button"
type="primary"
onClick={connected ? () => redeemSet(parseAmount(redeemAmount)) : wallet.connect}
style={{ width: "100%" }}
>
{ connected ? "Redeem Tokens" : "Connect Wallet" }
</Button>
</Card>
</div>
</Col>
<Col>
<div style={colStyle}>
<Card>
<h2>Issue Set</h2>
<p>Swap USDC for equal quantities of {contract_keys.contract_name} {contract_keys.outcomes[0].name} and {contract_keys.contract_name} {contract_keys.outcomes[1].name} tokens.</p>
<NumericInput
value={issueAmount}
onChange={setIssueAmount}
style={{
"margin-bottom": 10,
}}
addonAfter="USDC"
placeholder="0.00"
/>
<Button
className="trade-button"
type="primary"
onClick={connected ? () => issueSet(parseAmount(issueAmount)) : wallet.connect}
style={{ width: "100%" }}
>
{ connected ? "Issue Tokens" : "Connect Wallet" }
<Button
className="trade-button"
type="primary"
onClick={connected ? () => redeemWinner(winnerMarket, parseAmount(winnerAmount)) : wallet.connect}
style={{ width: "100%" }}
disabled={!contractData.decided}
>
{ connected ? "Redeem Tokens" : "Connect Wallet" }
</Button>
</Button>
</Card>
</div>
</Row>
<Row justify="center">
<div style={colStyle}>
<Card>
<h2>Redeem Set</h2>
<p>Swap {redeemMarket.outcomes[0].name} and {redeemMarket.outcomes[1].name} for equal quantities of USDC.</p>
</Card>
</div>
</Col>
</Row>
<NumericInput
value={redeemAmount}
onChange={setRedeemAmount}
style={{
"margin-bottom": 10,
}}
addonAfter={`${redeemMarket.outcomes[0].name} & ${redeemMarket.outcomes[1].name}`}
addonBefore={ <Select defaultValue={redeemMarket.contract_name} onChange={onSelectRedeemMarket}>
{ markets.map(m => <Select.Option value={m.contract_name}>{m.contract_name}</Select.Option>)}
</Select>}
placeholder="0.00"
/>
<Button
className="trade-button"
type="primary"
onClick={connected ? () => redeemSet(redeemMarket, parseAmount(redeemAmount)) : wallet.connect}
style={{ width: "100%" }}
>
{ connected ? "Redeem Tokens" : "Connect Wallet" }
</Button>
</Card>
</div>
</Row>
<Row justify="center">
<div style={colStyle}>
<Card>
<h2>Issue Set</h2>
<p>Swap USDC for equal quantities of {issueMarket.contract_name} {issueMarket.outcomes[0].name} and {issueMarket.contract_name} {issueMarket.outcomes[1].name} tokens.</p>
<NumericInput
value={issueAmount}
onChange={setIssueAmount}
style={{
"margin-bottom": 10,
}}
addonBefore={ <Select defaultValue={issueMarket.contract_name} onChange={onSelectIssueMarket}>
{ markets.map(m => <Select.Option value={m.contract_name}>{m.contract_name}</Select.Option>)}
</Select>}
addonAfter="USDC"
placeholder="0.00"
/>
<Button
className="trade-button"
type="primary"
onClick={connected ? () => issueSet(issueMarket, parseAmount(issueAmount)) : wallet.connect}
style={{ width: "100%" }}
>
{ connected ? "Issue Tokens" : "Connect Wallet" }
</Button>
</Card>
</div>
</Row>
</>
);
};

23
ui/src/markets/buf.json Normal file
View File

@ -0,0 +1,23 @@
{
"contract_name": "Buffalo Bills",
"details": "None",
"omega_contract_pk": "166vqxbrvTsGQA1As4NRgkxtCaf2ai3dvXzjrFKGFEy",
"omega_program_id": "9sSiUH8wZ1MCT3HJDYn8vMV57YuHQEXdNjVoncB5z6Cw",
"oracle_pk": "AiV4j8BnezLr6xojE2P7DT1cMCQKwybinSYBusoECm9w",
"outcomes": [
{
"icon": "/markets/SUPERBOWL21/buf.png",
"mint_pk": "F6aeFzCcoqZx4u7ggonCM5bKpbvD2HvhHmNYhYyGgMrY",
"name": "BUF-YES"
},
{
"icon": "/markets/SUPERBOWL21/buf_no.png",
"mint_pk": "2cfN1ZS7MBJZeVfixcRYffTH5HUZ8PcPoswDcTUeJuhY",
"name": "BUF-NO"
}
],
"quote_mint_pk": "Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7",
"quote_vault_pk": "83bbUP2fsdPgtc9MBBQwWU8X9Uy7W3DPaoNYHMunpyta",
"signer_nonce": 0,
"signer_pk": "6A64XHcqAeKZwJKYvUkCJGaheML1WyKHEYyoD7WeDh84"
}

23
ui/src/markets/gb.json Normal file
View File

@ -0,0 +1,23 @@
{
"contract_name": "Green Bay Packers",
"details": "None",
"omega_contract_pk": "7zKqUHTjH99fyUTBR9uQA6mWnTfKCJpz5ATPvcZrFB1u",
"omega_program_id": "9sSiUH8wZ1MCT3HJDYn8vMV57YuHQEXdNjVoncB5z6Cw",
"oracle_pk": "AiV4j8BnezLr6xojE2P7DT1cMCQKwybinSYBusoECm9w",
"outcomes": [
{
"icon": "/markets/SUPERBOWL21/gb.png",
"mint_pk": "CLVCpSeTUe9r3dGVUnd4tjv3kVYVntoTd54QMimxyQhQ",
"name": "GB-YES"
},
{
"icon": "/markets/SUPERBOWL21/gb_no.png",
"mint_pk": "76PF2sWfMJsTJ8kiFc5g7ARfn7TTCBDUUrxAV1Lkterz",
"name": "GB-NO"
}
],
"quote_mint_pk": "Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7",
"quote_vault_pk": "5rtxiUQSFwccZC8jhG3e2svCckuNSbVvMCgsAZUcQe6D",
"signer_nonce": 1,
"signer_pk": "5cPdPnb92g3YnCALWnWe6ASzzj6o8urmHm5DnUP5B3iz"
}

11
ui/src/markets/index.js Normal file
View File

@ -0,0 +1,11 @@
import buf from "./buf.json";
import gb from "./gb.json";
import kc from "./kc.json";
import tb from "./tb.json";
export const markets = [
kc,
gb,
buf,
tb
];

23
ui/src/markets/kc.json Normal file
View File

@ -0,0 +1,23 @@
{
"contract_name": "Kansas City Chiefs",
"details": "None",
"omega_contract_pk": "FgEPtRH7XH8jUcX394QGA4ggJbjy2ii2pPuTCwudmXSn",
"omega_program_id": "9sSiUH8wZ1MCT3HJDYn8vMV57YuHQEXdNjVoncB5z6Cw",
"oracle_pk": "AiV4j8BnezLr6xojE2P7DT1cMCQKwybinSYBusoECm9w",
"outcomes": [
{
"icon": "/markets/SUPERBOWL21/kc.png",
"mint_pk": "DV63zwVsfhkrGBwZBDoZqCTGJUHsz4hYEdfx7DxYGJ2c",
"name": "KC-YES"
},
{
"icon": "/markets/SUPERBOWL21/kc_no.png",
"mint_pk": "4CpcJvB9Qb4C3SmiVLsqTE1QY3vMNfQ6sPyGwy1yMmd8",
"name": "KC-NO"
}
],
"quote_mint_pk": "Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7",
"quote_vault_pk": "DvKiKpJGJfXKeUkUqgKNWeQ2pqn5iyjXgFXw1LsjysDW",
"signer_nonce": 1,
"signer_pk": "GEgT1mcKQRmGyJRnMgTF4jZYpY97WbLPKuJg5u6NJg8M"
}

23
ui/src/markets/tb.json Normal file
View File

@ -0,0 +1,23 @@
{
"contract_name": "Tampa Bay Buccaneers",
"details": "None",
"omega_contract_pk": "CSZ7ZQswbMaKBvH2aMuf7tYVaBbGE7ieAeinB8XSU4oV",
"omega_program_id": "9sSiUH8wZ1MCT3HJDYn8vMV57YuHQEXdNjVoncB5z6Cw",
"oracle_pk": "AiV4j8BnezLr6xojE2P7DT1cMCQKwybinSYBusoECm9w",
"outcomes": [
{
"icon": "/markets/SUPERBOWL21/tb.png",
"mint_pk": "3kRkByAQtCnL1nT9FfZQDKBDHH1ur4GDnqE3j8EX6ukx",
"name": "TB-YES"
},
{
"icon": "/markets/SUPERBOWL21/tb_no.png",
"mint_pk": "9gcLVUTwkAVRSB8SpKR2WKWy9ij2U3kEqZv12Nsx2awE",
"name": "TB-NO"
}
],
"quote_mint_pk": "Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7",
"quote_vault_pk": "CAdWm9MM9J9h3zQzxqoGUtLHd3kR5tSX629FGtjAgL5c",
"signer_nonce": 1,
"signer_pk": "BbMvjif2qDCgfNbp1ygMgrZ4FErRDzWxUGAFjMpLKDqx"
}

View File

@ -12,6 +12,7 @@ import { notify } from "./notifications";
import { ExplorerLink } from "../components/explorerLink";
import contract_keys from "../contract_keys.json";
import { markets } from "../markets";
export type Outcome = {
mint_pk: string,
@ -98,19 +99,19 @@ export function ConnectionProvider({ children = undefined as any }) {
return map;
}, new Map<string, KnownToken>());
contract_keys.outcomes.forEach(function (outcome: any) {
markets.forEach(function (market: any) {
market.outcomes.forEach(function (outcome: any) {
let outcomeTk = {
tokenSymbol: outcome.name,
tokenName: `${contract_keys.contract_name} ${outcome.name}`,
icon: outcome.icon.replace('https://predictomega.org', ''),
mintAddress: outcome.mint_pk,
};
let outcomeTk = {
tokenSymbol: outcome.name,
tokenName: `${contract_keys.contract_name} ${outcome.name}`,
icon: outcome.icon,
mintAddress: outcome.mint_pk,
};
list.push(outcomeTk);
knownMints.set(outcomeTk.mintAddress, outcomeTk);
list.push(outcomeTk);
knownMints.set(outcomeTk.mintAddress, outcomeTk);
});
});
let quoteTk = {
tokenSymbol: "USDC",
tokenName: "USDC",

View File

@ -19,6 +19,7 @@ import { useLocation } from "react-router-dom";
import bs58 from "bs58";
import contract_keys from "../contract_keys.json";
import { markets } from "../markets";
export interface CurrencyContextState {
mintAddress: string;
@ -235,8 +236,8 @@ const isValidAddress = (address: string) => {
};
function getDefaultTokens(tokens: KnownToken[], search: string) {
let defaultBase = contract_keys.outcomes[0].name;
let defaultQuote = contract_keys.outcomes[1].name;
let defaultBase = markets[0].outcomes[0].name;
let defaultQuote = markets[1].outcomes[1].name;
const nameToToken = tokens.reduce((map, item) => {
map.set(item.tokenSymbol, item);