/
index.tsx
263 lines (216 loc) · 8.44 KB
/
index.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
import Head from 'next/head'
import MintNFT from '../components/MintNFT';
import type { NextPage } from 'next'
import styles from '../styles/Home.module.css'
import { useState, useEffect } from "react";
import WalletInfo from '../components/WalletInfo';
import Program from '../contracts/nft.hl';
import {
Assets,
Address,
ByteArrayData,
Cip30Handle,
Cip30Wallet,
ConstrData,
hexToBytes,
NetworkParams,
Value,
TxOutput,
Tx,
WalletHelper} from "@hyperionbt/helios";
declare global {
interface Window {
cardano:any;
}
}
const Home: NextPage = () => {
const optimize = false;
const networkParamsUrl = "https://d1t0d7c2nekuk0.cloudfront.net/preprod.json";
//const networkParamsUrl = "https://d1t0d7c2nekuk0.cloudfront.net/preview.json";
const [walletInfo, setWalletInfo] = useState({ balance : ''});
const [walletIsEnabled, setWalletIsEnabled] = useState(false);
const [whichWalletSelected, setWhichWalletSelected] = useState(undefined);
const [walletAPI, setWalletAPI] = useState<undefined | any>(undefined);
const [walletHelper, setWalletHelper] = useState<undefined | any>(undefined);
const [tx, setTx] = useState({ txId : '' });
useEffect(() => {
const checkWallet = async () => {
setWalletIsEnabled(await checkIfWalletFound());
}
checkWallet();
}, [whichWalletSelected]);
useEffect(() => {
const enableSelectedWallet = async () => {
if (walletIsEnabled) {
await enableWallet();
}
}
enableSelectedWallet();
}, [walletIsEnabled]);
useEffect(() => {
const updateWalletInfo = async () => {
if (walletIsEnabled) {
const _balance = await getBalance() as string;
setWalletInfo({
...walletInfo,
balance : _balance
});
}
}
updateWalletInfo();
}, [walletAPI]);
// user selects what wallet to connect to
const handleWalletSelect = (obj : any) => {
const whichWalletSelected = obj.target.value
setWhichWalletSelected(whichWalletSelected);
}
const checkIfWalletFound = async () => {
let walletFound = false;
const walletChoice = whichWalletSelected;
if (walletChoice === "nami") {
walletFound = !!window?.cardano?.nami;
} else if (walletChoice === "eternl") {
walletFound = !!window?.cardano?.eternl;
}
return walletFound;
}
const enableWallet = async () => {
try {
const walletChoice = whichWalletSelected;
if (walletChoice === "nami") {
const handle: Cip30Handle = await window.cardano.nami.enable();
const walletAPI = new Cip30Wallet(handle);
const walletHelper = new WalletHelper(walletAPI);
setWalletHelper(walletHelper);
setWalletAPI(walletAPI);
} else if (walletChoice === "eternl") {
const handle: Cip30Handle = await window.cardano.eternl.enable();
const walletAPI = new Cip30Wallet(handle);
const walletHelper = new WalletHelper(walletAPI);
setWalletHelper(walletHelper);
setWalletAPI(walletAPI);
}
} catch (err) {
console.log('enableWallet error', err);
}
}
const getBalance = async () => {
try {
const balanceAmountValue = await walletHelper.calcBalance();
const balanceAmount = balanceAmountValue.lovelace;
const walletBalance : BigInt = BigInt(balanceAmount);
return walletBalance.toLocaleString();
} catch (err) {
console.log('getBalance error: ', err);
}
}
const mintNFT = async (params : any) => {
// Re-enable wallet API since wallet account may have been changed
await enableWallet();
const address = params[0];
const name = params[1];
const description = params[2];
const img = params[3];
const minAda : number = 2000000; // minimum lovelace needed to send an NFT
const maxTxFee: number = 500000; // maximum estimated transaction fee
const minChangeAmt: number = 1000000; // minimum lovelace needed to be sent back as change
const minAdaVal = new Value(BigInt(minAda));
const minUTXOVal = new Value(BigInt(minAda + maxTxFee + minChangeAmt));
// Get wallet UTXOs
const utxos = await walletHelper.pickUtxos(minUTXOVal);
// Get change address
const changeAddr = await walletHelper.changeAddress;
// Start building the transaction
const tx = new Tx();
// Add the UTXO as inputs
tx.addInputs(utxos[0]);
const nftProgram = new Program();
nftProgram.parameters = {["TX_ID"] : utxos[0][0].txId.hex};
nftProgram.parameters = {["TX_IDX"] : utxos[0][0].utxoIdx};
nftProgram.parameters = {["TN"] : name};
// Compile the helios minting script
const nftCompiledProgram = nftProgram.compile(optimize);
// Add the script as a witness to the transaction
tx.attachScript(nftCompiledProgram);
// Construct the NFT that we will want to send as an output
const nftTokenName = ByteArrayData.fromString(name).toHex();
const tokens: [number[], bigint][] = [[hexToBytes(nftTokenName), BigInt(1)]];
// Create an empty Redeemer because we must always send a Redeemer with
// a plutus script transaction even if we don't actually use it.
const mintRedeemer = new ConstrData(0, []);
// Indicate the minting we want to include as part of this transaction
tx.mintTokens(
nftCompiledProgram.mintingPolicyHash,
tokens,
mintRedeemer
)
// Construct the output and include both the minimum Ada as well as the minted NFT
tx.addOutput(new TxOutput(
Address.fromBech32(address),
new Value(minAdaVal.lovelace, new Assets([[nftCompiledProgram.mintingPolicyHash, tokens]]))
));
const networkParams = new NetworkParams(
await fetch(networkParamsUrl)
.then(response => response.json())
)
// Attached the metadata for the minting transaction
tx.addMetadata(721, {"map": [[nftCompiledProgram.mintingPolicyHash.hex, {"map": [[name,
{
"map": [["name", name],
["description", description],
["image", img]
]
}
]]}
]]
}
);
console.log("tx before final", tx.dump());
// Send any change back to the buyer
await tx.finalize(networkParams, changeAddr, utxos[1]);
console.log("Verifying signature...");
const signatures = await walletAPI.signTx(tx);
tx.addSignatures(signatures);
console.log("tx after final", tx.dump());
console.log("Submitting transaction...");
const txHash = await walletAPI.submitTx(tx);
console.log("txHash", txHash.hex);
setTx({ txId: txHash.hex });
}
return (
<div className={styles.container}>
<Head>
<title>Helios Tx Builder</title>
<meta name="description" content="Littercoin web tools page" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h3 className={styles.title}>
Helios Tx Builder
</h3>
<div className={styles.borderwallet}>
<p>
Connect to your wallet
</p>
<p className={styles.borderwallet}>
<input type="radio" id="nami" name="wallet" value="nami" onChange={handleWalletSelect}/>
<label>Nami</label>
</p>
<p className={styles.borderwallet}>
<input type="radio" id="eternl" name="wallet" value="eternl" onChange={handleWalletSelect}/>
<label>Eternl</label>
</p>
</div>
{!tx.txId && walletIsEnabled && <div className={styles.border}><WalletInfo walletInfo={walletInfo}/></div>}
{tx.txId && <div className={styles.border}><b>Transaction Success!!!</b>
<p>TxId <a href={"https://preprod.cexplorer.io/tx/" + tx.txId} target="_blank" rel="noopener noreferrer" >{tx.txId}</a></p>
<p>Please wait until the transaction is confirmed on the blockchain and reload this page before doing another transaction</p>
</div>}
{walletIsEnabled && !tx.txId && <div className={styles.border}><MintNFT onMintNFT={mintNFT}/></div>}
</main>
<footer className={styles.footer}>
</footer>
</div>
)
}
export default Home