-
Notifications
You must be signed in to change notification settings - Fork 3
/
transfer.tsx
127 lines (114 loc) · 5.17 KB
/
transfer.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import {FC, useCallback, useEffect} from "react";
import {useConnection, useWallet} from "@solana/wallet-adapter-react";
import {
createAssociatedTokenAccount,
sendTransactionWithTokenFee,
buildTransactionToTransfer,
buildTransactionToCreateAccount
} from "../utils/octane";
import {PublicKey} from "@solana/web3.js";
import useOctaneConfigStore from "../stores/useOctaneConfigStore";
import useTransferFormStore, { ATAState } from "../stores/useTransferFormStore";
import {OctaneFeesInfo} from "../components/OctaneFeesInfo";
import {RecipientWalletAddressInput} from "../components/RecipientWalletAddressInput";
import {MintInput} from "../components/MintInput";
import {TokenAmountInput} from "../components/TokenAmountInput";
import {SubmitButton} from "../components/SubmitButton";
import {notify} from "../utils/notifications";
import {Title} from "../components/Title";
const EXAMPLE_RECIPIENT = 'BAmWtkoKG64rpeUvgN37NVUz3SGYdu8f16PvpM2waK3v';
export const TransferView: FC = ({ }) => {
const { publicKey, signTransaction } = useWallet();
const { connection } = useConnection();
// todo: validate balance of wallet in tokens
// todo: do not let to input arbitrary strings into amount
// todo: handle errors via notify
const octaneConfig = useOctaneConfigStore((s) => s.config);
const feePayer = useOctaneConfigStore((s) => s.config ? new PublicKey(s.config.feePayer) : null);
const { fetchOctaneConfig, getTransferFeeConfig, getATAFeeConfig } = useOctaneConfigStore();
useEffect(fetchOctaneConfig, [fetchOctaneConfig]);
const mint = useTransferFormStore((s) => s.mint);
const address = useTransferFormStore((s) => s.address);
const amount = useTransferFormStore((s) => s.amount);
const accountState = useTransferFormStore((s) => s.accountState);
const { setMint, setAddress, setAmount, updateAccountState } = useTransferFormStore();
const submitTransfer = useCallback(async () => {
const mintAsPublicKey = new PublicKey(mint);
const addressAsPublicKey = new PublicKey(address);
const ATAFeeConfig = getATAFeeConfig( mint);
const transferFeeConfig = getTransferFeeConfig(mint);
if (accountState === ATAState.not_created) {
const accountTransaction = await buildTransactionToCreateAccount(
connection,
feePayer,
ATAFeeConfig,
mintAsPublicKey,
publicKey,
addressAsPublicKey,
);
const signedAccountTransaction = await signTransaction(accountTransaction);
const accountTxId = await createAssociatedTokenAccount(signedAccountTransaction);
notify({ type: 'success', message: 'Associated token account created!', txid: accountTxId });
}
const transferTransaction = await buildTransactionToTransfer(
connection,
feePayer,
transferFeeConfig,
mintAsPublicKey,
publicKey,
addressAsPublicKey,
Math.floor(parseFloat(amount) * (10 ** transferFeeConfig.decimals))
);
const signedTransferTransaction = await signTransaction(transferTransaction);
const transferTxId = await sendTransactionWithTokenFee(signedTransferTransaction);
notify({ type: 'success', message: 'Transfer is successful!', txid: transferTxId });
}, [mint, address, accountState, connection, feePayer, publicKey, amount, signTransaction, getTransferFeeConfig, getATAFeeConfig]);
const enableSubmit = publicKey && (accountState === ATAState.not_created || accountState === ATAState.created) && amount !== '';
return (
<div className="md:hero mx-auto p-4">
<div className="md:hero-content flex flex-col">
<Title text="Gasless Token Transfer" />
{ octaneConfig && (
<div className="text-center flex flex-col space-y-1">
<RecipientWalletAddressInput
address={address}
onChange={e => {
setAddress(e.target.value);
updateAccountState(connection);
}}
suggestion={EXAMPLE_RECIPIENT}
onSuggestionClick={() => setAddress(EXAMPLE_RECIPIENT)}
/>
<MintInput
currentMint={mint}
onChange={e => {
setMint(e.target.value);
updateAccountState(connection);
}}
availableMints={octaneConfig.endpoints.transfer.tokens.map(tokenFee => tokenFee.mint)}
/>
<TokenAmountInput
currentAmount={amount}
onChange={(e) => setAmount(e.target.value)}
/>
{ accountState === ATAState.created && (
<OctaneFeesInfo fees={[{name: 'Transaction fees', fee: getTransferFeeConfig(mint)}]}/>
)}
{ accountState === ATAState.not_created && (
<OctaneFeesInfo fees={[
{name: 'Transaction fees', fee: getTransferFeeConfig(mint)},
{name: 'Associated token account rent', fee: getATAFeeConfig(mint)},
]}/>
)}
<SubmitButton
onClick={submitTransfer}
disabled={!enableSubmit}
text={"Send transaction"}
disabledText={!publicKey ? "Connect wallet" : "Select mint and amount"}
/>
</div>
)}
</div>
</div>
);
};