Skip to content

Commit

Permalink
add getPolicyData() and mint() to FlightNft, amend test
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiaszimmermann committed Oct 24, 2024
1 parent 495114d commit 674f50c
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 84 deletions.
167 changes: 85 additions & 82 deletions contracts/examples/flight/FlightNft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

import {IPolicy} from "../../instance/module/IPolicy.sol";
import {IRegistry} from "../../registry/IRegistry.sol";

import {Amount} from "../../type/Amount.sol";
import {ChainNft} from "../../registry/ChainNft.sol";
import {FlightProduct} from "./FlightProduct.sol";
import {InstanceReader} from "../../instance/InstanceReader.sol";
Expand All @@ -24,13 +26,28 @@ contract FlightNft is
Ownable
{

ChainNft private _chainNft;
FlightProduct private _flightProduct;
InstanceReader private _reader;
IRegistry private _registry;
error ErrorFlightNftNotMinter();
error ErrorFlightNftAlreadyMinted(uint256 tokenId);
error ErrorFlightNftNotAvailable(uint256 tokenId);
error ErrorFlightNftNotFlightPolicy(uint256 tokenId);

ChainNft public immutable chainNft;
FlightProduct public immutable flightProduct;
InstanceReader public immutable instanceReader;
IRegistry public registry;

address public minter;
string private _baseUri;


modifier onlyMinter() {
if(msg.sender != minter) {
revert ErrorFlightNftNotMinter();
}
_;
}


constructor(
address flightProductAddress,
string memory nftName,
Expand All @@ -40,10 +57,12 @@ contract FlightNft is
ERC721(nftName, nftSymbol)
Ownable(msg.sender)
{
_flightProduct = FlightProduct(flightProductAddress);
_registry = _flightProduct.getRegistry();
_chainNft = ChainNft(_registry.getChainNftAddress());
_reader = _flightProduct.getInstance().getInstanceReader();
flightProduct = FlightProduct(flightProductAddress);
registry = flightProduct.getRegistry();
chainNft = ChainNft(registry.getChainNftAddress());
instanceReader = flightProduct.getInstance().getInstanceReader();

minter = msg.sender;
_baseUri = baseUri;
}

Expand All @@ -60,6 +79,43 @@ contract FlightNft is
}


/**
* Set the minter address.
*/
function setMinter(address minterAddress)
public
onlyOwner()
{
minter = minterAddress;
}


/**
* Mints a metadata NFT for the specified chainNft NFT.
* Only the minter can mint such NFTs.
*/
function mint(uint256 tokenId)
external
onlyMinter()
{
// verify nft does not yet exist
if (_ownerOf(tokenId) != address(0)) {
revert ErrorFlightNftAlreadyMinted(tokenId);
}

// verify nft on chainNft exists
// also checks if nft exists (ERC721NonexistentToken)
address nftOwner = chainNft.ownerOf(tokenId);

// verify nft is flight delay policy
if (registry.getParentNftId(NftIdLib.toNftId(tokenId)) != registry.getNftIdForAddress(address(flightProduct))) {
revert ErrorFlightNftNotFlightPolicy(tokenId);
}

_mint(nftOwner, tokenId);
}


/**
* @dev Return the NFT token URI for the specified token.
* No check is performed to ensure the token exists.
Expand All @@ -86,92 +142,55 @@ contract FlightNft is
function transferFrom(address from, address to, uint256 tokenId) public override { _revert(); }

function balanceOf(address owner) public override view returns (uint256 balance) {
return _chainNft.balanceOf(owner);
return chainNft.balanceOf(owner);
}

function getApproved(uint256 tokenId) public override view returns (address operator) {
return _chainNft.getApproved(tokenId);
return chainNft.getApproved(tokenId);
}

function isApprovedForAll(address owner, address operator) public override view returns (bool) {
return _chainNft.isApprovedForAll(owner, operator);
return chainNft.isApprovedForAll(owner, operator);
}

function ownerOf(uint256 tokenId) public override view returns (address owner) {
return _chainNft.ownerOf(tokenId);
return chainNft.ownerOf(tokenId);
}

function supportsInterface(bytes4 interfaceId) public override view returns (bool) {
return _chainNft.supportsInterface(interfaceId);
return chainNft.supportsInterface(interfaceId);
}


// /**
// * @dev See {IERC721Metadata-name}.
// */
// function name() public override view returns (string memory) {
// return _nftName;
// }


// /**
// * @dev See {IERC721Metadata-name}.
// */
// function symbol() public override view returns (string memory) {
// return _nftSymbol;
// }


/**
* @dev Return the NFT metadata in JSON format.
* examples:
* - https://basescan.org/address/0x4ed83635e2309a7c067d0f98efca47b920bf79b1#readContract
* {"name":"No-Punk #7580","image":"https://gateway.irys.xyz/InMDGHEx3L1YyRz6boihZGDHw4CKCRlLBlKnW6D83i4/7580.png","attributes":[{"trait_type":"Hair","value":"Cap"},{"trait_type":"Mouth","value":"Purple Lipstick"},{"trait_type":"Type","value":"Female"}]}
* - ,
* {"name":"My NFT","description": "A unique digital asset", "image": "https://example.com/nft/1.png" }
*/
function getMetadataJson(uint256 tokenId) external view returns (string memory) {
(
, // risk id
string memory flightData,
string memory departureTimeLocal,
string memory arrivalTimeLocal,
, // status
// delay minutes
) = getRiskData(NftIdLib.toNftId(tokenId));

return string(
abi.encodePacked(
"{\"name\":\"Flight Delay Policy #",
toString(tokenId),
"\",\"description\":\"Flight: ",
flightData,
", Scheduled Departure: ",
departureTimeLocal,
", Scheduled Arrival: ",
arrivalTimeLocal,
"\"}"
));
}


function getRiskData(NftId policyNftId)
function getPolicyData(NftId policyNftId)
public
view
returns (
RiskId riskId,
string memory flightData,
string memory departureTimeLocal,
string memory arrivalTimeLocal,
Amount premiumAmount,
Amount[5] memory payoutAmounts,
bytes1 status,
int256 delayMinutes
)
{
riskId = _reader.getPolicyInfo(policyNftId).riskId;
bytes memory data = _reader.getRiskInfo(riskId).data;
IPolicy.PolicyInfo memory info = instanceReader.getPolicyInfo(policyNftId);

// get financial data
premiumAmount = info.premiumAmount;

if (info.applicationData.length > 0) {
(, payoutAmounts) = abi.decode(info.applicationData, (Amount, Amount[5]));
}

// get risk data
riskId = info.riskId;
bytes memory data = instanceReader.getRiskInfo(riskId).data;

if (data.length > 0) {
FlightProduct.FlightRisk memory flightRisk = _flightProduct.decodeFlightRiskData(data);
FlightProduct.FlightRisk memory flightRisk = flightProduct.decodeFlightRiskData(data);
flightData = flightRisk.flightData.toString();
departureTimeLocal = flightRisk.departureTimeLocal;
arrivalTimeLocal = flightRisk.arrivalTimeLocal;
Expand All @@ -181,22 +200,6 @@ contract FlightNft is
}


/**
* returns the flight product address.
*/
function getChainNft() external view returns (address) {
return address(_chainNft);
}


/**
* returns the flight product address.
*/
function getFlightProduct() external view returns (address) {
return address(_flightProduct);
}


function toString(uint256 value) public pure returns (string memory) {
if (value == 0) {
return "0";
Expand Down
56 changes: 54 additions & 2 deletions test/examples/flight/FlightProduct.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,14 @@ contract FlightProductTest is FlightBaseTest {
// GIVEN - setp from flight base test

// deploy flight nft
vm.startPrank(flightOwner);
FlightNft flightNft = new FlightNft(
address(flightProduct),
"FDPLCY",
"Flight Delay Policy",
"https://flightdelay.integration.etherisc.com/api/nft/");
flightOwner,
"https://flightdelay.etherisc.app/api/nft/");
vm.stopPrank();

// create policy
approveProductTokenHandler();
Expand All @@ -318,10 +321,59 @@ contract FlightProductTest is FlightBaseTest {
permit);
vm.stopPrank();

(
RiskId ri,
string memory fd,
string memory dtl,
string memory atl,
Amount pa,
Amount[5] memory pas,
bytes1 s,
int256 d
) = flightNft.getPolicyData(policyNftId);

// solhint-disable
console.log("token uri", flightNft.tokenURI(policyNftId.toInt()));
console.log("token meatadata", flightNft.getMetadataJson(policyNftId.toInt()));
console.log("flightData", fd);
console.log("departure time local", dtl);
console.log("arrival time local", atl);
console.log("premium amount", pa.toInt());
console.log("payout amounts", pas[2].toInt(), pas[3].toInt(), pas[4].toInt());
// solhint-enable

assertEq(pa.toInt(), 15000000, "unexpected premium amount");
assertEq(pas[2].toInt(), 49450549, "unexpected payout amount (late >= 45')");
assertEq(pas[3].toInt(), 82417582, "unexpected cancelled amount");
assertEq(pas[4].toInt(), 82417582, "unexpected diverted amount");

uint256 tokenId = policyNftId.toInt();

vm.expectRevert(); // ErrorFlightNftNotMinter
flightNft.mint(tokenId);

vm.expectRevert(); // ERC721NonexistentToken(100)
vm.startPrank(flightOwner);
flightNft.mint(100);
vm.stopPrank();

uint256 productTokenId = flightProduct.getNftId().toInt();
vm.expectRevert(); // ErrorFlightNftNotFlightPolicy(203133705)
vm.startPrank(flightOwner);
flightNft.mint(productTokenId);
vm.stopPrank();

// happy path
uint256 policyTokenId = policyNftId.toInt();
vm.startPrank(flightOwner);
flightNft.mint(policyTokenId);
vm.stopPrank();

vm.expectRevert(); // ErrorFlightNftAlreadyMinted(243133705)
vm.startPrank(flightOwner);
flightNft.mint(policyTokenId);
vm.stopPrank();

assertEq(flightNft.ownerOf(policyNftId.toInt()), customer, "unexpected policy owner");
}


Expand Down

0 comments on commit 674f50c

Please sign in to comment.