Skip to content

Commit

Permalink
Merge pull request #254 from sunrise-stake/develop
Browse files Browse the repository at this point in the history
0.7.2 Maintenance
  • Loading branch information
dankelleher authored May 11, 2023
2 parents e452020 + 97f0db2 commit b5b4dbd
Show file tree
Hide file tree
Showing 50 changed files with 1,261 additions and 284 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: install node_modules
run: |
export PATH="/home/runner/.local/share/solana/install/active_release/bin:$PATH"
yarn --frozen-lockfile
yarn --frozen-lockfile --network-concurrency 2
- uses: dtolnay/rust-toolchain@stable
with:
Expand Down Expand Up @@ -140,4 +140,4 @@ jobs:
if: always()
with:
name: program-logs
path: .anchor/program-logs/*
path: .anchor/program-logs/*
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
"program:dylint": "cargo dylint --all --workspace",
"lint": "yarn workspaces run lint",
"test": "anchor test",
"test:anchor": "yarn mocha -p ./tsconfig.json -t 1000000 packages/tests/**/*.ts",
"test:anchor": "yarn mocha -p ./tsconfig.json --no-parallel -t 1000000 packages/tests/**/*.ts",
"program:build": "anchor build",
"program:deploy": "solana program deploy target/deploy/sunrise_stake.so --program-id sunzv8N3A8dRHwUBvxgRDEbWKk8t7yiHR4FLRgFsTX6",
"build": "yarn workspaces run build",
"app:build": "yarn workspace @sunrisestake/app build",
"app:start": "yarn workspace @sunrisestake/app start",
"report:mainnet": "cross-env REACT_APP_SOLANA_NETWORK=mainnet-beta yarn ts-node packages/scripts/get.ts",
"report:devnet": "cross-env REACT_APP_SOLANA_NETWORK=devnet yarn ts-node packages/scripts/get.ts"
"report:devnet": "cross-env REACT_APP_SOLANA_NETWORK=devnet yarn ts-node packages/scripts/get.ts",
"rebalance:mainnet": "cross-env REACT_APP_SOLANA_NETWORK=mainnet-beta yarn ts-node packages/scripts/rebalance.ts"
},
"dependencies": {
"@coral-xyz/anchor": "^0.26.0",
Expand All @@ -43,6 +44,7 @@
"lerna": "^6.4.1",
"mocha": "^10.2.0",
"prettier": "^2.7.1",
"ts-node": "^10.9.1",
"tsx": "^3.12.3",
"typescript": "*"
},
Expand Down
Binary file added packages/app/public/partners/bloom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed packages/app/public/partners/charity0.png
Binary file not shown.
Binary file removed packages/app/public/partners/charity1.png
Binary file not shown.
Binary file removed packages/app/public/partners/charity2.png
Binary file not shown.
Binary file removed packages/app/public/partners/charity3.png
Binary file not shown.
5 changes: 2 additions & 3 deletions packages/app/src/api/forest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
getTotals,
mintsToSelf,
mintsWithRecipientsAsTransfers,
prune,
} from "./util";
import { type SunriseClientWrapper } from "../common/sunriseClientWrapper";

Expand Down Expand Up @@ -189,9 +188,9 @@ export class ForestService {
treeNode
);

return prune({
return {
tree: treeNode,
neighbours,
});
};
}
}
33 changes: 16 additions & 17 deletions packages/app/src/api/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Given a set of transfers, return the first transfer for each sender-recipient pair
import { type Forest, type Mint, type Totals, type Transfer } from "./types";
import { addUp, memoise, round } from "../common/utils";
import { type Mint, type Totals, type Transfer, type TreeNode } from "./types";
import { addUp, memoise, mostRecent, round } from "../common/utils";
import { type Connection, PublicKey } from "@solana/web3.js";
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
Expand Down Expand Up @@ -53,23 +52,23 @@ export const mintsToSelf = (mints: Mint[]): Mint[] =>
(mint) => mint.sender === undefined || mint.sender.equals(mint.recipient)
);

export const prune = (forest: Forest): Forest => {
const seen: string[] = [];
// if a tree node is in the seen array, it's a duplicate, remove it.
export const isDeadTree = (tree: TreeNode): boolean =>
tree.totals.currentBalance === 0;

const pruneTree = (forest: Forest): Forest | null => {
if (seen.includes(forest.tree.address.toBase58())) return null;
export const getMostRecentActivity = (tree: TreeNode): Date => {
const mostRecentMint = mostRecent(tree.mints);
const mostRecentSend = mostRecent(tree.sent);
const mostRecentReceipt = mostRecent(tree.received);

seen.push(forest.tree.address.toBase58());
const prunedNeighbours = forest.neighbours
.map(pruneTree)
.filter((n): n is Forest => n !== null);
return { ...forest, neighbours: prunedNeighbours };
};
const timestamps: number[] = [
mostRecentMint,
mostRecentSend,
mostRecentReceipt,
]
.filter((x) => x !== undefined)
.map((x) => x?.timestamp.getTime() as number);

// the cast here is ok, because the seen array starts empty
// so the root node will never be pruned
return pruneTree(forest) as Forest;
return new Date(Math.max(...timestamps));
};

export const getTotals = (
Expand Down
114 changes: 78 additions & 36 deletions packages/app/src/common/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,97 @@
import clx from "classnames";
import React, { type ReactNode } from "react";
import React, { type MouseEventHandler, type ReactNode } from "react";
import { type ModalControl } from "../hooks";
import { InfoModal } from "./modals/InfoModal";
import { useInfoModal } from "../hooks/useInfoModal";

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
color?: "primary" | "secondary" | "danger" | "ticket" | "white";
variant?: "solid" | "outline";
size?: "sm" | "md" | "lg";
children?: ReactNode;
className?: string;
infoDisabled?: boolean;
disabledTitle?: string;
disabledMessage?: string;
}

// Execute the onClick only if the condition is true, otherwise show the modal
const orShowModal =
(
condition: boolean,
executeIfTrue: MouseEventHandler,
modalIfFalse: ModalControl
): MouseEventHandler =>
(event) => {
if (condition) {
executeIfTrue(event);
} else {
modalIfFalse.trigger();
}
};

const Button: React.FC<ButtonProps> = ({
children,
className,
color = "primary",
size = "md",
variant = "solid",
infoDisabled = false,
disabledTitle = "",
disabledMessage = "",
onClick,
...rest
}) => (
<button
className={clx(
"inline-flex items-center border-2 rounded-lg leading-6 shadow-sm disabled:brightness-75 hover:brightness-125",
{
"border-green hover:border-green-light": color === "primary",
"border-danger": color === "danger",
"border-outset": color === "secondary",
"border-ticket": color === "ticket",
"border-white": color === "white",
"bg-green hover:bg-green-light":
color === "primary" && variant === "solid",
"bg-danger": color === "danger" && variant === "solid",
"bg-outset": color === "secondary" && variant === "solid",
"bg-ticket": color === "ticket" && variant === "solid",
"bg-white": color === "white" && variant === "solid",
"bg-transparent": variant === "outline",
"text-green hover:text-green-light":
color === "primary" && variant === "outline",
"text-danger": color === "danger" && variant === "outline",
"text-outset": color === "secondary" && variant === "outline",
"text-ticket": color === "ticket" && variant === "outline",
"text-white": variant === "solid" && color !== "white",
"text-green": color === "white",
"px-8 py-4 text-2xl": size === "lg",
"px-8 py-4 text-xl": size === "md",
"px-5 py-3 text-xl": size === "sm",
},
className
)}
{...rest}
>
{children}
</button>
);
}) => {
const infoModal = useInfoModal();
const wrappedOnClick =
onClick !== undefined
? orShowModal(!infoDisabled, onClick, infoModal)
: undefined;
return (
<button
onClick={wrappedOnClick}
className={clx(
"inline-flex items-center border-2 rounded-lg leading-6 shadow-sm disabled:brightness-75",
infoDisabled ? "brightness-75" : "hover:brightness-125",
{
"border-green": color === "primary",
"hover:border-green-light": color === "primary" && !infoDisabled,
"border-danger": color === "danger",
"border-outset": color === "secondary",
"border-ticket": color === "ticket",
"border-white": color === "white",
"bg-green": color === "primary" && variant === "solid",
"hover:bg-green-light":
color === "primary" && variant === "solid" && !infoDisabled,
"bg-danger": color === "danger" && variant === "solid",
"bg-outset": color === "secondary" && variant === "solid",
"bg-ticket": color === "ticket" && variant === "solid",
"bg-white": color === "white" && variant === "solid",
"bg-transparent": variant === "outline",
"text-green hover:text-green-light":
color === "primary" && variant === "outline",
"text-danger": color === "danger" && variant === "outline",
"text-outset": color === "secondary" && variant === "outline",
"text-ticket": color === "ticket" && variant === "outline",
"text-white": variant === "solid" && color !== "white",
"text-green": color === "white",
"px-8 py-4 text-2xl": size === "lg",
"px-8 py-4 text-xl": size === "md",
"px-5 py-3 text-xl": size === "sm",
},
className
)}
{...rest}
>
<InfoModal
title={disabledTitle}
message={disabledMessage}
ok={infoModal.onModalOK}
show={infoModal.modalShown}
/>
{children}
</button>
);
};

export { Button };
20 changes: 14 additions & 6 deletions packages/app/src/common/components/LockForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { FiArrowDownLeft } from "react-icons/fi";
import { useSunriseStake } from "../context/sunriseStakeContext";
import { ZERO } from "../utils";
import { AmountInput, Button, Panel, Spinner } from "./";
import { useModal } from "../hooks";
import { LockWarningModal } from "./modals/LockWarningModal";

interface LockFormProps {
lock: (amount: string) => Promise<any>;
Expand All @@ -14,9 +16,20 @@ const LockForm: React.FC<LockFormProps> = ({ lock }) => {
const [amount, setAmount] = useState("");
const [valid, setValid] = useState(false);
const [isBusy, setIsBusy] = useState(false);
const lockModal = useModal(() => {
setIsBusy(true);
lock(amount).finally(() => {
setIsBusy(false);
});
});

return (
<Panel className="flex flex-row mx-auto mb-9 p-3 sm:p-4 rounded-lg w-full sm:w-[80%] md:w-[60%] lg:w-[40%] max-w-xl">
<LockWarningModal
ok={lockModal.onModalOK}
cancel={lockModal.onModalClose}
show={lockModal.modalShown}
/>
<AmountInput
className="mr-4 basis-3/4"
token="gSOL"
Expand All @@ -28,12 +41,7 @@ const LockForm: React.FC<LockFormProps> = ({ lock }) => {
variant="small"
/>
<Button
onClick={() => {
setIsBusy(true);
lock(amount).finally(() => {
setIsBusy(false);
});
}}
onClick={lockModal.trigger}
disabled={!valid || isBusy}
className="mr-auto sm:mr-0 m-auto"
size="sm"
Expand Down
20 changes: 12 additions & 8 deletions packages/app/src/common/components/modals/BaseModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Props = ModalProps & {
children?: ReactNode;
okEnabled?: boolean;
showActions?: boolean;
cancelVisible?: boolean;
};

const BaseModal: FC<Props> = ({
Expand All @@ -24,6 +25,7 @@ const BaseModal: FC<Props> = ({
cancel,
okEnabled = true,
showActions = true,
cancelVisible = true,
show,
}) => {
const clickOk = (): void => {
Expand Down Expand Up @@ -73,14 +75,16 @@ const BaseModal: FC<Props> = ({
<div className="font-bold">Continue</div>{" "}
<FiArrowRight className="ml-2 scale-150" />
</Button>
<Button
color="secondary"
className="mt-3 items-center w-full justify-center hover:opacity-70 sm:col-start-1 sm:mt-0 sm:text-sm"
onClick={clickCancel}
>
<div className="font-bold">Cancel</div>{" "}
<GiCancel className="ml-2" />
</Button>
{cancelVisible && (
<Button
color="secondary"
className="mt-3 items-center w-full justify-center hover:opacity-70 sm:col-start-1 sm:mt-0 sm:text-sm"
onClick={clickCancel}
>
<div className="font-bold">Cancel</div>{" "}
<GiCancel className="ml-2" />
</Button>
)}
</div>
)}
</Dialog.Panel>
Expand Down
39 changes: 39 additions & 0 deletions packages/app/src/common/components/modals/InfoModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Dialog } from "@headlessui/react";
import { ExclamationTriangleIcon } from "@heroicons/react/24/solid";
import React, { type FC, type PropsWithChildren } from "react";

import { BaseModal, type ModalProps } from "./";

type InfoModalProps = { title: string; message?: string } & PropsWithChildren &
Omit<ModalProps, "cancel">;
const InfoModal: FC<InfoModalProps> = (props) => {
return (
<BaseModal cancelVisible={false} {...props} cancel={() => {}}>
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full">
<ExclamationTriangleIcon
className="w-8"
aria-hidden="true"
color="#f9c23c"
/>
</div>
<div className="text-center">
<Dialog.Title
as="h3"
className="text-md font-bold leading-6 text-[#f9c23c] text-center"
>
{props.title}
</Dialog.Title>
<div className="mt-2">
<div className="flex flex-col gap-1 py-6 px-2">
{props.message !== undefined && (
<p className="text-md">{props.message}</p>
)}
{props.children}
</div>
</div>
</div>
</BaseModal>
);
};

export { InfoModal };
Loading

1 comment on commit b5b4dbd

@vercel
Copy link

@vercel vercel bot commented on b5b4dbd May 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

app – ./

app.sunrisestake.com
app-git-main-sunrise-stake.vercel.app
app-sunrise-stake.vercel.app

Please sign in to comment.