-
Notifications
You must be signed in to change notification settings - Fork 479
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make the auction example end-to-end (#6477)
- Loading branch information
Showing
28 changed files
with
980 additions
and
468 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"label": "Example: An Auction Smart Contract", | ||
"position": 15, | ||
"link": { | ||
"type": "generated-index", | ||
"description": "In this example we first present the Plutus Tx code for writing the on-chain validator script of a smart contract that controls the auction of an asset, which can be executed on the Cardano blockchain. We will then walk you through the steps to run it end-to-end on Cardano's Preview testnet." | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
doc/docusaurus/docs/auction-smart-contract/end-to-end/_category_.json
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,8 @@ | ||
{ | ||
"label": "End to end", | ||
"position": 10, | ||
"link": { | ||
"type": "generated-index", | ||
"description": "We will now demonstrate the process of running the auction example end-to-end on Cardano's Preview testnet." | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
doc/docusaurus/docs/auction-smart-contract/end-to-end/closing-the-auction.md
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,15 @@ | ||
--- | ||
sidebar_position: 25 | ||
--- | ||
|
||
# Closing the Auction | ||
|
||
Once the auction's end time has elapsed, a transaction can be submitted to finalize the auction, distributing the token and the highest bid accordingly. | ||
This transaction needs to do the following: | ||
|
||
- Spend the UTxO that contains the token being auctioned. | ||
- If no bids were placed (which can be determined by examining the datum attached to the UTxO), the token should be returned to the seller's address. | ||
- If at least one bid was placed, the token should be transferred to the highest bidder's address, and the highest bid amount should be sent to the seller's address. | ||
- Set a validity interval that starts no earlier than the auction's end time. | ||
|
||
The off-chain code for building and submitting this transaction will be very similar to the code for the bidding transactions, so the details are left as an exercise. |
45 changes: 45 additions & 0 deletions
45
doc/docusaurus/docs/auction-smart-contract/end-to-end/generating-keys.md
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,45 @@ | ||
--- | ||
sidebar_position: 5 | ||
--- | ||
|
||
# Generating Keys and Addresses | ||
|
||
To start, clone the plutus-tx-template repository into the `on-chain` directory. | ||
Make sure to have [NodeJS](https://nodejs.org/en) and [yarn](https://yarnpkg.com/) (or [npm](https://github.com/npm/cli), which comes bundled with NodeJS) installed. Then, create a separate `off-chain` directory, set up `package.json`, and add the required dependencies: | ||
|
||
``` | ||
git clone git@github.com:IntersectMBO/plutus-tx-template.git on-chain | ||
mkdir off-chain && cd $_ | ||
yarn init -y | ||
yarn add @meshsdk/core | ||
yarn add cbor | ||
``` | ||
|
||
We recommend using the Nix shell that comes with `plutus-tx-template` to run this example. | ||
The Nix shell provides the correct versions of all dependencies, including GHC, Cabal, Node.js, and various C libraries. | ||
To enter the nix shell, run | ||
|
||
``` | ||
nix develop on-chain/ | ||
``` | ||
|
||
The first run of `nix develop` may take some time so please be patient. | ||
|
||
We'll use [mesh](https://meshjs.dev/), a JavaScript framework, for writing off-chain code. | ||
We'll use [Blockfrost](https://blockfrost.io/) as the blockchain provider, to avoid the need of running a local node. | ||
If you don't have a Blockfrost account, you can sign up for one, and create a project for the Preview network. | ||
|
||
The first step is to generate keys and addresses for the seller and the bidders. | ||
Add a new file named `off-chain/generate-keys.mjs`, with the following content: | ||
|
||
<LiteralInclude file="generate-keys.mjs" language="javascript" title="generate-keys.mjs" /> | ||
|
||
Then, generate keys and addresses for one seller and two bidders by running: | ||
|
||
``` | ||
node generate-keys.mjs seller | ||
node generate-keys.mjs bidder1 | ||
node generate-keys.mjs bidder2 | ||
``` | ||
|
||
This will create three files for each participant (seller, bidder1, and bidder2): a `.skey` file that contains a secret key, a `.addr` file that contains the corresponding wallet address, and a `.pkh` file that contains the corresponding public key hash. |
29 changes: 29 additions & 0 deletions
29
doc/docusaurus/docs/auction-smart-contract/end-to-end/getting-funds.md
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,29 @@ | ||
--- | ||
sidebar_position: 10 | ||
--- | ||
|
||
# Getting Funds from the Faucet | ||
|
||
Next, we'll need to fund the wallet of each participant (seller, bidder1 and bidder2), in order to cover transaction fees and place bids. | ||
We can get funds from Cardano's [testnet faucet](https://docs.cardano.org/cardano-testnets/tools/faucet/). | ||
|
||
To request funds, enter the seller's address into the address field and click "request funds." | ||
This will deposit 10,000 (test) ADA into the seller's wallet. | ||
Make sure you select the correct network (Preview). | ||
|
||
Since the faucet limits how frequently you can request funds, and 10,000 ADA is more than sufficient for this example, we'll share the 10,000 ADA among the seller, bidder1, and bidder2. | ||
To do so, create a file named `off-chain/send-lovelace.mjs` with the following content: | ||
|
||
<LiteralInclude file="send-lovelace.mjs" language="javascript" title="send-lovelace.mjs" /> | ||
|
||
Substitute your Blockfrost project ID for `Replace with Blockfrost Project ID`. | ||
|
||
This Javascript module builds and submits a transaction that sends 1 billion Lovelace (equivalent to 1000 Ada) from the seller's wallet to the specified recipient. | ||
Run the following commands: | ||
|
||
``` | ||
node send-lovelace.mjs bidder1 | ||
node send-lovelace.mjs bidder2 | ||
``` | ||
|
||
After the transactions are confirmed and included in a block (usually within a minute), bidder1's and bidder2's wallets should each have 1000 Ada, and the seller's wallet should have approximately 8000 Ada (minus transaction fees), which you can verify on [Cardanoscan](https://preview.cardanoscan.io/). |
91 changes: 91 additions & 0 deletions
91
doc/docusaurus/docs/auction-smart-contract/end-to-end/mint.md
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,91 @@ | ||
--- | ||
sidebar_position: 15 | ||
--- | ||
|
||
# Minting the Token to be Auctioned | ||
|
||
Before we can start the auction, we need to mint a token to be auctioned. | ||
To do so, we first must determine the token's currency symbol and name. | ||
To mint or burn tokens with a specific currency symbol (`currencySymbol`), a Plutus script whose hash matches `currencySymbol` must be provided, and is used to validate the minting or burning action. | ||
Therefore, we'll first write the on-chain script, then compute its hash and use it as the currency symbol. | ||
|
||
## On-chain Minting Policy Script | ||
|
||
The full minting policy code can be found at [AuctionMintingPolicy.hs](https://github.com/IntersectMBO/plutus-tx-template/blob/main/src/AuctionMintingPolicy.hs). | ||
The main logic is in the following function: | ||
|
||
<LiteralInclude file="AuctionMintingPolicy.hs" language="haskell" title="AuctionMintingPolicy.hs" start="-- BLOCK1" end="-- BLOCK2" /> | ||
|
||
This script will pass if the following two conditions are met: | ||
|
||
1. The transaction is signed by a specific public key. | ||
2. The transaction mints exactly one token, whose currency symbol matches the script's hash (i.e., `ownCurrencySymbol ctx`). | ||
The token name can be anything. | ||
|
||
> :pushpin: **NOTE** | ||
> | ||
> A token minted in this way is _not_ considered a non-fungible token (NFT) because, while only one token can be minted in a single transaction, multiple transactions can mint additional tokens with the same currency symbol and token name. | ||
> To create a truly unique token, you would need a more complex minting policy, but for simplicity, that is not covered here. | ||
## Compile and Generate Blueprint for the Minting Policy | ||
|
||
Next, we need to compile the minting policy script and create its blueprint. | ||
To do so, we first need to supply a public key hash, which the minting policy will use for checking condition 1 above. | ||
Assuming the seller is the one minting the token, this should be the seller's public key hash. | ||
Open `GenMintingPolicyBlueprint.hs` in the `on-chain` directory, and replace `error "Replace with seller pkh"` with the content of `off-chain/seller.pkh`. | ||
|
||
The minting policy code comes with `plutus-tx-template`, so you can find it in the `on-chain` repository. | ||
To compile it and generate the blueprint, navigate to the `on-chain` directory and run | ||
|
||
``` | ||
cabal run gen-minting-policy-blueprint -- ../off-chain/plutus-auction-minting-policy.json | ||
``` | ||
|
||
You may need to run `cabal update` before executing this command for the first time. | ||
|
||
This should produce a blueprint file named `off-chain/plutus-auction-minting-policy.json`. | ||
|
||
## Compile and Generate Blueprint for the Auction Validator | ||
|
||
One final step before minting the token: since we want to lock the minted token at the script address corresponding to the auction validator, | ||
we must supply the parameters (i.e., `AuctionParams`) to the auction validator, compile the auction validator, and calculate its script address. | ||
|
||
Open `GenAuctionValidatorBlueprint.hs` in the `on-chain` directory, and replace all placeholders: | ||
- Replace `error "Replace with sellerh pkh"` with the content of `off-chain/seller.pkh`. | ||
- Replace `error "Replace with currency symbol"` with the minting policy hash, which you can find in the `hash` field in `off-chain/plutus-auction-minting-policy.json`. | ||
- Replace `error "Replace with the auction's end time"` with a POSIX timestamp for a time in the near future (say 24 hours from now). | ||
Note that the POSIX timestamp in Plutus is the number of _milliseconds_, rather than seconds, elapsed since January 1, 1970. | ||
In other words, add three zeros to the usual POSIX timestamp. | ||
For instance, the POSIX timestamp of September 1, 2024, 21:44:51 UTC, is 1725227091000. | ||
|
||
Then, navigate to the `on-chain` directory and run | ||
|
||
``` | ||
cabal run gen-auction-validator-blueprint -- ../off-chain/plutus-auction-validator.json | ||
``` | ||
|
||
This will generate a blueprint file named `off-chain/plutus-auction-validator.json`, which the off-chain code can read and calculate the auction validator's script address. | ||
|
||
|
||
## Off-chain Code for Minting | ||
|
||
We are now ready to write and execute the off-chain code for minting. | ||
Create a file named `off-chain/mint-token-for-auction.mjs` with the following content: | ||
|
||
<LiteralInclude file="mint-token-for-auction.mjs" language="javascript" title="mint-token-for-auction.mjs" /> | ||
|
||
Substitute your Blockfrost project ID for `Replace with Blockfrost Project ID`. | ||
|
||
This Javascript module uses the mesh library to build a transaction that mints a token (`tx.mintAsset`). | ||
The token will have the currency symbol of the minting policy's hash, and a token name of `TokenToBeAuctioned`. | ||
It will be sent to `auctionValidatorAddress`, with a datum corresponding to `Nothing`. | ||
The transaction is signed by the seller (`seller.skey`), and then submitted to the Preview testnet. | ||
|
||
Run the coding using: | ||
|
||
``` | ||
node mint-token-for-auction.mjs | ||
``` | ||
|
||
and you should see a message "Minted a token at address ..." printed in the console. | ||
Within a minute, you should be able to find the transaction using the transaction hash on [Cardanoscan](https://preview.cardanoscan.io/) and review its details. |
58 changes: 58 additions & 0 deletions
58
doc/docusaurus/docs/auction-smart-contract/end-to-end/placing-bids.md
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,58 @@ | ||
--- | ||
sidebar_position: 20 | ||
--- | ||
|
||
# Placing Bids | ||
|
||
Now we can start bidding. | ||
Let's place a bid of 100 Ada from bidder1, followed by a bid of 200 Ada from bidder2. | ||
Each transaction that places a bid must do the following: | ||
|
||
- Spend the UTxO that contains the token being auctioned. | ||
For bidder1, the transaction that produced the UTxO is the one that minted the token. | ||
For bidder2, the transaction that produced the UTxO is bidder1's transaction. | ||
The address of this UTxO is always the auction validator's script address, so each bidding transaction must include the auction validator and a redeemer[^1]. | ||
- Place a bid (via the redeemer) with an amount at least as high as the auction's minimum bid, and higher than the previous highest bid (if any). | ||
The existence and the details of a previous highest bid can be determined by inspecting the datum attached to the aforementioned UTxO. | ||
This is enforced by the auction validator's `sufficientBid` condition. | ||
- Lock the token being auctioned, together with the bid amount, in a new UTxO at the auction validator's script address. | ||
The new UTxO should also include a datum containing the details of this bid. | ||
This is enforced by the auction validator's `correctOutput` condition. | ||
- Refund the previous highest bid (if any) to its bidder's wallet address. | ||
This is enforced by the auction validator's `refundsPreviousHighestBid` condition. | ||
- Set a validity interval that ends no later than the auction's end time. | ||
This is enforced by the auction validator's `validBidTime` condition. | ||
|
||
To submit these bidding transactions, create a file named `off-chain/bid.mjs` for the off-chain code, with the following content: | ||
|
||
<LiteralInclude file="bid.mjs" language="javascript" title="bid.mjs" /> | ||
|
||
This Javascript module builds and submits a transaction that does exactly the above. | ||
|
||
The following substitutions are needed: | ||
|
||
- Substitute your Blockfrost project ID for `Replace with Blockfrost Project ID`. | ||
- Substitute a slot number no later than the auction's end time for `Replace with transaction expiration time`. | ||
For instance, if you set the auction's end time to be approximately 24 hours from now, you can use a slot number corresponding to approximately 12 hours from now. | ||
To determine the slot nmber, go to [Cardanoscan](https://preview.cardanoscan.io/), click on a recent transaction, take its Absolute Slot, and add 12 hours (43200) to it. | ||
|
||
Place the first bid by running | ||
|
||
``` | ||
node bid.mjs <minting-transaction-hash> bidder1 100000000 | ||
``` | ||
|
||
Replace `<minting-transaction-hash>` with the hash of the transaction we previously submitted for minting the token. | ||
This hash is used by the off-chain code to locate the UTxO that contains the token. | ||
|
||
After the first bidding transaction is confirmed, we can submit the second bid from bidder2, with a similar command: | ||
|
||
``` | ||
node bid.mjs <bidder1-transaction-hash> bidder2 200000000 | ||
``` | ||
|
||
Replace `<bidder1-transaction-hash>` with the hash of the previous transaction. | ||
|
||
--- | ||
|
||
[^1]: Instead of including the script in the transaction, we can use a reference script, but to keep things simple, we won't discuss that here. |
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
Oops, something went wrong.