mirror of https://github.com/certusone/oyster.git
Merge branch 'main' of https://github.com/solana-labs/oyster into main
This commit is contained in:
commit
a5468636ca
|
@ -1,14 +1,28 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button } from 'antd';
|
import { Button, Dropdown, Menu } from 'antd';
|
||||||
|
|
||||||
import { useCorrectNetwork } from '../../hooks/useCorrectNetwork';
|
import { useCorrectNetwork } from '../../hooks/useCorrectNetwork';
|
||||||
import { shortenAddress } from '@oyster/common';
|
import { shortenAddress } from '@oyster/common';
|
||||||
import { useEthereum } from '../../contexts';
|
import { useEthereum } from '../../contexts';
|
||||||
|
|
||||||
export const EthereumConnect = () => {
|
export const EthereumConnect = () => {
|
||||||
const { accounts, onConnectEthereum, connected, walletProvider } = useEthereum();
|
const {
|
||||||
|
accounts,
|
||||||
|
onConnectEthereum,
|
||||||
|
connected,
|
||||||
|
walletProvider,
|
||||||
|
select,
|
||||||
|
} = useEthereum();
|
||||||
const { hasCorrespondingNetworks } = useCorrectNetwork();
|
const { hasCorrespondingNetworks } = useCorrectNetwork();
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item key="3" onClick={select}>
|
||||||
|
Change Eth Wallet
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ marginRight: 8 }}>
|
<div style={{ marginRight: 8 }}>
|
||||||
{connected ? (
|
{connected ? (
|
||||||
|
@ -28,6 +42,13 @@ export const EthereumConnect = () => {
|
||||||
WRONG NETWORK
|
WRONG NETWORK
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
) : !!walletProvider ? (
|
||||||
|
<Dropdown.Button
|
||||||
|
onClick={() => onConnectEthereum && onConnectEthereum()}
|
||||||
|
overlay={menu}
|
||||||
|
>
|
||||||
|
Connect Ethereum
|
||||||
|
</Dropdown.Button>
|
||||||
) : (
|
) : (
|
||||||
<Button onClick={() => onConnectEthereum && onConnectEthereum()}>
|
<Button onClick={() => onConnectEthereum && onConnectEthereum()}>
|
||||||
Connect Ethereum
|
Connect Ethereum
|
||||||
|
|
|
@ -9,10 +9,12 @@ import { AppBar } from '@oyster/common';
|
||||||
import Wormhole from '../Wormhole';
|
import Wormhole from '../Wormhole';
|
||||||
import { Footer as AppFooter } from './../Footer';
|
import { Footer as AppFooter } from './../Footer';
|
||||||
import { EthereumConnect } from '../EthereumConnect';
|
import { EthereumConnect } from '../EthereumConnect';
|
||||||
|
import { useEthereum } from '../../contexts';
|
||||||
|
|
||||||
const { Header, Content, Footer } = Layout;
|
const { Header, Content, Footer } = Layout;
|
||||||
|
|
||||||
export const AppLayout = React.memo((props: any) => {
|
export const AppLayout = React.memo((props: any) => {
|
||||||
|
const { connected, disconnect } = useEthereum();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [wormholeReady, setWormholeReady] = useState(false);
|
const [wormholeReady, setWormholeReady] = useState(false);
|
||||||
|
|
||||||
|
@ -41,7 +43,21 @@ export const AppLayout = React.memo((props: any) => {
|
||||||
<h2>WORMHOLE</h2>
|
<h2>WORMHOLE</h2>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<AppBar useWalletBadge={true} left={<EthereumConnect />} />
|
<AppBar
|
||||||
|
additionalSettings={
|
||||||
|
connected ? (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => disconnect()}
|
||||||
|
style={{ marginTop: '8px' }}
|
||||||
|
>
|
||||||
|
Disconnect ETH
|
||||||
|
</Button>
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
|
useWalletBadge={true}
|
||||||
|
left={<EthereumConnect />}
|
||||||
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
)}
|
)}
|
||||||
<Content style={{ padding: '0 50px', flexDirection: 'column' }}>
|
<Content style={{ padding: '0 50px', flexDirection: 'column' }}>
|
||||||
|
|
|
@ -119,7 +119,7 @@ export const TokenSelectModal = (props: {
|
||||||
visible={isModalVisible}
|
visible={isModalVisible}
|
||||||
onCancel={() => hideModal()}
|
onCancel={() => hideModal()}
|
||||||
footer={null}
|
footer={null}
|
||||||
className={"token-select-modal"}
|
className={'token-select-modal'}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
autoFocus
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
import React, {
|
import React, { createContext, FunctionComponent, useContext } from 'react';
|
||||||
createContext,
|
|
||||||
FunctionComponent,
|
|
||||||
useContext,
|
|
||||||
} from 'react';
|
|
||||||
import { SolanaBridge } from '../core';
|
import { SolanaBridge } from '../core';
|
||||||
import {
|
import {
|
||||||
useConnection,
|
useConnection,
|
||||||
|
|
|
@ -14,13 +14,13 @@ import { useWallet as useEthereumWallet } from 'use-wallet';
|
||||||
import WalletConnectProvider from '@walletconnect/web3-provider';
|
import WalletConnectProvider from '@walletconnect/web3-provider';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import Fortmatic from 'fortmatic';
|
import Fortmatic from 'fortmatic';
|
||||||
import { useWallet, useLocalStorageState} from '@oyster/common';
|
import { useWallet, useLocalStorageState } from '@oyster/common';
|
||||||
import { WalletAdapter } from '@solana/wallet-base'
|
import { WalletAdapter } from '@solana/wallet-base';
|
||||||
import { TokenList, TokenInfo } from '@uniswap/token-lists';
|
import { TokenList, TokenInfo } from '@uniswap/token-lists';
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import { MetamaskWalletAdapter } from '../wallet-adapters/metamask';
|
import { MetamaskWalletAdapter } from '../wallet-adapters/metamask';
|
||||||
import { Button, Modal } from 'antd';
|
import { Button, Modal } from 'antd';
|
||||||
import {WalletConnectWalletAdapter} from "../wallet-adapters/wallet-connect";
|
import { WalletConnectWalletAdapter } from '../wallet-adapters/wallet-connect';
|
||||||
|
|
||||||
const ASSETS_URL =
|
const ASSETS_URL =
|
||||||
'https://raw.githubusercontent.com/solana-labs/oyster/main/assets/wallets/';
|
'https://raw.githubusercontent.com/solana-labs/oyster/main/assets/wallets/';
|
||||||
|
@ -48,6 +48,8 @@ export interface EthereumContextState {
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
chainId: number;
|
chainId: number;
|
||||||
walletProvider: any;
|
walletProvider: any;
|
||||||
|
select: () => void;
|
||||||
|
disconnect: () => void;
|
||||||
onConnectEthereum?: () => void;
|
onConnectEthereum?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +59,8 @@ export const EthereumContext = createContext<EthereumContextState>({
|
||||||
accounts: [''],
|
accounts: [''],
|
||||||
chainId: 0,
|
chainId: 0,
|
||||||
connected: false,
|
connected: false,
|
||||||
|
select() {},
|
||||||
|
disconnect() {},
|
||||||
walletProvider: null,
|
walletProvider: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -142,12 +146,12 @@ export const EthereumProvider: FunctionComponent = ({ children }) => {
|
||||||
}, [setTokens]);
|
}, [setTokens]);
|
||||||
|
|
||||||
const onConnectEthereum = useCallback(() => {
|
const onConnectEthereum = useCallback(() => {
|
||||||
if (wallet && providerUrl) {
|
if (wallet && providerUrl && !connected) {
|
||||||
wallet.connect();
|
wallet.connect();
|
||||||
} else {
|
} else if (!connected) {
|
||||||
select();
|
select();
|
||||||
}
|
}
|
||||||
}, [wallet, providerUrl]);
|
}, [wallet, connected, providerUrl]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (wallet) {
|
if (wallet) {
|
||||||
|
@ -162,6 +166,11 @@ export const EthereumProvider: FunctionComponent = ({ children }) => {
|
||||||
});
|
});
|
||||||
wallet.on('disconnect', error => {
|
wallet.on('disconnect', error => {
|
||||||
setConnected(false);
|
setConnected(false);
|
||||||
|
setAccounts([]);
|
||||||
|
// @ts-ignore
|
||||||
|
setChainId(0);
|
||||||
|
// @ts-ignore
|
||||||
|
setProvider(null);
|
||||||
});
|
});
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
wallet.on('accountsChanged', accounts => {
|
wallet.on('accountsChanged', accounts => {
|
||||||
|
@ -184,10 +193,10 @@ export const EthereumProvider: FunctionComponent = ({ children }) => {
|
||||||
const close = useCallback(() => setIsModalVisible(false), []);
|
const close = useCallback(() => setIsModalVisible(false), []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (walletConnected && !connected) {
|
if (walletConnected) {
|
||||||
onConnectEthereum();
|
onConnectEthereum();
|
||||||
}
|
}
|
||||||
}, [walletConnected, connected, providerUrl]);
|
}, [walletConnected, providerUrl]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EthereumContext.Provider
|
<EthereumContext.Provider
|
||||||
|
@ -199,6 +208,8 @@ export const EthereumProvider: FunctionComponent = ({ children }) => {
|
||||||
connected,
|
connected,
|
||||||
chainId,
|
chainId,
|
||||||
walletProvider,
|
walletProvider,
|
||||||
|
select,
|
||||||
|
disconnect: () => wallet?.disconnect(),
|
||||||
onConnectEthereum: () => onConnectEthereum(),
|
onConnectEthereum: () => onConnectEthereum(),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -81,7 +81,7 @@ export const HomeView = () => {
|
||||||
style: {},
|
style: {},
|
||||||
},
|
},
|
||||||
children: (
|
children: (
|
||||||
<a href={record.explorer} target="_blank" rel="noopener noreferrer" >
|
<a href={record.explorer} target="_blank" rel="noopener noreferrer">
|
||||||
{shortenAddress(text, 6)}
|
{shortenAddress(text, 6)}
|
||||||
</a>
|
</a>
|
||||||
),
|
),
|
||||||
|
@ -98,7 +98,11 @@ export const HomeView = () => {
|
||||||
style: {},
|
style: {},
|
||||||
},
|
},
|
||||||
children: (
|
children: (
|
||||||
<a href={record.wrappedExplorer} target="_blank" rel="noopener noreferrer" >
|
<a
|
||||||
|
href={record.wrappedExplorer}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
{shortenAddress(text, 6)}
|
{shortenAddress(text, 6)}
|
||||||
</a>
|
</a>
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
import { PublicKey, Transaction } from '@solana/web3.js';
|
import { PublicKey, Transaction } from '@solana/web3.js';
|
||||||
import { notify } from '@oyster/common';
|
import { notify } from '@oyster/common';
|
||||||
import { WalletAdapter } from '@solana/wallet-base'
|
import { WalletAdapter } from '@solana/wallet-base';
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
|
|
||||||
export class MetamaskWalletAdapter
|
export class MetamaskWalletAdapter
|
||||||
|
@ -95,8 +95,9 @@ export class MetamaskWalletAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
if (this._publicKey) {
|
if (this._provider) {
|
||||||
this._publicKey = null;
|
this._publicKey = null;
|
||||||
|
this._provider = null;
|
||||||
this.emit('disconnect');
|
this.emit('disconnect');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
import { PublicKey, Transaction } from '@solana/web3.js';
|
import { PublicKey, Transaction } from '@solana/web3.js';
|
||||||
import { notify } from '@oyster/common';
|
import { notify } from '@oyster/common';
|
||||||
import { WalletAdapter } from '@solana/wallet-base'
|
import { WalletAdapter } from '@solana/wallet-base';
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import WalletConnectProvider from "@walletconnect/web3-provider";
|
import WalletConnectProvider from '@walletconnect/web3-provider';
|
||||||
|
|
||||||
export class WalletConnectWalletAdapter
|
export class WalletConnectWalletAdapter
|
||||||
extends EventEmitter
|
extends EventEmitter
|
||||||
|
@ -13,11 +13,13 @@ export class WalletConnectWalletAdapter
|
||||||
_accounts: Array<any>;
|
_accounts: Array<any>;
|
||||||
_chainID: number;
|
_chainID: number;
|
||||||
_provider: any;
|
_provider: any;
|
||||||
|
_walletProvider: any;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._publicKey = null;
|
this._publicKey = null;
|
||||||
this._provider = null;
|
this._provider = null;
|
||||||
|
this._walletProvider = null;
|
||||||
this._accounts = [];
|
this._accounts = [];
|
||||||
this._chainID = 0;
|
this._chainID = 0;
|
||||||
this._onProcess = false;
|
this._onProcess = false;
|
||||||
|
@ -53,44 +55,52 @@ export class WalletConnectWalletAdapter
|
||||||
|
|
||||||
// Create WalletConnect Provider
|
// Create WalletConnect Provider
|
||||||
const walletConnectProvider = new WalletConnectProvider({
|
const walletConnectProvider = new WalletConnectProvider({
|
||||||
infuraId: "535ab8649e9f40cface13cbded7d647e",
|
infuraId: '535ab8649e9f40cface13cbded7d647e',
|
||||||
});
|
});
|
||||||
walletConnectProvider.enable().then(()=>{
|
walletConnectProvider
|
||||||
const provider = new ethers.providers.Web3Provider(walletConnectProvider);
|
.enable()
|
||||||
const signer = provider.getSigner();
|
.then(() => {
|
||||||
signer.getAddress().then(account => {
|
const provider = new ethers.providers.Web3Provider(
|
||||||
this._accounts = [account];
|
walletConnectProvider,
|
||||||
provider.getNetwork().then(network => {
|
);
|
||||||
this._chainID = network.chainId;
|
const signer = provider.getSigner();
|
||||||
this._provider = provider;
|
signer.getAddress().then(account => {
|
||||||
this.emit('connect');
|
this._accounts = [account];
|
||||||
|
provider.getNetwork().then(network => {
|
||||||
|
this._chainID = network.chainId;
|
||||||
|
this._provider = provider;
|
||||||
|
this._walletProvider = walletConnectProvider;
|
||||||
|
this.emit('connect');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
// @ts-ignore
|
||||||
|
walletConnectProvider.on(
|
||||||
|
'disconnect',
|
||||||
|
(code: number, reason: string) => {
|
||||||
|
this.emit('disconnect', { code, reason });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// @ts-ignore
|
||||||
|
walletConnectProvider.on('accountsChanged', (accounts: string[]) => {
|
||||||
|
this.emit('accountsChanged', accounts);
|
||||||
|
});
|
||||||
|
// @ts-ignore
|
||||||
|
walletConnectProvider.on('chainChanged', (chainId: number) => {
|
||||||
|
this.emit('chainChanged', chainId);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.disconnect();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this._onProcess = false;
|
||||||
});
|
});
|
||||||
// @ts-ignore
|
|
||||||
walletConnectProvider.on('disconnect', (code: number, reason: string) => {
|
|
||||||
this.emit('disconnect', {code, reason});
|
|
||||||
});
|
|
||||||
// @ts-ignore
|
|
||||||
walletConnectProvider.on('accountsChanged', (accounts: string[]) => {
|
|
||||||
this.emit('accountsChanged', accounts);
|
|
||||||
});
|
|
||||||
// @ts-ignore
|
|
||||||
walletConnectProvider.on('chainChanged', (chainId: number) => {
|
|
||||||
this.emit('chainChanged', chainId);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.disconnect();
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this._onProcess = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
if (this._publicKey) {
|
if (this._provider) {
|
||||||
this._publicKey = null;
|
this._publicKey = null;
|
||||||
|
this._walletProvider.disconnect();
|
||||||
this.emit('disconnect');
|
this.emit('disconnect');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,9 +74,9 @@ export const PROGRAM_IDS = [
|
||||||
name: 'testnet',
|
name: 'testnet',
|
||||||
timelock: () => ({
|
timelock: () => ({
|
||||||
programAccountId: new PublicKey(
|
programAccountId: new PublicKey(
|
||||||
'32siiZ12jH2i7BM2wtNuTDw2Hs5nBjVQuBh4P8cTSH3i',
|
'7CxEuz8Qtius9aCyJqGnWZyBNvf6WTTNmA8G26BdMTSF',
|
||||||
),
|
),
|
||||||
programId: new PublicKey('5KrVJvesyjdMy6Vq5wfuPSMdw7vWuUvtbHG98wBsEkX6'),
|
programId: new PublicKey('8DevpkpN6CsdczP6rQ64CHraApXFrq96oGm4VjSNCs4q'),
|
||||||
}),
|
}),
|
||||||
wormhole: () => ({
|
wormhole: () => ({
|
||||||
pubkey: new PublicKey('5gQf5AUhAgWYgUCt9ouShm9H7dzzXUsLdssYwe5krKhg'),
|
pubkey: new PublicKey('5gQf5AUhAgWYgUCt9ouShm9H7dzzXUsLdssYwe5krKhg'),
|
||||||
|
@ -95,9 +95,9 @@ export const PROGRAM_IDS = [
|
||||||
name: 'devnet',
|
name: 'devnet',
|
||||||
timelock: () => ({
|
timelock: () => ({
|
||||||
programAccountId: new PublicKey(
|
programAccountId: new PublicKey(
|
||||||
'Gc6ktQPgHDxf9GpN6CdLEnkkqj5NfGYJeLDWFLoN3wNb',
|
'8KkpkoDAQaQqjnkCtNXAyk2A8GLmsmWPjBLK7jmahhxZ',
|
||||||
),
|
),
|
||||||
programId: new PublicKey('FmxAXMEKaj7BvgH9zdRNMZZYdAk4mBeRdSQwUoM3QYYw'),
|
programId: new PublicKey('7SH5hE7uBecnfMpGjdPyJupgBhFHaXcNMCEgJbmoVV7t'),
|
||||||
}),
|
}),
|
||||||
wormhole: () => ({
|
wormhole: () => ({
|
||||||
pubkey: new PublicKey('WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC'),
|
pubkey: new PublicKey('WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC'),
|
||||||
|
|
|
@ -89,6 +89,7 @@ export const addCustomSingleSignerTransaction = async (
|
||||||
proposal.pubkey,
|
proposal.pubkey,
|
||||||
sigAccount,
|
sigAccount,
|
||||||
proposal.info.signatoryValidation,
|
proposal.info.signatoryValidation,
|
||||||
|
proposal.info.config,
|
||||||
transferAuthority.publicKey,
|
transferAuthority.publicKey,
|
||||||
authority,
|
authority,
|
||||||
slot,
|
slot,
|
||||||
|
|
|
@ -70,7 +70,6 @@ export const createProposal = async (
|
||||||
space: TimelockSetLayout.span,
|
space: TimelockSetLayout.span,
|
||||||
programId: PROGRAM_IDS.timelock.programId,
|
programId: PROGRAM_IDS.timelock.programId,
|
||||||
});
|
});
|
||||||
|
|
||||||
signers.push(timelockSetKey);
|
signers.push(timelockSetKey);
|
||||||
instructions.push(uninitializedTimelockSetInstruction);
|
instructions.push(uninitializedTimelockSetInstruction);
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,7 @@ export const registerProgramGovernance = async (
|
||||||
TimelockType.CustomSingleSignerV1,
|
TimelockType.CustomSingleSignerV1,
|
||||||
uninitializedTimelockConfig.votingEntryRule || VotingEntryRule.Anytime,
|
uninitializedTimelockConfig.votingEntryRule || VotingEntryRule.Anytime,
|
||||||
uninitializedTimelockConfig.minimumSlotWaitingPeriod || new BN(0),
|
uninitializedTimelockConfig.minimumSlotWaitingPeriod || new BN(0),
|
||||||
|
uninitializedTimelockConfig.timeLimit || new BN(0),
|
||||||
uninitializedTimelockConfig.name || '',
|
uninitializedTimelockConfig.name || '',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { Card, Spin } from 'antd';
|
||||||
import Meta from 'antd/lib/card/Meta';
|
import Meta from 'antd/lib/card/Meta';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { execute } from '../../actions/execute';
|
import { execute } from '../../actions/execute';
|
||||||
|
import { LABELS } from '../../constants';
|
||||||
import {
|
import {
|
||||||
TimelockSet,
|
TimelockSet,
|
||||||
TimelockStateStatus,
|
TimelockStateStatus,
|
||||||
|
@ -47,7 +48,9 @@ export function InstructionCard({
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
<p>Instruction: TODO</p>
|
<p>Instruction: TODO</p>
|
||||||
<p>Slot: {instruction.info.slot.toNumber()}</p>
|
<p>
|
||||||
|
{LABELS.DELAY}: {instruction.info.slot.toNumber()}
|
||||||
|
</p>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -108,7 +111,8 @@ function PlayStatusButton({
|
||||||
};
|
};
|
||||||
|
|
||||||
if (proposal.info.state.status != TimelockStateStatus.Executing) return null;
|
if (proposal.info.state.status != TimelockStateStatus.Executing) return null;
|
||||||
if (currSlot < instruction.info.slot.toNumber()) return null;
|
const elapsedTime = currSlot - proposal.info.state.votingEndedAt.toNumber();
|
||||||
|
if (elapsedTime < instruction.info.slot.toNumber()) return null;
|
||||||
|
|
||||||
if (playing === Playstate.Unplayed)
|
if (playing === Playstate.Unplayed)
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Card, Progress, Spin } from 'antd';
|
import { Card, Progress, Spin } from 'antd';
|
||||||
import { Form, Input } from 'antd';
|
import { Form, Input } from 'antd';
|
||||||
import { INSTRUCTION_LIMIT, TimelockSet } from '../../models/timelock';
|
import {
|
||||||
|
INSTRUCTION_LIMIT,
|
||||||
|
TimelockConfig,
|
||||||
|
TimelockSet,
|
||||||
|
} from '../../models/timelock';
|
||||||
import { contexts, ParsedAccount, hooks, utils } from '@oyster/common';
|
import { contexts, ParsedAccount, hooks, utils } from '@oyster/common';
|
||||||
import { addCustomSingleSignerTransaction } from '../../actions/addCustomSingleSignerTransaction';
|
import { addCustomSingleSignerTransaction } from '../../actions/addCustomSingleSignerTransaction';
|
||||||
import { SaveOutlined } from '@ant-design/icons';
|
import { SaveOutlined } from '@ant-design/icons';
|
||||||
import { Connection, PublicKey } from '@solana/web3.js';
|
import { Connection, PublicKey } from '@solana/web3.js';
|
||||||
|
import { LABELS } from '../../constants';
|
||||||
|
|
||||||
const { useWallet } = contexts.Wallet;
|
const { useWallet } = contexts.Wallet;
|
||||||
const { useConnection } = contexts.Connection;
|
const { useConnection } = contexts.Connection;
|
||||||
|
@ -25,8 +30,10 @@ enum UploadType {
|
||||||
export function NewInstructionCard({
|
export function NewInstructionCard({
|
||||||
proposal,
|
proposal,
|
||||||
position,
|
position,
|
||||||
|
config,
|
||||||
}: {
|
}: {
|
||||||
proposal: ParsedAccount<TimelockSet>;
|
proposal: ParsedAccount<TimelockSet>;
|
||||||
|
config: ParsedAccount<TimelockConfig>;
|
||||||
position: number;
|
position: number;
|
||||||
}) {
|
}) {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
@ -41,7 +48,19 @@ export function NewInstructionCard({
|
||||||
}) => {
|
}) => {
|
||||||
if (!values.slot.match(/^\d*$/)) {
|
if (!values.slot.match(/^\d*$/)) {
|
||||||
notify({
|
notify({
|
||||||
message: 'Slot can only be numeric',
|
message: LABELS.SLOT_MUST_BE_NUMERIC,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
parseInt(values.slot) < config.info.minimumSlotWaitingPeriod.toNumber()
|
||||||
|
) {
|
||||||
|
notify({
|
||||||
|
message:
|
||||||
|
LABELS.SLOT_MUST_BE_GREATER_THAN +
|
||||||
|
config.info.minimumSlotWaitingPeriod.toString(),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -69,7 +88,11 @@ export function NewInstructionCard({
|
||||||
actions={[<SaveOutlined key="save" onClick={form.submit} />]}
|
actions={[<SaveOutlined key="save" onClick={form.submit} />]}
|
||||||
>
|
>
|
||||||
<Form {...layout} form={form} name="control-hooks" onFinish={onFinish}>
|
<Form {...layout} form={form} name="control-hooks" onFinish={onFinish}>
|
||||||
<Form.Item name="slot" label="Slot" rules={[{ required: true }]}>
|
<Form.Item
|
||||||
|
name="slot"
|
||||||
|
label={LABELS.DELAY}
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
>
|
||||||
<Input maxLength={64} />
|
<Input maxLength={64} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|
|
@ -92,4 +92,10 @@ export const LABELS = {
|
||||||
LEAVE_BLANK_IF_YOU_WANT_ONE: 'Leave blank if you want one made for you',
|
LEAVE_BLANK_IF_YOU_WANT_ONE: 'Leave blank if you want one made for you',
|
||||||
ADDITIONAL_VOTING_MSG:
|
ADDITIONAL_VOTING_MSG:
|
||||||
' Please note that during voting, you cannot withdraw tokens you have used to vote. You must wait for the vote to complete.',
|
' Please note that during voting, you cannot withdraw tokens you have used to vote. You must wait for the vote to complete.',
|
||||||
|
SLOT_MUST_BE_NUMERIC: 'Slot can only be numeric',
|
||||||
|
SLOT_MUST_BE_GREATER_THAN: 'Slot must be greater than or equal to ',
|
||||||
|
DELAY: 'Slot Delay',
|
||||||
|
MIN_SLOT_MUST_BE_NUMERIC: 'Minimum Slot Waiting Period can only be numeric',
|
||||||
|
TIME_LIMIT_MUST_BE_NUMERIC: 'Time Limit can only be numeric',
|
||||||
|
TIME_LIMIT: 'Voting Time Limit',
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,14 +19,17 @@ import BN from 'bn.js';
|
||||||
/// 1. `[writable]` Timelock set account.
|
/// 1. `[writable]` Timelock set account.
|
||||||
/// 2. `[writable]` Signatory account
|
/// 2. `[writable]` Signatory account
|
||||||
/// 3. `[writable]` Signatory validation account.
|
/// 3. `[writable]` Signatory validation account.
|
||||||
/// 4. `[]` Transfer authority
|
/// 4. `[]` Timelock Config account.
|
||||||
/// 5. `[]` Timelock program account.
|
/// 5. `[]` Transfer authority
|
||||||
/// 6. `[]` Token program account.
|
/// 6. `[]` Timelock mint authority
|
||||||
|
/// 7. `[]` Timelock program account.
|
||||||
|
/// 8. `[]` Token program account.
|
||||||
export const addCustomSingleSignerTransactionInstruction = (
|
export const addCustomSingleSignerTransactionInstruction = (
|
||||||
timelockTransactionAccount: PublicKey,
|
timelockTransactionAccount: PublicKey,
|
||||||
timelockSetAccount: PublicKey,
|
timelockSetAccount: PublicKey,
|
||||||
signatoryAccount: PublicKey,
|
signatoryAccount: PublicKey,
|
||||||
signatoryValidationAccount: PublicKey,
|
signatoryValidationAccount: PublicKey,
|
||||||
|
timelockConfigAccount: PublicKey,
|
||||||
transferAuthority: PublicKey,
|
transferAuthority: PublicKey,
|
||||||
authority: PublicKey,
|
authority: PublicKey,
|
||||||
slot: string,
|
slot: string,
|
||||||
|
@ -85,6 +88,7 @@ export const addCustomSingleSignerTransactionInstruction = (
|
||||||
{ pubkey: timelockSetAccount, isSigner: false, isWritable: true },
|
{ pubkey: timelockSetAccount, isSigner: false, isWritable: true },
|
||||||
{ pubkey: signatoryAccount, isSigner: false, isWritable: true },
|
{ pubkey: signatoryAccount, isSigner: false, isWritable: true },
|
||||||
{ pubkey: signatoryValidationAccount, isSigner: false, isWritable: true },
|
{ pubkey: signatoryValidationAccount, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: timelockConfigAccount, isSigner: false, isWritable: false },
|
||||||
{ pubkey: transferAuthority, isSigner: true, isWritable: false },
|
{ pubkey: transferAuthority, isSigner: true, isWritable: false },
|
||||||
{ pubkey: authority, isSigner: false, isWritable: false },
|
{ pubkey: authority, isSigner: false, isWritable: false },
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,6 +24,7 @@ export const initTimelockConfigInstruction = (
|
||||||
timelockType: number,
|
timelockType: number,
|
||||||
votingEntryRule: number,
|
votingEntryRule: number,
|
||||||
minimumSlotWaitingPeriod: BN,
|
minimumSlotWaitingPeriod: BN,
|
||||||
|
timeLimit: BN,
|
||||||
name: string,
|
name: string,
|
||||||
): TransactionInstruction => {
|
): TransactionInstruction => {
|
||||||
const PROGRAM_IDS = utils.programIds();
|
const PROGRAM_IDS = utils.programIds();
|
||||||
|
@ -39,6 +40,7 @@ export const initTimelockConfigInstruction = (
|
||||||
BufferLayout.u8('timelockType'),
|
BufferLayout.u8('timelockType'),
|
||||||
BufferLayout.u8('votingEntryRule'),
|
BufferLayout.u8('votingEntryRule'),
|
||||||
Layout.uint64('minimumSlotWaitingPeriod'),
|
Layout.uint64('minimumSlotWaitingPeriod'),
|
||||||
|
Layout.uint64('timeLimit'),
|
||||||
BufferLayout.seq(BufferLayout.u8(), CONFIG_NAME_LENGTH, 'name'),
|
BufferLayout.seq(BufferLayout.u8(), CONFIG_NAME_LENGTH, 'name'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -57,6 +59,7 @@ export const initTimelockConfigInstruction = (
|
||||||
timelockType,
|
timelockType,
|
||||||
votingEntryRule,
|
votingEntryRule,
|
||||||
minimumSlotWaitingPeriod,
|
minimumSlotWaitingPeriod,
|
||||||
|
timeLimit,
|
||||||
name: nameAsBytes,
|
name: nameAsBytes,
|
||||||
},
|
},
|
||||||
data,
|
data,
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { PublicKey, TransactionInstruction } from '@solana/web3.js';
|
import {
|
||||||
|
PublicKey,
|
||||||
|
SYSVAR_CLOCK_PUBKEY,
|
||||||
|
TransactionInstruction,
|
||||||
|
} from '@solana/web3.js';
|
||||||
import { utils } from '@oyster/common';
|
import { utils } from '@oyster/common';
|
||||||
import * as BufferLayout from 'buffer-layout';
|
import * as BufferLayout from 'buffer-layout';
|
||||||
import { TimelockInstruction } from './timelock';
|
import { TimelockInstruction } from './timelock';
|
||||||
|
@ -14,6 +18,7 @@ import { TimelockInstruction } from './timelock';
|
||||||
/// 4. `[]` Timelock mint authority
|
/// 4. `[]` Timelock mint authority
|
||||||
/// 5. `[]` Timelock program account pub key.
|
/// 5. `[]` Timelock program account pub key.
|
||||||
/// 6. `[]` Token program account.
|
/// 6. `[]` Token program account.
|
||||||
|
/// 7. `[]` Clock sysvar.
|
||||||
export const signInstruction = (
|
export const signInstruction = (
|
||||||
timelockSetAccount: PublicKey,
|
timelockSetAccount: PublicKey,
|
||||||
signatoryAccount: PublicKey,
|
signatoryAccount: PublicKey,
|
||||||
|
@ -46,6 +51,7 @@ export const signInstruction = (
|
||||||
isWritable: false,
|
isWritable: false,
|
||||||
},
|
},
|
||||||
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
|
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
||||||
];
|
];
|
||||||
return new TransactionInstruction({
|
return new TransactionInstruction({
|
||||||
keys,
|
keys,
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const DESC_SIZE = 200;
|
||||||
export const NAME_SIZE = 32;
|
export const NAME_SIZE = 32;
|
||||||
export const CONFIG_NAME_LENGTH = 32;
|
export const CONFIG_NAME_LENGTH = 32;
|
||||||
export const INSTRUCTION_LIMIT = 450;
|
export const INSTRUCTION_LIMIT = 450;
|
||||||
export const TRANSACTION_SLOTS = 5;
|
export const TRANSACTION_SLOTS = 4;
|
||||||
export const TEMP_FILE_TXN_SIZE = 1000;
|
export const TEMP_FILE_TXN_SIZE = 1000;
|
||||||
|
|
||||||
export enum TimelockInstruction {
|
export enum TimelockInstruction {
|
||||||
|
@ -43,6 +43,8 @@ export interface TimelockConfig {
|
||||||
governanceMint: PublicKey;
|
governanceMint: PublicKey;
|
||||||
/// Program ID that is tied to this config (optional)
|
/// Program ID that is tied to this config (optional)
|
||||||
program: PublicKey;
|
program: PublicKey;
|
||||||
|
/// Time limit in slots for proposal to be open to voting
|
||||||
|
timeLimit: BN;
|
||||||
/// Optional name
|
/// Optional name
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +59,7 @@ export const TimelockConfigLayout: typeof BufferLayout.Structure = BufferLayout.
|
||||||
Layout.uint64('minimumSlotWaitingPeriod'),
|
Layout.uint64('minimumSlotWaitingPeriod'),
|
||||||
Layout.publicKey('governanceMint'),
|
Layout.publicKey('governanceMint'),
|
||||||
Layout.publicKey('program'),
|
Layout.publicKey('program'),
|
||||||
|
Layout.uint64('timeLimit'),
|
||||||
BufferLayout.seq(BufferLayout.u8(), CONFIG_NAME_LENGTH, 'name'),
|
BufferLayout.seq(BufferLayout.u8(), CONFIG_NAME_LENGTH, 'name'),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -95,6 +98,9 @@ export enum TimelockStateStatus {
|
||||||
|
|
||||||
/// Deleted
|
/// Deleted
|
||||||
Deleted = 4,
|
Deleted = 4,
|
||||||
|
|
||||||
|
/// Defeated
|
||||||
|
Defeated = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const STATE_COLOR: Record<string, string> = {
|
export const STATE_COLOR: Record<string, string> = {
|
||||||
|
@ -103,6 +109,7 @@ export const STATE_COLOR: Record<string, string> = {
|
||||||
[TimelockStateStatus.Executing]: 'green',
|
[TimelockStateStatus.Executing]: 'green',
|
||||||
[TimelockStateStatus.Completed]: 'purple',
|
[TimelockStateStatus.Completed]: 'purple',
|
||||||
[TimelockStateStatus.Deleted]: 'gray',
|
[TimelockStateStatus.Deleted]: 'gray',
|
||||||
|
[TimelockStateStatus.Defeated]: 'red',
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface TimelockState {
|
export interface TimelockState {
|
||||||
|
@ -111,6 +118,10 @@ export interface TimelockState {
|
||||||
timelockTransactions: PublicKey[];
|
timelockTransactions: PublicKey[];
|
||||||
name: string;
|
name: string;
|
||||||
descLink: string;
|
descLink: string;
|
||||||
|
votingEndedAt: BN;
|
||||||
|
votingBeganAt: BN;
|
||||||
|
executions: number;
|
||||||
|
usedTxnSlots: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timelockTxns = [];
|
const timelockTxns = [];
|
||||||
|
@ -137,6 +148,10 @@ export const TimelockSetLayout: typeof BufferLayout.Structure = BufferLayout.str
|
||||||
Layout.uint64('totalSigningTokensMinted'),
|
Layout.uint64('totalSigningTokensMinted'),
|
||||||
BufferLayout.seq(BufferLayout.u8(), DESC_SIZE, 'descLink'),
|
BufferLayout.seq(BufferLayout.u8(), DESC_SIZE, 'descLink'),
|
||||||
BufferLayout.seq(BufferLayout.u8(), NAME_SIZE, 'name'),
|
BufferLayout.seq(BufferLayout.u8(), NAME_SIZE, 'name'),
|
||||||
|
Layout.uint64('votingEndedAt'),
|
||||||
|
Layout.uint64('votingBeganAt'),
|
||||||
|
BufferLayout.u8('executions'),
|
||||||
|
BufferLayout.u8('usedTxnSlots'),
|
||||||
...timelockTxns,
|
...timelockTxns,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -248,6 +263,10 @@ export const TimelockSetParser = (
|
||||||
descLink: utils.fromUTF8Array(data.descLink).replaceAll('\u0000', ''),
|
descLink: utils.fromUTF8Array(data.descLink).replaceAll('\u0000', ''),
|
||||||
name: utils.fromUTF8Array(data.name).replaceAll('\u0000', ''),
|
name: utils.fromUTF8Array(data.name).replaceAll('\u0000', ''),
|
||||||
timelockTransactions: timelockTxns,
|
timelockTransactions: timelockTxns,
|
||||||
|
votingEndedAt: data.votingEndedAt,
|
||||||
|
votingBeganAt: data.votingBeganAt,
|
||||||
|
executions: data.executions,
|
||||||
|
usedTxnSlots: data.usedTxnSlots,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -298,9 +317,10 @@ export const TimelockConfigParser = (
|
||||||
executionType: data.executionType,
|
executionType: data.executionType,
|
||||||
timelockType: data.timelockType,
|
timelockType: data.timelockType,
|
||||||
votingEntryRule: data.votingEntryRule,
|
votingEntryRule: data.votingEntryRule,
|
||||||
minimimSlotWaitingPeriod: data.minimimSlotWaitingPeriod,
|
minimumSlotWaitingPeriod: data.minimumSlotWaitingPeriod,
|
||||||
governanceMint: data.governanceMint,
|
governanceMint: data.governanceMint,
|
||||||
program: data.program,
|
program: data.program,
|
||||||
|
timeLimit: data.timeLimit,
|
||||||
name: utils.fromUTF8Array(data.name).replaceAll('\u0000', ''),
|
name: utils.fromUTF8Array(data.name).replaceAll('\u0000', ''),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { PublicKey, TransactionInstruction } from '@solana/web3.js';
|
import {
|
||||||
|
PublicKey,
|
||||||
|
SYSVAR_CLOCK_PUBKEY,
|
||||||
|
TransactionInstruction,
|
||||||
|
} from '@solana/web3.js';
|
||||||
import { utils } from '@oyster/common';
|
import { utils } from '@oyster/common';
|
||||||
import * as Layout from '../utils/layout';
|
import * as Layout from '../utils/layout';
|
||||||
|
|
||||||
|
@ -23,6 +27,7 @@ import BN from 'bn.js';
|
||||||
/// 10. `[]` Timelock program mint authority
|
/// 10. `[]` Timelock program mint authority
|
||||||
/// 11. `[]` Timelock program account pub key.
|
/// 11. `[]` Timelock program account pub key.
|
||||||
/// 12. `[]` Token program account.
|
/// 12. `[]` Token program account.
|
||||||
|
/// 13. `[]` Clock sysvar.
|
||||||
export const voteInstruction = (
|
export const voteInstruction = (
|
||||||
timelockSetAccount: PublicKey,
|
timelockSetAccount: PublicKey,
|
||||||
votingAccount: PublicKey,
|
votingAccount: PublicKey,
|
||||||
|
@ -75,6 +80,7 @@ export const voteInstruction = (
|
||||||
isWritable: false,
|
isWritable: false,
|
||||||
},
|
},
|
||||||
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
|
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
return new TransactionInstruction({
|
return new TransactionInstruction({
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
import { PublicKey } from '@solana/web3.js';
|
import { PublicKey } from '@solana/web3.js';
|
||||||
import { Table } from 'antd';
|
import { Table } from 'antd';
|
||||||
import MintGovernanceTokens from '../../components/Proposal/MintGovernanceTokens';
|
import MintGovernanceTokens from '../../components/Proposal/MintGovernanceTokens';
|
||||||
|
import BN from 'bn.js';
|
||||||
const { useUserAccounts } = hooks;
|
const { useUserAccounts } = hooks;
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
|
@ -47,14 +48,21 @@ const columns = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: LABELS.VOTING_ENTRY_RULES,
|
title: LABELS.VOTING_ENTRY_RULES,
|
||||||
dataIndex: 'votingEntryRules',
|
dataIndex: 'votingEntryRule',
|
||||||
key: 'votingEntryRules',
|
key: 'votingEntryRule',
|
||||||
render: (number: number) => <span>{VotingEntryRule[number]}</span>,
|
render: (number: number) => <span>{VotingEntryRule[number]}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: LABELS.MINIMUM_SLOT_WAITING_PERIOD,
|
title: LABELS.MINIMUM_SLOT_WAITING_PERIOD,
|
||||||
dataIndex: 'minimumSlotWaitingPeriod',
|
dataIndex: 'minimumSlotWaitingPeriod',
|
||||||
key: 'minimumSlotWaitingPeriod',
|
key: 'minimumSlotWaitingPeriod',
|
||||||
|
render: (number: BN) => <span>{number.toNumber()}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: LABELS.TIME_LIMIT,
|
||||||
|
dataIndex: 'timeLimit',
|
||||||
|
key: 'timeLimit',
|
||||||
|
render: (number: BN) => <span>{number.toNumber()}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: LABELS.GOVERNANCE_MINT,
|
title: LABELS.GOVERNANCE_MINT,
|
||||||
|
|
|
@ -79,18 +79,25 @@ export function NewForm({
|
||||||
consensusAlgorithm: ConsensusAlgorithm;
|
consensusAlgorithm: ConsensusAlgorithm;
|
||||||
votingEntryRule: VotingEntryRule;
|
votingEntryRule: VotingEntryRule;
|
||||||
minimumSlotWaitingPeriod: string;
|
minimumSlotWaitingPeriod: string;
|
||||||
|
timeLimit: string;
|
||||||
governanceMint: string;
|
governanceMint: string;
|
||||||
program: string;
|
program: string;
|
||||||
name: string;
|
name: string;
|
||||||
}) => {
|
}) => {
|
||||||
if (!values.minimumSlotWaitingPeriod.match(/^\d*$/)) {
|
if (!values.minimumSlotWaitingPeriod.match(/^\d*$/)) {
|
||||||
notify({
|
notify({
|
||||||
message: 'Minimum Slot Waiting Period can only be numeric',
|
message: LABELS.MIN_SLOT_MUST_BE_NUMERIC,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!values.timeLimit.match(/^\d*$/)) {
|
||||||
|
notify({
|
||||||
|
message: LABELS.TIME_LIMIT_MUST_BE_NUMERIC,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uninitializedConfig = {
|
const uninitializedConfig = {
|
||||||
timelockType: values.timelockType,
|
timelockType: values.timelockType,
|
||||||
executionType: values.executionType,
|
executionType: values.executionType,
|
||||||
|
@ -102,6 +109,7 @@ export function NewForm({
|
||||||
: undefined,
|
: undefined,
|
||||||
program: new PublicKey(values.program),
|
program: new PublicKey(values.program),
|
||||||
name: values.name,
|
name: values.name,
|
||||||
|
timeLimit: new BN(values.timeLimit),
|
||||||
};
|
};
|
||||||
|
|
||||||
const newConfig = await registerProgramGovernance(
|
const newConfig = await registerProgramGovernance(
|
||||||
|
@ -143,6 +151,13 @@ export function NewForm({
|
||||||
>
|
>
|
||||||
<Input maxLength={64} />
|
<Input maxLength={64} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="timeLimit"
|
||||||
|
label={LABELS.TIME_LIMIT}
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
>
|
||||||
|
<Input maxLength={64} />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="consensusAlgorithm"
|
name="consensusAlgorithm"
|
||||||
label={LABELS.CONSENSUS_ALGORITHM}
|
label={LABELS.CONSENSUS_ALGORITHM}
|
||||||
|
|
|
@ -257,6 +257,7 @@ function InnerProposalView({
|
||||||
<Col xs={24} sm={24} md={12} lg={8}>
|
<Col xs={24} sm={24} md={12} lg={8}>
|
||||||
<NewInstructionCard
|
<NewInstructionCard
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
|
config={timelockConfig}
|
||||||
position={instructionsForProposal.length}
|
position={instructionsForProposal.length}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
Loading…
Reference in New Issue