Skip to content

Commit

Permalink
Merge branch 'master' into tomasztorz/l2b-7577-check-issue-with-explo…
Browse files Browse the repository at this point in the history
…rer-links
  • Loading branch information
adamiak committed Oct 15, 2024
2 parents fca7c1d + 6d5dfe6 commit e097c3e
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 68 deletions.
4 changes: 3 additions & 1 deletion packages/backend/src/api/controllers/UserController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ export class UserController {

async getUserPage(
givenUser: Partial<UserDetails>,
starkKey: StarkKey
starkKey: StarkKey,
performUserActions: boolean | undefined
): Promise<ControllerResult> {
const context = await this.pageContextService.getPageContext(givenUser)
const collateralAsset = this.pageContextService.getCollateralAsset(context)
Expand Down Expand Up @@ -308,6 +309,7 @@ export class UserController {
totalTransactions,
offers,
totalOffers: forcedTradeOffersCount,
performUserActions,
})

return { type: 'success', content }
Expand Down
9 changes: 8 additions & 1 deletion packages/backend/src/api/routers/FrontendRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,19 @@ export function createFrontendRouter(
params: z.object({
starkKey: stringAs(StarkKey),
}),
query: z.object({
performUserActions: z
.string()
.transform((value) => value === 'true')
.optional(),
}),
}),
async (ctx) => {
const givenUser = getGivenUser(ctx)
const result = await userController.getUserPage(
givenUser,
ctx.params.starkKey
ctx.params.starkKey,
ctx.query.performUserActions
)
applyControllerResult(ctx, result)
}
Expand Down
13 changes: 13 additions & 0 deletions packages/escape-hatch-test/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as helpers from '@nomicfoundation/hardhat-toolbox/network-helpers'
import { ethers } from 'ethers'

export class HardhatUtils {
USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
ERC20_ABI = [
'function balanceOf(address _owner) public view returns (uint256 balance)'
]
PERPETUAL_ABI = [
'function isFrozen() public view returns (bool)',
'function forcedWithdrawalRequest(uint256,uint256,uint256,bool) external',
Expand Down Expand Up @@ -61,6 +65,15 @@ export class HardhatUtils {
return this.provider.getBalance(address)
}

async getUSDCBalance(address: string) {
const usdc = new ethers.Contract(
this.USDC_ADDRESS,
this.ERC20_ABI,
this.provider
)
return await usdc.balanceOf(address)
}

async triggerFreezable() {
// Impersonate the user of position #1
await helpers.impersonateAccount(this.FORCED_WITHDRAWAL.ethereumAddress)
Expand Down
68 changes: 68 additions & 0 deletions packages/frontend/src/preview/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,74 @@ const routes: Route[] = [
})
},
},
{
path: '/users/someone/exchange-frozen',
description: 'Someone user page, the exchange is frozen.',
render: (ctx) => {
const context = getPerpetualPageContext(ctx, {
fallbackToFakeUser: true,
})
context.freezeStatus = 'frozen'

ctx.body = renderUserPage({
context: context,
starkKey: StarkKey.fake(),
ethereumAddress: context.user.address,
exchangeAddress: EthereumAddress.fake(),
withdrawableAssets: [],
finalizableOffers: [],
assets: [
randomUserAssetEntry('ESCAPE', { hashOrId: AssetId('USDC-6') }),
...repeat(7, () => randomUserAssetEntry('USE_COLLATERAL_ESCAPE')),
],
totalAssets: 18,
balanceChanges: repeat(10, randomUserBalanceChangeEntry),
totalBalanceChanges: 6999,
transactions: repeat(10, randomUserTransactionEntry),
totalTransactions: 48,
l2Transactions: [],
totalL2Transactions: 0,
offers: repeat(6, randomUserOfferEntry),
totalOffers: 6,
escapableAssets: [],
})
},
},
{
path: '/users/someone/exchange-frozen/performUserActions',
description:
'Someone user page, the exchange is frozen. You can perform actions for them.',
render: (ctx) => {
const context = getPerpetualPageContext(ctx, {
fallbackToFakeUser: true,
})
context.freezeStatus = 'frozen'

ctx.body = renderUserPage({
context: context,
starkKey: StarkKey.fake(),
ethereumAddress: context.user.address,
exchangeAddress: EthereumAddress.fake(),
withdrawableAssets: [],
finalizableOffers: [],
assets: [
randomUserAssetEntry('ESCAPE', { hashOrId: AssetId('USDC-6') }),
...repeat(7, () => randomUserAssetEntry('USE_COLLATERAL_ESCAPE')),
],
totalAssets: 18,
balanceChanges: repeat(10, randomUserBalanceChangeEntry),
totalBalanceChanges: 6999,
transactions: repeat(10, randomUserTransactionEntry),
totalTransactions: 48,
l2Transactions: [],
totalL2Transactions: 0,
offers: repeat(6, randomUserOfferEntry),
totalOffers: 6,
escapableAssets: [],
performUserActions: true,
})
},
},
{
path: '/escape/:positionOrVaultId',
link: '/escape/12345',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ContentWrapper } from '../../components/page/ContentWrapper'
import { Page } from '../../components/page/Page'
import { TermsOfServiceAck } from '../../components/TermsOfServiceAck'
import { reactToHtml } from '../../reactToHtml'
import { PerformUserActionsPanel } from '../user/components/PerformUserActionsPanel'

export const VERIFY_ESCAPE_REQUEST_FORM_ID = 'verify-escape-request-form'

Expand Down Expand Up @@ -62,59 +63,67 @@ function EscapeHatchActionPage(props: Props) {
description="Withdraw funds via Escape Hatch"
context={props.context}
>
<ContentWrapper className="grid auto-rows-min grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-12">
<div className="flex flex-col gap-3">
<div className="hidden text-xxl font-semibold lg:block">
Escape your funds
<ContentWrapper className="flex flex-col lg:gap-12">
<PerformUserActionsPanel
starkKey={props.starkKey}
performUserActions={true}
context={props.context}
/>
<div className="grid auto-rows-min grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-12">
<div className="flex flex-col gap-3">
<div className="hidden text-xxl font-semibold lg:block">
Escape your funds
</div>
<span className="font-medium text-zinc-500 lg:mt-3">
The exchange is frozen, preventing it from executing regular
operations or supporting standard actions.
</span>
<span className="font-medium text-zinc-500">
You have the option to request a withdrawal of the entire value of
any position to position's owner address by activating an 'escape
hatch.' This process involves interacting with an Ethereum
contract, which calculates the total value of the position,
including any open trades and funding rates.
</span>
<span className="font-bold">
Ultimately the funds will be withdraw to the Ethereum address of
the position's owner, regardless of the wallet used to perform the
Escape Hatch transactions.
</span>
<span>The escape process consists of three steps:</span>
<OrderedList items={steps} />
<span className="font-medium text-zinc-500">
Please note, the execution of an Escape can be expensive due to
the Ethereum gas cost.
</span>
</div>
<span className="font-medium text-zinc-500 lg:mt-3">
The exchange is frozen, preventing it from executing regular
operations or supporting standard actions.
</span>
<span className="font-medium text-zinc-500">
You have the option to request a withdrawal of the entire value of
any position by activating an 'Escape Hatch.' This process involves
interacting with an Ethereum contract, which calculates the total
value of the position, including any open trades and funding rates.
</span>
<span className="font-bold">
Ultimately the funds will be withdraw to the Ethereum address of the
position's owner, regardless of the wallet used to perform the
Escape Hatch transactions.
</span>
<span>The escape process consists of three steps:</span>
<OrderedList items={steps} />
<span className="font-medium text-zinc-500">
Please note, the execution of an Escape can be expensive due to the
Ethereum gas cost.
</span>
</div>
<Card className="row-start-1 h-min lg:col-start-2">
<form
id={VERIFY_ESCAPE_REQUEST_FORM_ID}
className="flex flex-col gap-6"
data-props={formPropsJson}
data-user={userJson}
>
<div className="flex items-end justify-between">
<span className="text-xl font-semibold">Escape</span>
<span>
<span className="text-sm font-medium text-zinc-500">
{props.context.tradingMode === 'perpetual'
? 'Position'
: 'Vault'}
</span>{' '}
<span className="text-lg font-semibold">
#{props.positionOrVaultId.toString()}
<Card className="row-start-1 h-min lg:col-start-2">
<form
id={VERIFY_ESCAPE_REQUEST_FORM_ID}
className="flex flex-col gap-6"
data-props={formPropsJson}
data-user={userJson}
>
<div className="flex items-end justify-between">
<span className="text-xl font-semibold">Escape</span>
<span>
<span className="text-sm font-medium text-zinc-500">
{props.context.tradingMode === 'perpetual'
? 'Position'
: 'Vault'}
</span>{' '}
<span className="text-lg font-semibold">
#{props.positionOrVaultId.toString()}
</span>
</span>
</span>
</div>
<TermsOfServiceAck prefix="By initiating the escape process, you agree to our" />
<Button className="w-full" size="lg">
Initiate Escape
</Button>
</form>
</Card>
</div>
<TermsOfServiceAck prefix="By initiating the escape process, you agree to our" />
<Button className="w-full" size="lg">
Initiate Escape
</Button>
</form>
</Card>
</div>
</ContentWrapper>
</Page>
)
Expand Down
39 changes: 28 additions & 11 deletions packages/frontend/src/view/pages/user/UserPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
getTransactionTableProps,
getUserPageProps,
} from './common'
import { PerformUserActionsPanel } from './components/PerformUserActionsPanel'
import { UserAssetEntry, UserAssetsTable } from './components/UserAssetTable'
import {
UserBalanceChangeEntry,
Expand Down Expand Up @@ -56,6 +57,7 @@ interface UserPageProps {
totalL2Transactions: number
offers?: OfferEntry[]
totalOffers: number
performUserActions?: boolean
}

export function renderUserPage(props: UserPageProps) {
Expand All @@ -64,7 +66,18 @@ export function renderUserPage(props: UserPageProps) {

function UserPage(props: UserPageProps) {
const common = getUserPageProps(props.starkKey)
const isMine = props.context.user?.starkKey === props.starkKey
let isMine = props.context.user?.starkKey === props.starkKey

if (!isMine) {
// If exchange is frozen and flag is passed, let others perform these actions
if (
props.context.user !== undefined &&
props.context.freezeStatus === 'frozen' &&
props.performUserActions
) {
isMine = true
}
}

const { title: assetsTableTitle, ...assetsTablePropsWithoutTitle } =
getAssetsTableProps(props.starkKey)
Expand Down Expand Up @@ -97,16 +110,20 @@ function UserPage(props: UserPageProps) {
chainId={props.context.chainId}
ethereumAddress={props.ethereumAddress}
/>
{isMine && (
<UserQuickActionsTable
escapableAssets={props.escapableAssets}
withdrawableAssets={props.withdrawableAssets}
finalizableOffers={props.finalizableOffers}
context={props.context}
exchangeAddress={props.exchangeAddress}
starkKey={props.starkKey}
/>
)}
<PerformUserActionsPanel
performUserActions={props.performUserActions}
starkKey={props.starkKey}
context={props.context}
/>
<UserQuickActionsTable
escapableAssets={props.escapableAssets}
withdrawableAssets={props.withdrawableAssets}
finalizableOffers={props.finalizableOffers}
context={props.context}
exchangeAddress={props.exchangeAddress}
starkKey={props.starkKey}
isMine={isMine}
/>
</div>
</section>
<Tabs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type FinalizeEscapeFormProps = z.infer<typeof FinalizeEscapeFormProps>
export const FinalizeEscapeFormProps = z.intersection(
z.object({
exchangeAddress: stringAs(EthereumAddress),
isMine: z.boolean().optional(),
}),
z.discriminatedUnion('tradingMode', [
z.object({
Expand Down Expand Up @@ -54,7 +55,10 @@ export function FinalizeEscapeForm(props: Props) {
data-props={formPropsJson}
data-user={userJson}
>
<Button className="ml-auto w-32 !px-0" size="sm">
<Button
className={'ml-auto w-32 !px-0 ' + (!props.isMine ? 'invisible' : '')}
size="sm"
>
Finalize escape
</Button>
</form>
Expand Down
Loading

0 comments on commit e097c3e

Please sign in to comment.