generated from veeso-dev/rust-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
953 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
src/ekoke_erc20_swap_frontend/src/js/components/App/pages/SwapIcrcToErc20.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import * as React from 'react'; | ||
import * as Icon from 'react-feather'; | ||
import { useConnectedMetaMask } from 'metamask-react'; | ||
import { useConnectedIcWallet } from 'react-ic-wallet'; | ||
|
||
import { Page, PageProps } from '../ConnectedPage'; | ||
import { e8sToEkoke, validateEthAddress } from '../../../utils'; | ||
import Container from '../../reusable/Container'; | ||
import Link from '../../reusable/Link'; | ||
import Ethereum from '../../svg/Ethereum'; | ||
import Heading from '../../reusable/Heading'; | ||
import InternetComputer from '../../svg/InternetComputer'; | ||
import Input from '../../reusable/Input'; | ||
import Alerts from '../../reusable/Alerts'; | ||
import Paragraph from '../../reusable/Paragraph'; | ||
import Button from '../../reusable/Button'; | ||
|
||
const SwapIcrcToErc20 = ({ onSwitchPage }: PageProps) => { | ||
const { account } = useConnectedMetaMask(); | ||
const { principal } = useConnectedIcWallet(); | ||
|
||
const [recipientAddress, setRecipientAddress] = | ||
React.useState<string>(account); | ||
const [amount, setAmount] = React.useState<string>(''); | ||
const [userBalance, setUserBalance] = React.useState<BigInt>(); | ||
const [processing, setProcessing] = React.useState<boolean>(false); | ||
const [error, setError] = React.useState<string | null>(null); | ||
const [swapTxHash, setSwapTxHash] = React.useState<string | false>(false); | ||
|
||
const onRecipientAddressChanged = ( | ||
e: React.ChangeEvent<HTMLInputElement>, | ||
) => { | ||
setRecipientAddress(e.target.value); | ||
}; | ||
|
||
const onAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
setAmount(e.target.value); | ||
}; | ||
|
||
const validateUserAmount = ( | ||
amount: string | number | readonly string[] | undefined, | ||
): boolean => { | ||
if (typeof amount !== 'string') return false; | ||
|
||
if (isNaN(parseInt(amount))) { | ||
return false; | ||
} | ||
|
||
if (userBalance === undefined) return true; | ||
|
||
const userAmount = BigInt(amount); | ||
return userAmount <= userBalance.valueOf(); | ||
}; | ||
|
||
const onSwap = () => { | ||
setProcessing(true); | ||
|
||
// check if the user has enough balance | ||
if (!validateUserAmount(amount)) { | ||
setProcessing(false); | ||
setError('Insufficient balance.'); | ||
return; | ||
} | ||
if (!validateEthAddress(recipientAddress)) { | ||
setProcessing(false); | ||
setError('Invalid address.'); | ||
return; | ||
} | ||
|
||
const numAmount = BigInt(amount); | ||
}; | ||
|
||
const disabled = !recipientAddress || !amount || processing; | ||
|
||
return ( | ||
<Container.FlexCols className="items-center justify-center"> | ||
<Container.Card className="px-12 sm:px-4"> | ||
<Container.FlexResponsiveRow className="items-center sm:items-start justify-between sm:justify-start gap-8"> | ||
<Container.Container className="flex-0"> | ||
{!processing && ( | ||
<Link.IconLink | ||
className="hover:cursor-pointer" | ||
onClick={() => onSwitchPage(Page.Summary)} | ||
> | ||
<Icon.ArrowLeft className="mr-2 inline" /> | ||
Back | ||
</Link.IconLink> | ||
)} | ||
</Container.Container> | ||
<Container.FlexRow className="flex-1 items-center justify-center gap-4"> | ||
<InternetComputer className="h-[32px] sm:hidden" /> | ||
<Heading.H1 className="sm:text-lg">Swap ICRC to ERC20</Heading.H1> | ||
<Ethereum className="w-[32px] sm:hidden" /> | ||
</Container.FlexRow> | ||
</Container.FlexResponsiveRow> | ||
{userBalance && ( | ||
<Container.Container className="py-4 text-text"> | ||
<span>Your EKOKE ICRC balance: {e8sToEkoke(userBalance)}</span> | ||
</Container.Container> | ||
)} | ||
<Container.FlexCols className="gap-4"> | ||
<Container.Container> | ||
<Input.IconInput | ||
className="pl-[40px] sm:pl-[8px]" | ||
icon={<Ethereum className="h-[20px] sm:hidden" />} | ||
label="Recipient Ethereum Address" | ||
id="recipient-eth-address" | ||
placeholder={account} | ||
value={recipientAddress} | ||
validate={validateEthAddress} | ||
validationMessage="Please enter a valid ethereum address." | ||
onChange={onRecipientAddressChanged} | ||
/> | ||
</Container.Container> | ||
<Container.Container> | ||
<Input.IconInput | ||
className="pl-[40px]" | ||
icon={<Icon.DollarSign size={20} className="inline" />} | ||
label="Amount" | ||
id="swap-amount" | ||
value={amount} | ||
placeholder="10000" | ||
type="number" | ||
validationMessage="Please enter a valid amount." | ||
validate={validateUserAmount} | ||
onChange={onAmountChange} | ||
/> | ||
</Container.Container> | ||
<Container.FlexCols className="items-center justify-center gap-8 sm:gap-2"> | ||
{error && ( | ||
<Alerts.Danger> | ||
<Paragraph.Default className="!text-left"> | ||
{error} | ||
</Paragraph.Default> | ||
</Alerts.Danger> | ||
)} | ||
{swapTxHash && ( | ||
<Alerts.Info> | ||
<Paragraph.Default className="!text-left"> | ||
Swap successful! See your transaction{' '} | ||
<Link.Paragraph | ||
href={`https://etherscan.io/tx/${swapTxHash}`} | ||
target="_blank" | ||
> | ||
{swapTxHash} | ||
</Link.Paragraph> | ||
</Paragraph.Default> | ||
</Alerts.Info> | ||
)} | ||
<Button.Cta onClick={onSwap} disabled={disabled}> | ||
{processing ? ( | ||
<Icon.Loader className="inline mr-2 animate-spin" size={20} /> | ||
) : ( | ||
<InternetComputer className="inline mr-2 h-[20px]" /> | ||
)} | ||
<span>Swap</span> | ||
</Button.Cta> | ||
</Container.FlexCols> | ||
</Container.FlexCols> | ||
</Container.Card> | ||
</Container.FlexCols> | ||
); | ||
}; | ||
|
||
export default SwapIcrcToErc20; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import * as React from 'react'; | ||
import { ActorSubclass } from '@dfinity/agent'; | ||
import { useIcWallet } from 'react-ic-wallet'; | ||
|
||
import { | ||
EkokeLedger, | ||
idlFactory as ekokeLedgerIdlFactory, | ||
} from './ekoke-ledger.did'; | ||
import { | ||
EkokeErc20Swap, | ||
idlFactory as ekokeErc20SwapIdlFactory, | ||
} from './ekoke-erc20-swap.did'; | ||
|
||
const EKOKE_ERC20_SWAP_CANISTER_ID = ''; | ||
const EKOKE_LEDGER_CANISTER_ID = ''; | ||
|
||
interface Context { | ||
ekokeErc20Swap?: ActorSubclass<EkokeErc20Swap>; | ||
ekokeLedger?: ActorSubclass<EkokeLedger>; | ||
} | ||
|
||
export const AgentContext = React.createContext<Context>({}); | ||
|
||
const AgentContextProvider = ({ children }: { children?: React.ReactNode }) => { | ||
const [ekokeErc20Swap, setEkokeErc20Swap] = | ||
React.useState<ActorSubclass<EkokeErc20Swap>>(); | ||
const [ekokeLedger, setEkokeLedger] = | ||
React.useState<ActorSubclass<EkokeLedger>>(); | ||
|
||
const { createActor, status } = useIcWallet(); | ||
|
||
React.useEffect(() => { | ||
if (status === 'connected') { | ||
createActor(EKOKE_ERC20_SWAP_CANISTER_ID, ekokeErc20SwapIdlFactory).then( | ||
(actor) => { | ||
setEkokeErc20Swap(actor as ActorSubclass<EkokeErc20Swap>); | ||
}, | ||
); | ||
createActor(EKOKE_LEDGER_CANISTER_ID, ekokeLedgerIdlFactory).then( | ||
(actor) => { | ||
setEkokeLedger(actor as ActorSubclass<EkokeLedger>); | ||
}, | ||
); | ||
} else { | ||
setEkokeErc20Swap(undefined); | ||
setEkokeLedger(undefined); | ||
} | ||
}, [status]); | ||
|
||
return ( | ||
<AgentContext.Provider value={{ ekokeErc20Swap, ekokeLedger }}> | ||
{children} | ||
</AgentContext.Provider> | ||
); | ||
}; | ||
|
||
export default AgentContextProvider; |
Oops, something went wrong.