Skip to content

Commit

Permalink
moving to ligo/fa v1.0.8
Browse files Browse the repository at this point in the history
  • Loading branch information
zamrokk committed Oct 10, 2023
1 parent eb289bc commit c6e19b5
Show file tree
Hide file tree
Showing 24 changed files with 417 additions and 2,356 deletions.
157 changes: 80 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,53 +29,50 @@ cd ..
Point to the new template changing the first import line of your `nft.jsligo` file to

```ligolang
#import "@ligo/fa/lib/fa2/asset/single_asset.jsligo" "SINGLEASSET"
#import "@ligo/fa/lib/fa2/asset/single_asset.impl.jsligo" "FA2Impl"
```

It means you will change the namespace from `NFT` to `SINGLEASSET` everywhere (like this you are sure to use the correct library)
On nft.jsligo file, query/replace any reference to `NFT` namespace by `SingleAsset` one

Change the `offer` and `storage` definitions

```ligolang
type offer = {
quantity : nat,
price : nat
};
export type offer = { quantity: nat, price: nat };
type storage =
{
administrators: set<address>,
totalSupply: nat,
offers: map<address,offer>, //user sells an offer
ledger: SINGLEASSET.Ledger.t,
metadata: SINGLEASSET.Metadata.t,
token_metadata: SINGLEASSET.TokenMetadata.t,
operators: SINGLEASSET.Operators.t,
owners: set<SINGLEASSET.Storage.owner>
};
export type storage = {
administrators: set<address>,
totalSupply: nat,
offers: map<address, offer>, //user sells an offer
ledger: FA2Impl.Datatypes.ledger,
metadata: FA2Impl.TZIP16.metadata,
token_metadata: FA2Impl.TZIP12.tokenMetadata,
operators: FA2Impl.Datatypes.operators,
};
```

Explanation:

- `offers` is now a `map<address,offer>`, because you don't have to store `token_id` as a key, now the key is the owner's address. Each owner can sell a part of the unique collection
- `offer` requires a quantity, each owner will sell a part of the unique collection
- `totalSupply` is set while minting in order to track the global quantity of minted items on the collection. It makes it unnecessary to recalculate each time the quantity from each owner's holdings (this value is constant)
- Because the ledger is made of `big_map` of key `owners`, we cache the keys to be able to loop on it
- Since we have a unique collection, we remove `token_ids`. `token_id` will be set to `0`

- Replace all `token_ids` fields by `owners` field on the file `nft.jsligo`

Edit the `mint` function to add the `quantity` extra param, and finally change the `return`

```ligolang
@entry
const mint = (
[quantity, name, description, symbol, ipfsUrl]
: [nat, bytes, bytes, bytes, bytes],
[quantity, name, description, symbol, ipfsUrl]: [
nat,
bytes,
bytes,
bytes,
bytes
],
s: storage
): ret => {
if (quantity <= (0 as nat)) return failwith("0");
if (!Set.mem(Tezos.get_sender(), s.administrators)) return failwith("1");
if (! Set.mem(Tezos.get_sender(), s.administrators)) return failwith("1");
const token_info: map<string, bytes> =
Map.literal(
list(
Expand All @@ -95,14 +92,13 @@ const mint = (
...s,
totalSupply: quantity,
ledger: Big_map.literal(list([[Tezos.get_sender(), quantity as nat]])) as
SINGLEASSET.Ledger.t,
FA2Impl.SingleAsset.ledger,
token_metadata: Big_map.add(
0 as nat,
{ token_id: 0 as nat, token_info: token_info },
s.token_metadata
),
operators: Big_map.empty as SINGLEASSET.Operators.t,
owners: Set.add(Tezos.get_sender(), s.owners)
operators: Big_map.empty as FA2Impl.SingleAsset.operators,
}
]
};
Expand All @@ -116,20 +112,19 @@ const sell = ([quantity, price]: [nat, nat], s: storage): ret => {
//check balance of seller
const sellerBalance =
SINGLEASSET.Storage.get_amount_for_owner(
FA2Impl.Sidecar.get_amount_for_owner(
{
ledger: s.ledger,
metadata: s.metadata,
operators: s.operators,
token_metadata: s.token_metadata,
owners: s.owners
}
)(Tezos.get_source());
if (quantity > sellerBalance) return failwith("2");
//need to allow the contract itself to be an operator on behalf of the seller
const newOperators =
SINGLEASSET.Operators.add_operator(s.operators)(Tezos.get_source())(
FA2Impl.Sidecar.add_operator(s.operators)(Tezos.get_source())(
Tezos.get_self_address()
);
//DECISION CHOICE: if offer already exists, we just override it
Expand All @@ -156,11 +151,11 @@ Also edit the `buy` function to replace `token_id` by `quantity`, check quantiti
const buy = ([quantity, seller]: [nat, address], s: storage): ret => {
//search for the offer
return match(
Map.find_opt(seller, s.offers),
{
None: () => failwith("3"),
Some: (offer: offer) => {
return match(Map.find_opt(seller, s.offers)) {
when (None()):
failwith("3")
when (Some(offer)):
do {
//check if quantity is enough
if (quantity > offer.quantity) return failwith("4");
Expand All @@ -180,11 +175,11 @@ const buy = ([quantity, seller]: [nat, address], s: storage): ret => {
//transfer tokens from seller to buyer
let ledger =
SINGLEASSET.Ledger.decrease_token_amount_for_user(s.ledger)(seller)(
FA2Impl.Sidecar.decrease_token_amount_for_user(s.ledger)(seller)(
quantity
);
ledger =
SINGLEASSET.Ledger.increase_token_amount_for_user(ledger)(
ledger
= FA2Impl.Sidecar.increase_token_amount_for_user(ledger)(
Tezos.get_source()
)(quantity);
//update new offer
Expand All @@ -196,36 +191,33 @@ const buy = ([quantity, seller]: [nat, address], s: storage): ret => {
...s,
offers: Map.update(seller, Some(newOffer), s.offers),
ledger: ledger,
owners: Set.add(Tezos.get_source(), s.owners)
}
]
}
}
)
}
};
```

Edit the storage file `nft.storageList.jsligo` as it. (:warning: you can change the `administrator` address to your own address or keep `alice`)

```ligolang
#import "nft.jsligo" "Contract"
#import "@ligo/fa/lib/fa2/asset/single_asset.jsligo" "SINGLEASSET"
const default_storage =
{
administrators: Set.literal(
list(["tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" as address])
) as set<address>,
totalSupply: 0 as nat,
offers: Map.empty as map<address, Contract.offer>,
ledger: Big_map.empty as SINGLEASSET.Ledger.t,
metadata: Big_map.literal(
list(
const default_storage: Contract.storage = {
administrators: Set.literal(
list(["tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" as address])
) as set<address>,
totalSupply: 0 as nat,
offers: Map.empty as map<address, Contract.offer>,
ledger: Big_map.empty as Contract.FA2Impl.SingleAsset.ledger,
metadata: Big_map.literal(
list(
[
["", bytes `tezos-storage:data`],
[
["", bytes `tezos-storage:data`],
[
"data",
bytes
`{
"data",
bytes
`{
"name":"FA2 NFT Marketplace",
"description":"Example of FA2 implementation",
"version":"0.0.1",
Expand All @@ -239,29 +231,27 @@ const default_storage =
"errors": [],
"views": []
}`
]
]
)
) as SINGLEASSET.Metadata.t,
token_metadata: Big_map.empty as SINGLEASSET.TokenMetadata.t,
operators: Big_map.empty as SINGLEASSET.Operators.t,
owners: Set.empty as set<SINGLEASSET.owner>
};
]
)
) as Contract.FA2Impl.TZIP16.metadata,
token_metadata: Big_map.empty as Contract.FA2Impl.TZIP12.tokenMetadata,
operators: Big_map.empty as Contract.FA2Impl.SingleAsset.operators,
};
```

Compile again and deploy to ghostnet.

```bash
TAQ_LIGO_IMAGE=ligolang/ligo:0.73.0 taq compile nft.jsligo
TAQ_LIGO_IMAGE=ligolang/ligo:1.0.0 taq compile nft.jsligo
taq deploy nft.tz -e "testing"
```

```logs
┌──────────┬──────────────────────────────────────┬───────┬──────────────────┬────────────────────────────────┐
│ Contract │ Address │ Alias │ Balance In Mutez │ Destination │
├──────────┼──────────────────────────────────────┼───────┼──────────────────┼────────────────────────────────┤
│ nft.tz │ KT1QAV6tJ4ZVSDSF6WqCr4qRD7a33DY3iDpj │ nft │ 0 │ https://ghostnet.ecadinfra.com │
│ nft.tz │ KT1EUWEeR9RHMb5q5jeW5jbhxBFHbLTqQgiZ │ nft │ 0 │ https://ghostnet.ecadinfra.com │
└──────────┴──────────────────────────────────────┴───────┴──────────────────┴────────────────────────────────┘
```

Expand Down Expand Up @@ -300,7 +290,7 @@ const refreshUserContextOnPageReload = async () => {
let tokenMetadata: TZIP21TokenMetadata = (await c
.tzip12()
.getTokenMetadata(0)) as TZIP21TokenMetadata;
nftContratTokenMetadataMap.set(0, tokenMetadata);
nftContratTokenMetadataMap.set("0", tokenMetadata);

setNftContratTokenMetadataMap(new Map(nftContratTokenMetadataMap)); //new Map to force refresh
} catch (error) {
Expand Down Expand Up @@ -766,6 +756,7 @@ We introduce the quantity and remove the `token_id` variable. Replace the full f
```typescript
import { InfoOutlined } from "@mui/icons-material";
import SellIcon from "@mui/icons-material/Sell";
import * as api from "@tzkt/sdk-api";
import {
Box,
Expand Down Expand Up @@ -813,6 +804,8 @@ type Offer = {
};
export default function OffersPage() {
api.defaults.baseUrl = "https://api.ghostnet.tzkt.io";
const [selectedTokenId, setSelectedTokenId] = React.useState<number>(0);
const [currentPageIndex, setCurrentPageIndex] = useState<number>(1);
Expand Down Expand Up @@ -849,9 +842,18 @@ export default function OffersPage() {
if (storage) {
console.log("context is not empty, init page now");
const ledgerBigMapId = (
storage.ledger as unknown as { id: BigNumber }
).id.toNumber();
const ownersKeys = await api.bigMapsGetKeys(ledgerBigMapId, {
micheline: "Json",
active: true,
});
await Promise.all(
storage.owners.map(async (owner) => {
if (owner === userAddress) {
ownersKeys.map(async (ownerKey) => {
if (ownerKey.key === userAddress) {
const ownerBalance = await storage.ledger.get(
userAddress as address
);
Expand All @@ -864,7 +866,7 @@ export default function OffersPage() {
console.log(
"found for " +
owner +
ownerKey.key +
" on token_id " +
0 +
" with balance " +
Expand Down Expand Up @@ -957,22 +959,22 @@ export default function OffersPage() {
<Typography>{"ID : " + 0}</Typography>
<Typography>
{"Description : " +
nftContratTokenMetadataMap.get(0)?.description}
nftContratTokenMetadataMap.get("0")?.description}
</Typography>
</Box>
}
>
<InfoOutlined />
</Tooltip>
}
title={nftContratTokenMetadataMap.get(0)?.name}
title={nftContratTokenMetadataMap.get("0")?.name}
/>
<CardMedia
sx={{ width: "auto", marginLeft: "33%" }}
component="img"
height="100px"
image={nftContratTokenMetadataMap
.get(0)
.get("0")
?.thumbnailUri?.replace(
"ipfs://",
"https://gateway.pinata.cloud/ipfs/"
Expand Down Expand Up @@ -1237,7 +1239,8 @@ export default function WineCataloguePage() {
<Typography>{"ID : " + 0}</Typography>
<Typography>
{"Description : " +
nftContratTokenMetadataMap.get(0)?.description}
nftContratTokenMetadataMap.get("0")
?.description}
</Typography>
<Typography>{"Seller : " + owner} </Typography>
</Box>
Expand All @@ -1246,14 +1249,14 @@ export default function WineCataloguePage() {
<InfoOutlined />
</Tooltip>
}
title={nftContratTokenMetadataMap.get(0)?.name}
title={nftContratTokenMetadataMap.get("0")?.name}
/>
<CardMedia
sx={{ width: "auto", marginLeft: "33%" }}
component="img"
height="100px"
image={nftContratTokenMetadataMap
.get(0)
.get("0")
?.thumbnailUri?.replace(
"ipfs://",
"https://gateway.pinata.cloud/ipfs/"
Expand Down
4 changes: 2 additions & 2 deletions solution/.taq/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@
},
{
"type": "npm",
"name": "@taqueria/plugin-contract-types"
"name": "@taqueria/plugin-taquito"
},
{
"type": "npm",
"name": "@taqueria/plugin-taquito"
"name": "@taqueria/plugin-contract-types"
}
]
}
Loading

0 comments on commit c6e19b5

Please sign in to comment.