Skip to content

Commit da86e74

Browse files
authored
Merge pull request #414 from Emurgo/add-mint-nft-example
add mint nft example
2 parents 86dc681 + 0b1fbf9 commit da86e74

File tree

1 file changed

+251
-0
lines changed

1 file changed

+251
-0
lines changed
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
2+
# Minting Nfts using yoroi backend and cardano-serialization-lib
3+
4+
This example mints nfts directly to an account on testnet.
5+
6+
code taken from (here)[https://github.com/ozgrakkurt/cardano-mint-nft]
7+
8+
```javascript
9+
import CardanoWasm from "@emurgo/cardano-serialization-lib-nodejs";
10+
import axios from "axios";
11+
import cbor from "cbor";
12+
13+
const mintNft = async (
14+
privateKey,
15+
policy,
16+
assetName,
17+
description,
18+
imageUrl,
19+
mediaType
20+
) => {
21+
const FEE = 300000;
22+
23+
const publicKey = privateKey.to_public();
24+
25+
const addr = CardanoWasm.BaseAddress.new(
26+
CardanoWasm.NetworkInfo.testnet().network_id(),
27+
CardanoWasm.StakeCredential.from_keyhash(publicKey.hash()),
28+
CardanoWasm.StakeCredential.from_keyhash(publicKey.hash())
29+
).to_address();
30+
31+
const policyPubKey = policy.privateKey.to_public();
32+
33+
const policyAddr = CardanoWasm.BaseAddress.new(
34+
CardanoWasm.NetworkInfo.testnet().network_id(),
35+
CardanoWasm.StakeCredential.from_keyhash(policyPubKey.hash()),
36+
CardanoWasm.StakeCredential.from_keyhash(policyPubKey.hash())
37+
).to_address();
38+
39+
console.log(`ADDR: ${addr.to_bech32()}`);
40+
41+
// get utxos for our address and select one that is probably big enough to pay the tx fee
42+
const utxoRes = await axios.post(
43+
"https://testnet-backend.yoroiwallet.com/api/txs/utxoForAddresses",
44+
{
45+
addresses: [addr.to_bech32()],
46+
}
47+
);
48+
49+
let utxo = null;
50+
51+
if (utxoRes.data) {
52+
for (const utxoEntry of utxoRes.data) {
53+
if (utxoEntry.amount > FEE) {
54+
utxo = utxoEntry;
55+
}
56+
}
57+
}
58+
59+
if (utxo === null) {
60+
throw new Error("no utxo found with sufficient ADA.");
61+
}
62+
63+
console.log(`UTXO: ${JSON.stringify(utxo, null, 4)}`);
64+
65+
// get current global slot from yoroi backend
66+
const { data: slotData } = await axios.get(
67+
"https://testnet-backend.yoroiwallet.com/api/v2/bestblock"
68+
);
69+
70+
const ttl = slotData.globalSlot + 60 * 60 * 2; // two hours from now
71+
72+
const txBuilder = CardanoWasm.TransactionBuilder.new(
73+
CardanoWasm.TransactionBuilderConfigBuilder.new()
74+
.fee_algo(
75+
CardanoWasm.LinearFee.new(
76+
CardanoWasm.BigNum.from_str("44"),
77+
CardanoWasm.BigNum.from_str("155381")
78+
)
79+
)
80+
.coins_per_utxo_word(CardanoWasm.BigNum.from_str("34482"))
81+
.pool_deposit(CardanoWasm.BigNum.from_str("500000000"))
82+
.key_deposit(CardanoWasm.BigNum.from_str("2000000"))
83+
.max_value_size(5000)
84+
.max_tx_size(16384)
85+
.build()
86+
);
87+
88+
const scripts = CardanoWasm.NativeScripts.new();
89+
90+
const policyKeyHash = CardanoWasm.BaseAddress.from_address(policyAddr)
91+
.payment_cred()
92+
.to_keyhash();
93+
94+
console.log(
95+
`POLICY_KEYHASH: ${Buffer.from(policyKeyHash.to_bytes()).toString("hex")}`
96+
);
97+
98+
// add key hash script so only people with policy key can mint assets using this policyId
99+
const keyHashScript = CardanoWasm.NativeScript.new_script_pubkey(
100+
CardanoWasm.ScriptPubkey.new(policyKeyHash)
101+
);
102+
scripts.add(keyHashScript);
103+
104+
const policyTtl = policy.ttl || ttl;
105+
106+
console.log(`POLICY_TTL: ${policyTtl}`);
107+
108+
// add timelock so policy is locked after this slot
109+
const timelock = CardanoWasm.TimelockExpiry.new(policyTtl);
110+
const timelockScript = CardanoWasm.NativeScript.new_timelock_expiry(timelock);
111+
scripts.add(timelockScript);
112+
113+
const mintScript = CardanoWasm.NativeScript.new_script_all(
114+
CardanoWasm.ScriptAll.new(scripts)
115+
);
116+
117+
const privKeyHash = CardanoWasm.BaseAddress.from_address(addr)
118+
.payment_cred()
119+
.to_keyhash();
120+
txBuilder.add_key_input(
121+
privKeyHash,
122+
CardanoWasm.TransactionInput.new(
123+
CardanoWasm.TransactionHash.from_bytes(Buffer.from(utxo.tx_hash, "hex")),
124+
utxo.tx_index
125+
),
126+
CardanoWasm.Value.new(CardanoWasm.BigNum.from_str(utxo.amount))
127+
);
128+
129+
txBuilder.add_mint_asset_and_output_min_required_coin(
130+
mintScript,
131+
CardanoWasm.AssetName.new(Buffer.from(assetName)),
132+
CardanoWasm.Int.new_i32(1),
133+
CardanoWasm.TransactionOutputBuilder.new().with_address(addr).next()
134+
);
135+
136+
const policyId = Buffer.from(mintScript.hash().to_bytes()).toString("hex");
137+
138+
console.log(`POLICY_ID: ${policyId}`);
139+
140+
const metadata = {
141+
[policyId]: {
142+
[assetName]: {
143+
name: assetName,
144+
description,
145+
image: imageUrl,
146+
mediaType,
147+
},
148+
},
149+
};
150+
151+
console.log(`METADATA: ${JSON.stringify(metadata, null, 4)}`);
152+
153+
// transaction ttl can't be later than policy ttl
154+
const txTtl = ttl > policyTtl ? policyTtl : ttl;
155+
156+
console.log(`TX_TTL: ${txTtl}`);
157+
158+
txBuilder.set_ttl(txTtl);
159+
txBuilder.add_json_metadatum(
160+
CardanoWasm.BigNum.from_str("721"),
161+
JSON.stringify(metadata)
162+
);
163+
164+
txBuilder.add_change_if_needed(addr);
165+
166+
const txBody = txBuilder.build();
167+
const txHash = CardanoWasm.hash_transaction(txBody);
168+
169+
console.log(`TX_HASH: ${Buffer.from(txHash.to_bytes()).toString("hex")}`);
170+
171+
// sign the tx using the policy key and main key
172+
const witnesses = CardanoWasm.TransactionWitnessSet.new();
173+
const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new();
174+
vkeyWitnesses.add(CardanoWasm.make_vkey_witness(txHash, policy.privateKey));
175+
vkeyWitnesses.add(CardanoWasm.make_vkey_witness(txHash, privateKey));
176+
witnesses.set_vkeys(vkeyWitnesses);
177+
witnesses.set_native_scripts;
178+
const witnessScripts = CardanoWasm.NativeScripts.new();
179+
witnessScripts.add(mintScript);
180+
witnesses.set_native_scripts(witnessScripts);
181+
182+
const unsignedTx = txBuilder.build_tx();
183+
184+
// create signed transaction
185+
const tx = CardanoWasm.Transaction.new(
186+
unsignedTx.body(),
187+
witnesses,
188+
unsignedTx.auxiliary_data()
189+
);
190+
191+
const signedTx = Buffer.from(tx.to_bytes()).toString("base64");
192+
193+
// submit the transaction using yoroi backend
194+
try {
195+
const { data } = await axios.post(
196+
"https://testnet-backend.yoroiwallet.com/api/txs/signed",
197+
{
198+
signedTx,
199+
}
200+
);
201+
202+
console.log(`SUBMIT_RESULT: ${JSON.stringify(data, null, 4)}`);
203+
} catch (error) {
204+
console.error(
205+
`failed to submit tx via yoroi backend: ${error.toString()}. error details: ${JSON.stringify(
206+
error.response?.data
207+
)}`
208+
);
209+
}
210+
};
211+
212+
try {
213+
const privateKey = CardanoWasm.PrivateKey.from_bech32(
214+
//"ed25519_sk1fde2u8u2qme8uau5ac3w6c082gvtnmxt6uke2w8e07xwzewxee3q3n0f8e"
215+
"ed25519_sk18j0a6704zyerm6dsj6p2fp8juw5m43rfgk0y84jnm7w5khs4dpqquewh43"
216+
);
217+
218+
console.log(`PRIVATE KEY: ${privateKey.to_bech32()}`);
219+
220+
/*
221+
const policyPrivateKey = CardanoWasm.PrivateKey.from_bech32(
222+
"ed25519_sk1q96x2g66j5g7u5wydl7kcagk0h8upxznt3gj48h6njqthkyr7faqxmnnte"
223+
);
224+
*/
225+
226+
// import policy key from a .skey file
227+
const policyPrivateKey = CardanoWasm.PrivateKey.from_normal_bytes(
228+
cbor.decodeFirstSync(
229+
"582009ca7f508dd5a5f9823d367e98170f25606799f49ae7363a47a11d7d3502c91f"
230+
)
231+
);
232+
233+
console.log(`POLICY_PRIV_KEY: ${policyPrivateKey.to_bech32()}`);
234+
235+
await mintNft(
236+
privateKey, // main key
237+
{
238+
privateKey: policyPrivateKey, // policy key
239+
// pass null here to get automatic ttl for policy
240+
// and paste the POLICY_TTL output you get in console to here to mint with same policy
241+
ttl: null, // policy ttl
242+
},
243+
"asdNFT5", // assetName
244+
"some descr this is a new nft with same policy", // description
245+
"ipfs://QmNhmDPJMgdsFRM9HyiQEJqrKkpsWFshqES8mPaiFRq9Zk", // image url
246+
"image/jpeg" // mediaType
247+
);
248+
} catch (err) {
249+
console.error(`failed to mint nft: ${err.toString()}`);
250+
}
251+
```

0 commit comments

Comments
 (0)