Skip to content

Commit

Permalink
feat: hybrid custody (#7)
Browse files Browse the repository at this point in the history
* feat: setup owned account

* feat: parent view

* feat: remove parent from child

* feat[WIP]: hc manager

* feat[WIP]: redeem account & show childAccounts

* feat: setup childAccount display

* feat: update ParentView and ChildView UI

* feat: remove child

* feat: transfer ownership / accept ownership / show owner of ownedAccount

* feat: transfer ownership from manager

* refactor: owned account page name

* feat: OwnedAccount Display

* feat: OwnedAccounts Display

* fix: Display style

* chore: clean logs
  • Loading branch information
LanfordCai authored Aug 21, 2023
1 parent b9a64ed commit ec7d5f3
Show file tree
Hide file tree
Showing 34 changed files with 2,309 additions and 12 deletions.
4 changes: 3 additions & 1 deletion .env.development
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
NEXT_PUBLIC_CHAIN_ENV=mainnet

NEXT_PUBLIC_APP_URL=https://flowview.vercel.app/
NEXT_PUBLIC_APP_URL=http://localhost:3000/
NEXT_PUBLIC_ACCESS_NODE_API=https://floral-special-valley.flow-mainnet.quiknode.pro
NEXT_PUBLIC_WALLET_DISCOVERY=https://fcl-discovery.onflow.org/authn

Expand All @@ -23,3 +23,5 @@ NEXT_PUBLIC_FUNGIBLE_TOKEN_SWITCHBOARD_ADDRESS=0xf233dcee88fe0abe
NEXT_PUBLIC_FLOWBOX_ADDRESS=0x1b3930856571a52b
NEXT_PUBLIC_ACCOUNTBOOKMARK_ADDRESS=0x39b144ab4d348e2b
NEXT_PUBLIC_NFTSTOREFRONTV2_ADDRESS=0x4eb8a10cb9f87357

NEXT_PUBLIC_HYBRIDCUSTODY_ADDRESS=0xd8a7e05a7ac670c0
3 changes: 2 additions & 1 deletion .env.production
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
NEXT_PUBLIC_CHAIN_ENV=mainnet

NEXT_PUBLIC_APP_URL=https://flowview.vercel.app/
NEXT_PUBLIC_APP_URL=https://flowview.app/
NEXT_PUBLIC_ACCESS_NODE_API=https://floral-special-valley.flow-mainnet.quiknode.pro
NEXT_PUBLIC_WALLET_DISCOVERY=https://fcl-discovery.onflow.org/authn

Expand All @@ -23,3 +23,4 @@ NEXT_PUBLIC_FUNGIBLE_TOKEN_SWITCHBOARD_ADDRESS=0xf233dcee88fe0abe
NEXT_PUBLIC_FLOWBOX_ADDRESS=0x1b3930856571a52b
NEXT_PUBLIC_ACCOUNTBOOKMARK_ADDRESS=0x39b144ab4d348e2b
NEXT_PUBLIC_NFTSTOREFRONTV2_ADDRESS=0x4eb8a10cb9f87357
NEXT_PUBLIC_HYBRIDCUSTODY_ADDRESS=0xd8a7e05a7ac670c0
30 changes: 21 additions & 9 deletions components/common/Siderbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ export default function Sidebar(props) {
{ id: "5", label: `Storefront`, link: { pathname: "/account/[account]/storefront", query: { account: account } } },
{ id: "6", label: `Contract`, link: { pathname: "/account/[account]/contract", query: { account: account } } },
{
id: "7", label: "Storage", subItems: [
{ id: "7-0", isSubItem: true, label: "Public Items", smLabel: "Public", link: { pathname: "/account/[account]/public", query: { account: account } } },
{ id: "7-1", isSubItem: true, label: "Stored Items", smLabel: "Stored", link: { pathname: "/account/[account]/storage", query: { account: account } } },
{ id: "7-2", isSubItem: true, label: "Private Items", smLabel: "Private", link: { pathname: "/account/[account]/private", query: { account: account } } },
id: "7", label: `Acct Linking`, subItems: [
{ id: "7-0", isSubItem: true, label: "Owned Acct", smLabel: "Owned Acct", link: { pathname: "/account/[account]/hc/owned_account", query: { account: account } } },
{ id: "7-1", isSubItem: true, label: "HC Manager", smLabel: "HC Manager", link: { pathname: "/account/[account]/hc/manager", query: { account: account } } },
]
},
{
id: "8", label: "Storage", subItems: [
{ id: "8-0", isSubItem: true, label: "Public Items", smLabel: "Public", link: { pathname: "/account/[account]/public", query: { account: account } } },
{ id: "8-1", isSubItem: true, label: "Stored Items", smLabel: "Stored", link: { pathname: "/account/[account]/storage", query: { account: account } } },
{ id: "8-2", isSubItem: true, label: "Private Items", smLabel: "Private", link: { pathname: "/account/[account]/private", query: { account: account } } },
]
}
// { id: "5", label: `Analyzer`, link: { pathname: "/account/[account]/analyzer", query: { account: account } } },
Expand All @@ -33,10 +39,16 @@ export default function Sidebar(props) {
{ id: "4", label: `Collection`, link: { pathname: "/account/[account]/collection", query: { account: account } } },
{ id: "5", label: `Contract`, link: { pathname: "/account/[account]/contract", query: { account: account } } },
{
id: "6", label: "Storage", subItems: [
{ id: "6-0", isSubItem: true, label: "Public Items", smLabel: "Public", link: { pathname: "/account/[account]/public", query: { account: account } } },
{ id: "6-1", isSubItem: true, label: "Stored Items", smLabel: "Stored", link: { pathname: "/account/[account]/storage", query: { account: account } } },
{ id: "6-2", isSubItem: true, label: "Private Items", smLabel: "Private", link: { pathname: "/account/[account]/private", query: { account: account } } },
id: "6", label: `Acct Linking`, subItems: [
{ id: "6-0", isSubItem: true, label: "Owned Acct", smLabel: "Owned Acct", link: { pathname: "/account/[account]/hc/owned_account", query: { account: account } } },
{ id: "6-1", isSubItem: true, label: "HC Manager", smLabel: "HC Manager", link: { pathname: "/account/[account]/hc/manager", query: { account: account } } },
]
},
{
id: "7", label: "Storage", subItems: [
{ id: "7-0", isSubItem: true, label: "Public Items", smLabel: "Public", link: { pathname: "/account/[account]/public", query: { account: account } } },
{ id: "7-1", isSubItem: true, label: "Stored Items", smLabel: "Stored", link: { pathname: "/account/[account]/storage", query: { account: account } } },
{ id: "7-2", isSubItem: true, label: "Private Items", smLabel: "Private", link: { pathname: "/account/[account]/private", query: { account: account } } },
]
}
// { id: "5", label: `Analyzer`, link: { pathname: "/account/[account]/analyzer", query: { account: account } } },
Expand Down Expand Up @@ -78,7 +90,7 @@ export default function Sidebar(props) {
<div
className="flex flex-col p-3 rounded-xl"
>
<div className="flex flex-col gap-y-4 items-start">
<div className="flex flex-col gap-y-3 items-start">
{menuItems.map(({ label: label, ...menu }, index) => {
const classes = getNavItemClasses(menu)
return (
Expand Down
71 changes: 71 additions & 0 deletions components/hybrid_custody/ChildView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useRouter } from "next/router";
import publicConfig from "../../publicConfig";
import { useRecoilState } from "recoil";
import { showSetupDisplayState, transactionInProgressState, transactionStatusState } from "../../lib/atoms";
import { removeChildAccount, removeChildFromChild, setupChildAccountDisplay } from "../../flow/hc_transactions";
import { useSWRConfig } from "swr";
import OwnedDisplayView from "./OwnedDisplayView";

export default function ChildView(props) {
const [transactionInProgress, setTransactionInProgress] = useRecoilState(transactionInProgressState)
const [, setTransactionStatus] = useRecoilState(transactionStatusState)
const [showSetupDisplay, setShowSetupDisplay] = useRecoilState(showSetupDisplayState)
const { mutate } = useSWRConfig()

const router = useRouter()
const { child, account, user } = props

return (
<div className="min-w-[1076px] flex flex-col gap-y-3 p-4 shadow-md rounded-2xl bg-white">
<div className="flex flex-col gap-y-2">
<div className="flex gap-x-2 justify-between items-center">
<div className="cursor-pointer px-2 text-xl font-bold text-black decoration-drizzle decoration-2 underline">
<a href={`${publicConfig.appURL}//account/${child.address}`}
target="_blank"
rel="noopener noreferrer">
{child.address}
</a>
</div>
<div className="flex gap-x-2 justify-between">
{/* <button
type="button"
disabled={transactionInProgress}
className={`text-black disabled:bg-drizzle-light disabled:text-gray-500 bg-drizzle hover:bg-drizzle-dark px-3 py-2 text-sm rounded-2xl font-semibold shrink-0`}
onClick={async () => {
// TODO: REMOVE CHILD
mutate(["hcManagerInfoFetcher", account])
}}
>
{"Set Manager Cap Filter"}
</button> */}
<button
type="button"
disabled={transactionInProgress || !(user && user.loggedIn && user.addr == account)}
className={`text-black disabled:bg-drizzle-light disabled:text-gray-500 bg-drizzle hover:bg-drizzle-dark px-3 py-2 text-sm rounded-2xl font-semibold shrink-0`}
onClick={async () => {
setShowSetupDisplay({ show: true, mode: "ChildAccount", childAddress: child.address })
}}
>
{"Set Display"}
</button>
<button
type="button"
disabled={transactionInProgress || !(user && user.loggedIn && user.addr == account)}
className={`text-white disabled:bg-red-400 disabled:text-white bg-red-600 hover:bg-red-800 px-3 py-2 text-sm rounded-2xl font-semibold shrink-0`}
onClick={async () => {
await removeChildAccount(child.address, setTransactionInProgress, setTransactionStatus)
mutate(["hcManagerInfoFetcher", account])
}}
>
{"Remove"}
</button>
</div>
</div>
{
child.display ?
<OwnedDisplayView display={child.display} style={"Small"} type="Child" /> : null
}
</div>
</div>
)
}
45 changes: 45 additions & 0 deletions components/hybrid_custody/OwnedDisplayView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Image from "next/image"
import publicConfig from "../../publicConfig"
import { getImageSrcFromMetadataViewsFile } from "../../lib/utils"

export default function OwnedDisplayView(props) {
const { display, style } = props

if (style == "Small") {
return (
<div className="flex gap-x-3 justify-between">
<div className="flex items-center gap-x-3">
<div className="w-10 rounded-full overflow-hidden aspect-square relative shrink-0">
<Image src={getImageSrcFromMetadataViewsFile(display.thumbnail)} alt="" fill sizes="5vw" />
</div>
<div className="flex flex-col">
<label className="text-medium font-semibold">
{`${display.name}`}
</label>
<label className="text-sm font-medium text-gray-600">
{`${display.description}`}
</label>
</div>
</div>
</div>
)
}

return (
<div className="flex gap-x-3 justify-between">
<div className="flex items-center gap-x-3">
<div className="w-20 rounded-full overflow-hidden aspect-square relative">
<Image src={getImageSrcFromMetadataViewsFile(display.thumbnail)} alt="" fill sizes="5vw" />
</div>
<div className="flex flex-col w-full">
<label className="text-lg font-bold">
{`${display.name}`}
</label>
<label className="text-sm font-medium text-gray-600">
{`${display.description}`}
</label>
</div>
</div>
</div>
)
}
59 changes: 59 additions & 0 deletions components/hybrid_custody/OwnedView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useRouter } from "next/router";
import publicConfig from "../../publicConfig";
import { useRecoilState } from "recoil";
import { showSetupDisplayState, showTransferOwnershipState, transactionInProgressState, transactionStatusState } from "../../lib/atoms";
import { removeChildAccount, removeChildFromChild, setupChildAccountDisplay } from "../../flow/hc_transactions";
import { useSWRConfig } from "swr";
import OwnedDisplayView from "./OwnedDisplayView";

export default function OwnedView(props) {
const [transactionInProgress, setTransactionInProgress] = useRecoilState(transactionInProgressState)
const [, setTransactionStatus] = useRecoilState(transactionStatusState)
const [showSetupDisplay, setShowSetupDisplay] = useRecoilState(showSetupDisplayState)
const [showTransferOwnership, setShowTransferOwnership] = useRecoilState(showTransferOwnershipState)
const { mutate } = useSWRConfig()

const router = useRouter()
const { child, account, user } = props

return (
<div className="min-w-[1076px] flex flex-col gap-y-3 p-4 shadow-md rounded-2xl bg-white">
<div className="flex flex-col gap-y-2">
<div className="flex gap-x-2 justify-between items-center">
<div className="cursor-pointer px-2 text-xl font-bold text-black decoration-drizzle decoration-2 underline">
<a href={`${publicConfig.appURL}//account/${child.address}`}
target="_blank"
rel="noopener noreferrer">
{child.address}
</a>
</div>
<div className="flex gap-x-2 justify-between">
<button
className={`text-white disabled:bg-red-400 bg-red-600 hover:bg-red-800 px-3 py-2 text-sm rounded-2xl font-semibold shrink-0`}
disabled={transactionInProgress || !(user && user.loggedIn && user.addr == account)}
onClick={async () => {
setShowTransferOwnership({ show: true, mode: "FromManager", ownedAddress: child.address })
}}
>
Transfer Ownership
</button>

{/* <button
type="button"
disabled={transactionInProgress || !(user && user.loggedIn && user.addr == account)}
className={`text-white disabled:bg-red-400 disabled:text-white bg-red-600 hover:bg-red-800 px-3 py-2 text-sm rounded-2xl font-semibold shrink-0`}
onClick={async () => {
}}
>
{"Remove"}
</button> */}
</div>
</div>
{
child.display ?
<OwnedDisplayView display={child.display} style={"Small"} type="Owned" /> : null
}
</div>
</div>
)
}
78 changes: 78 additions & 0 deletions components/hybrid_custody/ParentView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { useRouter } from "next/router";
import publicConfig from "../../publicConfig";
import { useRecoilState } from "recoil";
import { transactionInProgressState, transactionStatusState } from "../../lib/atoms";
import { removeParentFromChild } from "../../flow/hc_transactions";
import { useSWRConfig } from "swr";

export default function ParentView(props) {
const [transactionInProgress, setTransactionInProgress] = useRecoilState(transactionInProgressState)
const [, setTransactionStatus] = useRecoilState(transactionStatusState)
const { mutate } = useSWRConfig()

const router = useRouter()
const { parent, account, user } = props

return (
<div className="min-w-[1076px] flex flex-col gap-y-3 p-4 shadow-md rounded-2xl bg-white">
<div className="flex flex-col gap-y-2">
{
!parent.isClaimed ?
<div>
<label className={`font-bold text-xs px-2 py-1 leading-5 rounded-full bg-yellow-100 text-yellow-800`}>{"UNCLAIMED"}</label>
</div>
: <div>
<label className={`font-bold text-xs px-2 py-1 leading-5 rounded-full bg-green-100 text-green-800`}>{"CLAIMED"}</label>
</div>
}
<div className="flex gap-x-2 justify-between items-center">
<div className="cursor-pointer px-2 text-xl font-bold text-black decoration-drizzle decoration-2 underline">
<a href={`${publicConfig.appURL}//account/${parent.address}`}
target="_blank"
rel="noopener noreferrer">
{parent.address}
</a>
</div>
<div>
<button
type="button"
disabled={transactionInProgress || !(user && user.loggedIn && user.addr == account) || !parent.isClaimed}
className={`text-white disabled:bg-red-400 disabled:text-white bg-red-600 hover:bg-red-800 px-3 py-2 text-sm rounded-2xl font-semibold shrink-0`}
onClick={async () => {
await removeParentFromChild(parent.address, setTransactionInProgress, setTransactionStatus)
mutate(["ownedAccountInfoFetcher", account])
}}
>
{"Remove"}
</button>
</div>
</div>
<div className="flex gap-x-2 px-2 text-base text-black">
<div className="text-sm font-semibold text-black">Factory&nbsp;
<a
href={`${publicConfig.appURL}/account/${parent.childAccount.factory.address}`}
target="_blank"
rel="noopener noreferrer"
className="text-gray-600 text-sm font-medium cursor-pointer decoration-drizzle decoration-2 underline">
{`${parent.childAccount.factory.address}`}
</a>
</div>
</div>
<div className="flex gap-x-2 px-2 text-base font-semibold text-black">
<div className="text-sm font-semibold text-black">Filter&nbsp;
<a
href={`${publicConfig.appURL}/account/${parent.childAccount.filter.address}`}
target="_blank"
rel="noopener noreferrer"
className="text-gray-600 text-sm font-medium cursor-pointer decoration-drizzle decoration-2 underline">
{`${parent.childAccount.filter.address}`}
</a>
</div>
</div>
</div>


</div>

)
}
Loading

1 comment on commit ec7d5f3

@vercel
Copy link

@vercel vercel bot commented on ec7d5f3 Aug 21, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.