From 8d569051f6549010b35d6a8b2ee5317aaffe9667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Kh=C3=A1nh?= <108068667+independenceee@users.noreply.github.com> Date: Tue, 19 Nov 2024 09:46:52 +0700 Subject: [PATCH 1/7] onchain conditional with remove address --- contract/script/txbuilder/cip68.txbuilder.ts | 8 +- contract/tests/cip68.test.ts | 126 +++++++-------- contract/validators/mint.ak | 152 +++++++++---------- contract/validators/store.ak | 67 ++++---- 4 files changed, 177 insertions(+), 176 deletions(-) diff --git a/contract/script/txbuilder/cip68.txbuilder.ts b/contract/script/txbuilder/cip68.txbuilder.ts index 53dccfc5..c0bd26a9 100644 --- a/contract/script/txbuilder/cip68.txbuilder.ts +++ b/contract/script/txbuilder/cip68.txbuilder.ts @@ -166,7 +166,7 @@ export class Cip68Contract extends MeshAdapter implements ICip68Contract { const unsignedTx = this.meshTxBuilder // .txIn(userUtxo.input.txHash, userUtxo.input.outputIndex) - .txIn(collateral.input.txHash, collateral.input.outputIndex) + // .txIn(collateral.input.txHash, collateral.input.outputIndex) .mintPlutusScriptV3() .mint(quantity, this.policyId, CIP68_222(stringToHex(assetName))) .mintingScript(this.mintScriptCbor) @@ -209,7 +209,6 @@ export class Cip68Contract extends MeshAdapter implements ICip68Contract { // ) .mintRedeemerValue(mConStr1([])) - .requiredSignerHash(deserializeAddress(walletAddress).pubKeyHash) .changeAddress(walletAddress) .selectUtxosFrom(utxos) @@ -218,7 +217,8 @@ export class Cip68Contract extends MeshAdapter implements ICip68Contract { collateral.input.outputIndex, collateral.output.amount, collateral.output.address, - ); + ) + .setNetwork(appNetwork); return unsignedTx.complete(); }; @@ -282,7 +282,7 @@ export class Cip68Contract extends MeshAdapter implements ICip68Contract { collateral.input.outputIndex, collateral.output.amount, collateral.output.address, - ); + ).setNetwork(appNetwork); return unsignedTx.complete(); }; diff --git a/contract/tests/cip68.test.ts b/contract/tests/cip68.test.ts index f4a007a4..0df8ff8d 100644 --- a/contract/tests/cip68.test.ts +++ b/contract/tests/cip68.test.ts @@ -34,90 +34,90 @@ describe("Mint, Burn, Update, Remove Assets (NFT/TOKEN) CIP68", function () { }); }); - test("Mint", async function () { - const cip68Contract: Cip68Contract = new Cip68Contract({ - fetcher: blockfrostProvider, - wallet: wallet, - meshTxBuilder: meshTxBuilder, - }); - - const unsignedTx: string = await cip68Contract.mint({ - assetName: "CIP68 Generators.", - metadata: { - name: "CIP68 Generators", - image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua", - mediaType: "image/jpg", - description: "Open source dynamic assets (Token/NFT) generator (CIP68)", - author: deserializeAddress(await wallet.getChangeAddress()).pubKeyHash, - }, - quantity: "1", - }); - const signedTx = await wallet.signTx(unsignedTx, true); - const txHash = await wallet.submitTx(signedTx); - console.log(txHash); - txHashTemp = txHash; - blockfrostProvider.onTxConfirmed(txHash, () => { - expect(txHash.length).toBe(64); - }); - }); - - // test("Burn", async function () { + // test("Mint", async function () { // const cip68Contract: Cip68Contract = new Cip68Contract({ // fetcher: blockfrostProvider, // wallet: wallet, // meshTxBuilder: meshTxBuilder, // }); - // const unsignedTx: string = await cip68Contract.burn({ + // const unsignedTx: string = await cip68Contract.mint({ // assetName: "CIP68 Generators.", - // txHash: - // "0249ca1df634c30e434f9f1e1b8f368ad06c51ac3e5e55e9869e1b321852c562", - // quantity: "-1", // metadata: { - // name: "CIP68 Generators 01", + // name: "CIP68 Generators", // image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua", // mediaType: "image/jpg", // description: "Open source dynamic assets (Token/NFT) generator (CIP68)", // author: deserializeAddress(await wallet.getChangeAddress()).pubKeyHash, // }, + // quantity: "1", // }); // const signedTx = await wallet.signTx(unsignedTx, true); // const txHash = await wallet.submitTx(signedTx); // console.log(txHash); // txHashTemp = txHash; - // jest.setTimeout(20000); - // expect(txHash.length).toBe(64); + // blockfrostProvider.onTxConfirmed(txHash, () => { + // expect(txHash.length).toBe(64); + // }); // }); - // test("Update", async function () { - // const cip68Contract: Cip68Contract = new Cip68Contract({ - // fetcher: blockfrostProvider, - // wallet: wallet, - // meshTxBuilder: meshTxBuilder, - // }); + test("Burn", async function () { + const cip68Contract: Cip68Contract = new Cip68Contract({ + fetcher: blockfrostProvider, + wallet: wallet, + meshTxBuilder: meshTxBuilder, + }); - // const unsignedTx: string = await cip68Contract.update({ - // assetName: "CIP68 Generators.", - // metadata: { - // name: "CIP68 Generators", - // image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua", - // mediaType: "image/jpg", - // description: "Open source dynamic assets (Token/NFT) generator (CIP68)", - // owner:await wallet.getChangeAddress(), - // author: deserializeAddress(await wallet.getChangeAddress()).pubKeyHash, - // }, - // txHash: - // "a38ab56346da11896822301dbed1439db8a07ac9cc5718f08d3fb31051848d8f", - // }); - // jest.setTimeout(20000); - // const signedTx = await wallet.signTx(unsignedTx, true); - // const txHash = await wallet.submitTx(signedTx); - // console.log(txHash); - // txHashTemp = txHash; - // jest.setTimeout(20000); - // expect(txHash.length).toBe(64); - // expect(txHash.length).toBe(64); - // }); + const unsignedTx: string = await cip68Contract.burn({ + assetName: "CIP68 Generators.", + txHash: + "20ed0f7af1af7e94d13d45b9aa0727f27435a10e9c0b5cb6fc85e4c8b27f4898", + quantity: "-1", + metadata: { + name: "CIP68 Generators 01", + image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua", + mediaType: "image/jpg", + description: "Open source dynamic assets (Token/NFT) generator (CIP68)", + author: deserializeAddress(wallet.getChangeAddress()).pubKeyHash, + }, + }); + const signedTx = await wallet.signTx(unsignedTx, true); + const txHash = await wallet.submitTx(signedTx); + console.log(txHash); + txHashTemp = txHash; + jest.setTimeout(20000); + expect(txHash.length).toBe(64); + }); + + test("Update", async function () { + const cip68Contract: Cip68Contract = new Cip68Contract({ + fetcher: blockfrostProvider, + wallet: wallet, + meshTxBuilder: meshTxBuilder, + }); + + const unsignedTx: string = await cip68Contract.update({ + assetName: "CIP68 Generators.", + metadata: { + name: "CIP68 Generators", + image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua", + mediaType: "image/jpg", + description: "Open source dynamic assets (Token/NFT) generator (CIP68)", + owner: wallet.getChangeAddress(), + author: deserializeAddress(wallet.getChangeAddress()).pubKeyHash, + }, + txHash: + "20ed0f7af1af7e94d13d45b9aa0727f27435a10e9c0b5cb6fc85e4c8b27f4898", + }); + jest.setTimeout(20000); + const signedTx = await wallet.signTx(unsignedTx, true); + const txHash = await wallet.submitTx(signedTx); + console.log(txHash); + txHashTemp = txHash; + jest.setTimeout(20000); + expect(txHash.length).toBe(64); + expect(txHash.length).toBe(64); + }); // test('Mint Reference Script', async function () { // const cip68Contract: Cip68Contract = new Cip68Contract({ diff --git a/contract/validators/mint.ak b/contract/validators/mint.ak index f4d653b8..f109b32e 100644 --- a/contract/validators/mint.ak +++ b/contract/validators/mint.ak @@ -10,91 +10,87 @@ use cip68generator/utils use types/cip68 use validation/find -// Validator: Mint -// Description: Valodator use mint and burn assets dynamic assets (Token/NFT) generator (CIP68) -// Parameters: exchange: Exchange wallet address, exchange_fee: Minimum price sent to the exchange, store: Store address wallet, - -// Mint Redeemer -// sign_by_author: +// @validator: Mint +// @title: Open source dynamic assets (Token/NFT) generator (CIP68) +// @description: Validator is used to mint and burn tokens (nft, token) according to CIP68 +// @params - exchange: The address when users operate with the platform, they will have to pay a platform maintenance fee. +// @params - exchange_fee: The minimum amount a user must pay to the exchange when interacting with the exchange. +// @params - store: The place to store reference tokens of user tokens in nfts minted according to CIP68 of the exchange. validator mint( exchange: VerificationKeyHash, exchange_fee: Int, store: ScriptHash, ) { mint(redeemer: MintRedeemer, policy_id: PolicyId, transaction: Transaction) { - True - // let Transaction { outputs, extra_signatories, mint, .. } = transaction - // let mint_flatten = - // mint - // |> without_lovelace() - // |> assets.flatten() - - // let reference_token_option = - // utils.token_prefix(mint_flatten, cip68.prefix_100) - // let user_token_option = utils.token_prefix(mint_flatten, cip68.prefix_222) - // let amount_mint_token: Int = list.length(mint_flatten) - // let exchange_address = address.from_verification_key(exchange) - // let output_utxo_exchange = - // utils.find_output(outputs, exchange_fee, exchange_address) - // let check_none_token = - // utils.check_none_token(user_token_option, reference_token_option) - - // when check_none_token is { - // True -> - // when (reference_token_option, user_token_option) is { - // (Some(reference_token), Some(user_token)) -> { - // let reference_value = - // assets.from_asset(policy_id, reference_token, 1) - // // let user_value = - // // assets.from_asset(policy_id, user_token, amount_mint_token - 1) - // let store_address = address.from_script(store) - // let output_utxo_store = - // find.output_by_addr_value(outputs, store_address, reference_value) - - // when redeemer is { - // Mint -> and { - // amount_mint_token >= 2, - // minting.exact(mint_flatten, policy_id, reference_token, 1)?, - // minting.exact( - // mint_flatten, - // policy_id, - // user_token, - // amount_mint_token - 1, - // )?, - // utils.check_output_utxo(output_utxo_store, extra_signatories)?, - // output_utxo_exchange != None, - // } - - // // bytearray.compare( - // // bytearray.drop(reference_token, 4), - // // bytearray.drop(user_token, 4), - // // ) == Equal, - // Burn -> and { - // minting.by_prefix( - // mint_flatten, - // policy_id, - // cip68.prefix_100, - // -1, - // )?, - // output_utxo_exchange != None, - // minting.by_prefix( - // mint_flatten, - // policy_id, - // cip68.prefix_222, - // -1, - // )?, - // } - // } - // } + let Transaction { outputs, extra_signatories, mint, .. } = transaction + let mint_flatten = + mint + |> without_lovelace() + |> assets.flatten() + let amount_mint_token: Int = list.length(mint_flatten) + let exchange_address = address.from_verification_key(exchange) + let user_token_option = utils.token_prefix(mint_flatten, cip68.prefix_222) + let reference_token_option = + utils.token_prefix(mint_flatten, cip68.prefix_100) + when redeemer is { + // @action: Mint - Conditions for minting assets + // - check_signed_by_author: When a user mints an asset, the datum of the store validator contains an author field and the user must provide a signature to identify that this is the author who minted the asset. This condition is fulfilled by comparing the signature entered in the datum of the store validator. + // - check_exchange_fee: Check in the transaction output that there exists an utxo containing the exchange's minimum fee and is sent to the exchange's address defined in params. + // - check_format_asset: Check that the specified fields such as name, image, media_type, author exist in the store validator's datum (ask asset metadata). These fields are required. + // - amount_tx_output: number of utxo output = 3 (địa chỉ exchange_fee, địa chỉ store validator và đỉa chỉ người nhận) + // - check_store_address: Check that the asset (CIP100) sent to an address must be the store validator's address + Mint -> { + let output_utxo_exchange = + utils.find_output(outputs, exchange_fee, exchange_address) + let check_none_token = + utils.check_none_token(user_token_option, reference_token_option) + when check_none_token is { + True -> + when (reference_token_option, user_token_option) is { + (Some(reference_token), Some(user_token)) -> { + let reference_value = + assets.from_asset(policy_id, reference_token, 1) + let store_address = address.from_script(store) + let output_utxo_store = + find.output_by_addr_value( + outputs, + store_address, + reference_value, + ) + and { + amount_mint_token >= 2, + minting.exact(mint_flatten, policy_id, reference_token, 1)?, + minting.exact( + mint_flatten, + policy_id, + user_token, + amount_mint_token - 1, + )?, + utils.check_output_utxo(output_utxo_store, extra_signatories)?, + output_utxo_exchange != None, + bytearray.compare( + bytearray.drop(reference_token, 4), + bytearray.drop(user_token, 4), + ) == Equal, + } + } + _ -> False + } + _ -> False + } + } - // // bytearray.compare( - // // bytearray.drop(reference_token, 4), - // // bytearray.drop(user_token, 4), - // // ) == Equal, - // _ -> False - // } - // _ -> False - // } + // @action: Burn - Conditions for burning assets (NFT/Token) + // - + Burn -> and { + // bytearray.compare( + // bytearray.drop(reference_token, 4), + // bytearray.drop(user_token, 4), + // ) == Equal, + minting.by_prefix(mint_flatten, policy_id, cip68.prefix_222, -1)?, + minting.by_prefix(mint_flatten, policy_id, cip68.prefix_100, -1)?, + } + } } else(_) { diff --git a/contract/validators/store.ak b/contract/validators/store.ak index 03d0dad4..6af161ae 100644 --- a/contract/validators/store.ak +++ b/contract/validators/store.ak @@ -9,6 +9,11 @@ use cip68generator/utils use types/cip68.{CIP68} use validation/find.{output_by_addr_value} +// @validator: Store +// @title: Open source dynamic assets (Token/NFT) generator (CIP68) +// @description: Validator is used to update and remove tokens (nft, token) according to CIP68 +// @params - exchange: The address when users operate with the platform, they will have to pay a platform maintenance fee. +// @params - exchange_fee: The minimum amount a user must pay to the exchange when interacting with the exchange. validator store(exchange: VerificationKeyHash, exchange_fee: Int) { spend( datum: Option, @@ -16,37 +21,37 @@ validator store(exchange: VerificationKeyHash, exchange_fee: Int) { output_reference: OutputReference, transaction: Transaction, ) { - True - // expect Some(datum_output) = datum - // let Transaction { inputs, outputs, extra_signatories, .. } = transaction - // let exchange_address = address.from_verification_key(exchange) - // let output_utxo_exchange = - // utils.find_output(outputs, exchange_fee, exchange_address) - // expect Some(input) = find_input(inputs, output_reference) - // let script_address = input.output.address - // let reference_token = - // input.output.value - // |> without_lovelace() - // let validator_output = - // output_by_addr_value(outputs, script_address, reference_token) - // expect InlineDatum(datum_input) = validator_output.datum - // let meradatum_output: CIP68 = datum_output - // expect metadatum_input: CIP68 = datum_input - // expect author_input: ByteArray = cip68.get(metadatum_input, "author") - // expect author_output: ByteArray = cip68.get(meradatum_output, "author") - // let check_author = author_input == author_output - - // when redeemer is { - // Update -> and { - // utils.check_output_utxo(validator_output, extra_signatories)?, - // output_utxo_exchange != None, - // check_author, - // } - // Remove -> and { - // utils.check_output_utxo(validator_output, extra_signatories)?, - // output_utxo_exchange != None, - // } - // } + expect Some(datum_output) = datum + let Transaction { inputs, outputs, extra_signatories, .. } = transaction + let exchange_address = address.from_verification_key(exchange) + let output_utxo_exchange = + utils.find_output(outputs, exchange_fee, exchange_address) + when redeemer is { + Update -> { + expect Some(input) = find_input(inputs, output_reference) + let script_address = input.output.address + let reference_token = + input.output.value + |> without_lovelace() + let validator_output = + output_by_addr_value(outputs, script_address, reference_token) + expect InlineDatum(datum_input) = validator_output.datum + let meradatum_output: CIP68 = datum_output + expect metadatum_input: CIP68 = datum_input + expect author_input: ByteArray = cip68.get(metadatum_input, "author") + expect author_output: ByteArray = cip68.get(meradatum_output, "author") + let check_author = author_input == author_output + and { + utils.check_output_utxo(validator_output, extra_signatories)?, + output_utxo_exchange != None, + check_author, + } + } + Remove -> and { + True, + True, + } + } } else(_) { From fe46c1708388c0ad4f71c60a0f173e9f155c7d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Kh=C3=A1nh?= <108068667+independenceee@users.noreply.github.com> Date: Tue, 19 Nov 2024 14:12:54 +0700 Subject: [PATCH 2/7] feature: mint asset successfully --- contract/lib/cip68generator/utils.ak | 10 +++ contract/script/txbuilder/cip68.txbuilder.ts | 27 +------ contract/tests/cip68.test.ts | 84 ++++++++++---------- contract/validators/mint.ak | 16 ++-- contract/validators/store.ak | 23 ++++-- 5 files changed, 80 insertions(+), 80 deletions(-) diff --git a/contract/lib/cip68generator/utils.ak b/contract/lib/cip68generator/utils.ak index 979fb6b4..1c642fbf 100644 --- a/contract/lib/cip68generator/utils.ak +++ b/contract/lib/cip68generator/utils.ak @@ -108,3 +108,13 @@ pub fn check_address_duplicate( pub fn check_amount(output: Output, price: Int) -> Bool { lovelace_of(output.value) >= price } + +// Check the output utxo containing the reference nft +pub fn check_output_utxo_reference_token(output: Output) -> Bool { + let output_value = + output.value + |> without_lovelace() + |> flatten() + // Check no other token is present in the utxo containing reference nft + list.length(output_value) == 1 +} diff --git a/contract/script/txbuilder/cip68.txbuilder.ts b/contract/script/txbuilder/cip68.txbuilder.ts index 5117c6f5..7f1896fd 100644 --- a/contract/script/txbuilder/cip68.txbuilder.ts +++ b/contract/script/txbuilder/cip68.txbuilder.ts @@ -165,15 +165,10 @@ export class Cip68Contract extends MeshAdapter implements ICip68Contract { console.log(storeUtxo.output); const unsignedTx = this.meshTxBuilder - // .txIn(userUtxo.input.txHash, userUtxo.input.outputIndex) - // .txIn(collateral.input.txHash, collateral.input.outputIndex) + .mintPlutusScriptV3() .mint(quantity, this.policyId, CIP68_222(stringToHex(assetName))) .mintingScript(this.mintScriptCbor) - // .mintTxInReference( - // mintUtxoRef.input.txHash, - // mintUtxoRef.input.outputIndex, - // ) .mintRedeemerValue(mConStr1([])) .spendingPlutusScriptV3() @@ -181,18 +176,8 @@ export class Cip68Contract extends MeshAdapter implements ICip68Contract { .txInInlineDatumPresent() .txInRedeemerValue(mConStr1([])) .txInScript(this.storeScriptCbor) - // .spendingTxInReference( - // storeUtxoRef.input.txHash, - // storeUtxoRef.input.outputIndex, - // ) - // .txOut(walletAddress, [ - // { - // unit: "lovelave", - // quantity: storeUtxo.output.amount[0].quantity, - // }, - // ]) - .txOutInlineDatumValue(metadataToCip68(metadata)) + // .txOutInlineDatumValue(metadataToCip68(metadata)) .txOut(EXCHANGE_FEE_ADDRESS, [ { unit: "lovelace", @@ -203,12 +188,8 @@ export class Cip68Contract extends MeshAdapter implements ICip68Contract { .mintPlutusScriptV3() .mint(quantity, this.policyId, CIP68_100(stringToHex(assetName))) .mintingScript(this.mintScriptCbor) - // .mintTxInReference( - // mintUtxoRef.input.txHash, - // mintUtxoRef.input.outputIndex, - // ) - .mintRedeemerValue(mConStr1([])) + .requiredSignerHash(deserializeAddress(walletAddress).pubKeyHash) .changeAddress(walletAddress) .selectUtxosFrom(utxos) @@ -266,7 +247,7 @@ export class Cip68Contract extends MeshAdapter implements ICip68Contract { .txOutInlineDatumValue(metadataToCip68(metadata)) .txOut( - "addr_test1qzwu6jcqk8f96fxq02pvq2h4a927ggn35f2gzdklfte4kwx0sd5zdvsat2chsyyjxkjxcg6uz2y46avd46mzqdgdy3dsckqxs4", + EXCHANGE_FEE_ADDRESS, [ { unit: "lovelace", diff --git a/contract/tests/cip68.test.ts b/contract/tests/cip68.test.ts index 53915568..9b79a208 100644 --- a/contract/tests/cip68.test.ts +++ b/contract/tests/cip68.test.ts @@ -34,88 +34,88 @@ describe("Mint, Burn, Update, Remove Assets (NFT/TOKEN) CIP68", function () { }); }); - // test("Mint", async function () { - // const cip68Contract: Cip68Contract = new Cip68Contract({ - // fetcher: blockfrostProvider, - // wallet: wallet, - // meshTxBuilder: meshTxBuilder, - // }); - - // const unsignedTx: string = await cip68Contract.mint({ - // assetName: "CIP68 Generators.", - // metadata: { - // name: "CIP68 Generators", - // image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua", - // mediaType: "image/jpg", - // description: "Open source dynamic assets (Token/NFT) generator (CIP68)", - // author: deserializeAddress(await wallet.getChangeAddress()).pubKeyHash, - // }, - // quantity: "1", - // }); - // const signedTx = await wallet.signTx(unsignedTx, true); - // const txHash = await wallet.submitTx(signedTx); - // console.log(txHash); - // txHashTemp = txHash; - // blockfrostProvider.onTxConfirmed(txHash, () => { - // expect(txHash.length).toBe(64); - // }); - // }); - - test("Burn", async function () { + test("Mint", async function () { const cip68Contract: Cip68Contract = new Cip68Contract({ fetcher: blockfrostProvider, wallet: wallet, meshTxBuilder: meshTxBuilder, }); - const unsignedTx: string = await cip68Contract.burn({ + const unsignedTx: string = await cip68Contract.mint({ assetName: "CIP68 Generators.", - txHash: - "20ed0f7af1af7e94d13d45b9aa0727f27435a10e9c0b5cb6fc85e4c8b27f4898", - quantity: "-1", metadata: { - name: "CIP68 Generators 01", + name: "CIP68 Generators", image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua", mediaType: "image/jpg", description: "Open source dynamic assets (Token/NFT) generator (CIP68)", - author: deserializeAddress(wallet.getChangeAddress()).pubKeyHash, + author: deserializeAddress(await wallet.getChangeAddress()).pubKeyHash, }, + quantity: "1", }); const signedTx = await wallet.signTx(unsignedTx, true); const txHash = await wallet.submitTx(signedTx); console.log(txHash); txHashTemp = txHash; - jest.setTimeout(20000); - expect(txHash.length).toBe(64); + blockfrostProvider.onTxConfirmed(txHash, () => { + expect(txHash.length).toBe(64); + }); }); - test("Update", async function () { + test("Burn", async function () { const cip68Contract: Cip68Contract = new Cip68Contract({ fetcher: blockfrostProvider, wallet: wallet, meshTxBuilder: meshTxBuilder, }); - const unsignedTx: string = await cip68Contract.update({ + const unsignedTx: string = await cip68Contract.burn({ assetName: "CIP68 Generators.", + txHash: + "ac8ef3e9801700f3f0ea96a3d69a98ec122c4e5c463705cf2d165d746ff21d7a", + quantity: "-1", metadata: { - name: "CIP68 Generators", + name: "CIP68 Generators 01", image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua", mediaType: "image/jpg", description: "Open source dynamic assets (Token/NFT) generator (CIP68)", - owner: wallet.getChangeAddress(), author: deserializeAddress(wallet.getChangeAddress()).pubKeyHash, }, - txHash: - "20ed0f7af1af7e94d13d45b9aa0727f27435a10e9c0b5cb6fc85e4c8b27f4898", }); const signedTx = await wallet.signTx(unsignedTx, true); const txHash = await wallet.submitTx(signedTx); console.log(txHash); txHashTemp = txHash; + jest.setTimeout(20000); expect(txHash.length).toBe(64); }); + // test("Update", async function () { + // const cip68Contract: Cip68Contract = new Cip68Contract({ + // fetcher: blockfrostProvider, + // wallet: wallet, + // meshTxBuilder: meshTxBuilder, + // }); + + // const unsignedTx: string = await cip68Contract.update({ + // assetName: "CIP68 Generators.", + // metadata: { + // name: "CIP68 Generators", + // image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua", + // mediaType: "image/jpg", + // description: "Open source dynamic assets (Token/NFT) generator (CIP68)", + // owner: wallet.getChangeAddress(), + // author: deserializeAddress(wallet.getChangeAddress()).pubKeyHash, + // }, + // txHash: + // "e50c51590fd74dc4eb0aa7f7e275eccae26833878b3125d1db71ed5e351d8671", + // }); + // const signedTx = await wallet.signTx(unsignedTx, true); + // const txHash = await wallet.submitTx(signedTx); + // console.log(txHash); + // txHashTemp = txHash; + // expect(txHash.length).toBe(64); + // }); + // test('Mint Reference Script', async function () { // const cip68Contract: Cip68Contract = new Cip68Contract({ // fetcher: blockfrostProvider, diff --git a/contract/validators/mint.ak b/contract/validators/mint.ak index f109b32e..31439ceb 100644 --- a/contract/validators/mint.ak +++ b/contract/validators/mint.ak @@ -32,6 +32,8 @@ validator mint( let user_token_option = utils.token_prefix(mint_flatten, cip68.prefix_222) let reference_token_option = utils.token_prefix(mint_flatten, cip68.prefix_100) + let output_utxo_exchange = + utils.find_output(outputs, exchange_fee, exchange_address) when redeemer is { // @action: Mint - Conditions for minting assets // - check_signed_by_author: When a user mints an asset, the datum of the store validator contains an author field and the user must provide a signature to identify that this is the author who minted the asset. This condition is fulfilled by comparing the signature entered in the datum of the store validator. @@ -40,8 +42,6 @@ validator mint( // - amount_tx_output: number of utxo output = 3 (địa chỉ exchange_fee, địa chỉ store validator và đỉa chỉ người nhận) // - check_store_address: Check that the asset (CIP100) sent to an address must be the store validator's address Mint -> { - let output_utxo_exchange = - utils.find_output(outputs, exchange_fee, exchange_address) let check_none_token = utils.check_none_token(user_token_option, reference_token_option) when check_none_token is { @@ -83,11 +83,13 @@ validator mint( // @action: Burn - Conditions for burning assets (NFT/Token) // - Burn -> and { - // bytearray.compare( - // bytearray.drop(reference_token, 4), - // bytearray.drop(user_token, 4), - // ) == Equal, - minting.by_prefix(mint_flatten, policy_id, cip68.prefix_222, -1)?, + output_utxo_exchange != None, + minting.by_prefix( + mint_flatten, + policy_id, + cip68.prefix_222, + -(amount_mint_token - 1), + )?, minting.by_prefix(mint_flatten, policy_id, cip68.prefix_100, -1)?, } } diff --git a/contract/validators/store.ak b/contract/validators/store.ak index 6af161ae..5da6c03e 100644 --- a/contract/validators/store.ak +++ b/contract/validators/store.ak @@ -4,6 +4,7 @@ use cardano/assets.{without_lovelace} use cardano/transaction.{ InlineDatum, Output, OutputReference, Transaction, find_input, } +use cardano/tx use cip68generator/types.{Remove, StoreRedeemer, Update} use cip68generator/utils use types/cip68.{CIP68} @@ -26,13 +27,13 @@ validator store(exchange: VerificationKeyHash, exchange_fee: Int) { let exchange_address = address.from_verification_key(exchange) let output_utxo_exchange = utils.find_output(outputs, exchange_fee, exchange_address) + expect Some(input) = find_input(inputs, output_reference) + let script_address = input.output.address + let reference_token = + input.output.value + |> without_lovelace() when redeemer is { Update -> { - expect Some(input) = find_input(inputs, output_reference) - let script_address = input.output.address - let reference_token = - input.output.value - |> without_lovelace() let validator_output = output_by_addr_value(outputs, script_address, reference_token) expect InlineDatum(datum_input) = validator_output.datum @@ -47,10 +48,16 @@ validator store(exchange: VerificationKeyHash, exchange_fee: Int) { check_author, } } - Remove -> and { - True, - True, + Remove -> { + expect InlineDatum(datum_input) = input.output.datum + expect metadatum_input: CIP68 = datum_input + expect author_input: ByteArray = cip68.get(metadatum_input, "author") + // let validating_output = output_by_value(outputs, reference_token) + and { + tx.verify_signature(extra_signatories, author_input), + output_utxo_exchange != None, } + } } } From d70b8a9955d6ceee7f8422da6960a03b47fe69b3 Mon Sep 17 00:00:00 2001 From: tidvn Date: Tue, 19 Nov 2024 17:06:30 +0700 Subject: [PATCH 3/7] chore: update compiler version from v1.1.0 to v1.1.6 in aiken.toml --- contract/aiken.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/aiken.toml b/contract/aiken.toml index 46ddc800..564a76f7 100644 --- a/contract/aiken.toml +++ b/contract/aiken.toml @@ -1,6 +1,6 @@ name = "cardano2vn/cip68generator" version = "0.0.0" -compiler = "v1.1.0" +compiler = "v1.1.6" plutus = "v3" license = "Apache-2.0" description = "Aiken contracts for project 'cardano2vn/cip68generator'" From f8a7129994e88a6ef1244bcc76816349f2ecaddc Mon Sep 17 00:00:00 2001 From: tidvn Date: Tue, 19 Nov 2024 17:31:00 +0700 Subject: [PATCH 4/7] feat: add Docker Compose configuration and update Dockerfile for Prisma integration --- Dockerfile | 22 +++++----------------- docker-compose.yaml | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 docker-compose.yaml diff --git a/Dockerfile b/Dockerfile index a34a9d82..4a1cc07f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,23 +5,17 @@ FROM base AS deps RUN apk add --no-cache libc6-compat WORKDIR /app -# Install dependencies based on the preferred package manager -COPY package.json package-lock.json* ./ -RUN npm install -force; - +COPY package.json ./ +RUN npm install -force +RUN npm install -g prisma - -# Rebuild the source code only when needed +# # Rebuild the source code only when needed FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . -# Next.js collects completely anonymous telemetry data about general usage. -# Learn more here: https://nextjs.org/telemetry -# Uncomment the following line in case you want to disable telemetry during the build. -# ENV NEXT_TELEMETRY_DISABLED=1 - +RUN npx prisma generate RUN npm run build # Production image, copy all the files and run next @@ -29,8 +23,6 @@ FROM base AS runner WORKDIR /app ENV NODE_ENV=production -# Uncomment the following line in case you want to disable telemetry during runtime. -# ENV NEXT_TELEMETRY_DISABLED=1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs @@ -41,8 +33,6 @@ COPY --from=builder /app/public ./public RUN mkdir .next RUN chown nextjs:nodejs .next -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static @@ -52,7 +42,5 @@ EXPOSE 3000 ENV PORT=3000 -# server.js is created by next build from the standalone output -# https://nextjs.org/docs/pages/api-reference/next-config-js/output ENV HOSTNAME="0.0.0.0" CMD ["node", "server.js"] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..0750a315 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,33 @@ +services: + postgres: + container_name: cip68_postgres + image: postgres:latest + env_file: + - stack.env + volumes: + - cip68:/data/postgres + # ports: + # - "5432:5432" + networks: + - cip68 + restart: unless-stopped + + cip68_webapp: + container_name: cip68_webapp + env_file: + - ./stack.env + build: + context: . + dockerfile: Dockerfile + ports: + - "3785:3000" + depends_on: + - postgres + networks: + - cip68 + restart: always +networks: + cip68: + driver: bridge +volumes: + cip68: From 8936b2b6538779475132e7d270564eb60a50a39c Mon Sep 17 00:00:00 2001 From: tidvn Date: Tue, 19 Nov 2024 17:32:15 +0700 Subject: [PATCH 5/7] fix: uncomment ports configuration in Docker Compose for PostgreSQL service --- docker-compose.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 0750a315..d550ed48 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,8 +6,8 @@ services: - stack.env volumes: - cip68:/data/postgres - # ports: - # - "5432:5432" + ports: + - "5432:5432" networks: - cip68 restart: unless-stopped From d16e82e41d68cfacd66307d8da728e0c1cd07503 Mon Sep 17 00:00:00 2001 From: tidvn Date: Tue, 19 Nov 2024 17:43:41 +0700 Subject: [PATCH 6/7] chore: update .gitignore to include stack.env and remove unused environment variables from app-environment.ts --- .gitignore | 3 ++- src/constants/app-environment.ts | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 7002d942..41fc2bb9 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,5 @@ build/ docs/ plutus.json -yarn.lock \ No newline at end of file +yarn.lock +stack.env \ No newline at end of file diff --git a/src/constants/app-environment.ts b/src/constants/app-environment.ts index 22fd3a07..28f75af8 100644 --- a/src/constants/app-environment.ts +++ b/src/constants/app-environment.ts @@ -9,8 +9,6 @@ const appNetwork: Network = const appNetworkId = appNetwork === "mainnet" ? 1 : 0; -const BACKEND_URL = process.env.BACKEND_URL || ""; -const CONTEXT_PATH = process.env.CONTEXT_PATH || "/api"; const IPFS_ENDPOINT = process.env.IPFS_ENDPOINT || ""; const IPFS_GATEWAY = process.env.NEXT_PUBLIC_IPFS_GATEWAY || "https://ipfs.io/"; export { @@ -18,8 +16,6 @@ export { appNetworkId, BLOCKFROST_API_KEY, KOIOS_RPC_URL, - BACKEND_URL, - CONTEXT_PATH, IPFS_ENDPOINT, IPFS_GATEWAY, }; From af0933ea28c59d9ba5161a4aea388c67769a98a0 Mon Sep 17 00:00:00 2001 From: tidvn Date: Tue, 19 Nov 2024 17:45:56 +0700 Subject: [PATCH 7/7] feat: replace docker-compose.yaml with docker-compose.yml for improved configuration management --- docker-compose.yaml => docker-compose.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docker-compose.yaml => docker-compose.yml (100%) diff --git a/docker-compose.yaml b/docker-compose.yml similarity index 100% rename from docker-compose.yaml rename to docker-compose.yml