# Shipment POAPs

## Shipment POAPs

We can write a contract, `ApePackageCreator`, that tacks on an `ApePOAP` NFT to any user shipment that includes a Bored Ape Yacht Club NFT to another user via the PostOffice contract.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import "./PostOffice.sol";
import "./ApePOAP.sol";

interface CapsuleData {
    enum CapsuleType {
        SIMPLE,
        ERC20,
        ERC721,
        ERC1155
    }

    struct CapsuleContent {
        CapsuleType capsuleType;
        address[] tokenAddresses;
        uint256[] tokenIds;
        uint256[] amounts;
        string tokenURI;
    }
}

contract ApePackageCreator is ERC721Holder {
    PostOffice private postOffice;
    ApePOAP private apePOAP;
    IERC721 private boredApe;

    // replace these with the actual contract addresses
    address constant POST_OFFICE_ADDRESS = 0x3b61403AdE9240699042D3663814a3679dc15d56;
    address constant APE_POAP_ADDRESS = 0xC67F5E3a5B697AE004Edd8F84925189a81c6DC4b;
    address constant BORED_APE_ADDRESS = 0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D;

    constructor() {
        postOffice = PostOffice(POST_OFFICE_ADDRESS);
        apePOAP = ApePOAP(APE_POAP_ADDRESS);
        boredApe = IERC721(BORED_APE_ADDRESS);
    }

    function createApePackage(
        address to_,
        uint256 tokenId,
        bytes32 passwordHash_,
        uint64 unlockTimestamp_
    ) external returns (uint256) {
        // Transfer Bored Ape from user to this contract
        boredApe.safeTransferFrom(msg.sender, address(this), tokenId);

        // Mint a new Ape POAP
        apePOAP.mint(address(this));

        address[] memory _tokenAddresses = new address[](2);
        _tokenAddresses[0] = address(boredApe);
        _tokenAddresses[1] = address(apePOAP);

        uint256[] memory _tokenIds = new uint256[](2);
        _tokenIds[0] = tokenId;
        _tokenIds[1] = apePOAP.tokenOfOwnerByIndex(address(this), apePOAP.balanceOf(address(this)) - 1);

        CapsuleData.CapsuleContent memory _packageContent = CapsuleData.CapsuleContent({
            capsuleType: CapsuleData.CapsuleType.ERC721,
            tokenAddresses: _tokenAddresses,
            tokenIds: _tokenIds,
            amounts: new uint256[](2),
            tokenURI: "https://apepoap.io/metadata"
        });

        SecurityInfo memory _securityInfo = SecurityInfo({
            passwordHash: passwordHash_,
            unlockTimestamp: unlockTimestamp_,
            keyAddress: address(0),
            keyId: 0
        });

        // ship package through PostOffice
        uint256 packageId = postOffice.shipPackage(_packageContent, _securityInfo, to_);

        return packageId;
    }

    function pickup(
        uint256 packageId_,
        string calldata rawPassword_,
        string calldata salt_,
        bool shouldRedeem_
    ) external {
        postOffice.pickup(packageId_, rawPassword_, salt_, shouldRedeem_);
    }
}
```

`ApePackageCreator` accepts the recipient's address, the Bored Ape's token ID, a security hash, and an unlock timestamp. It then transfers the Bored Ape NFT from the sender to itself, mints a new `ApePOAP` NFT for the sender, and creates a new package in the `PostOffice` contract containing both NFTs. The new package can then be redeemed by the recipient using the PostOffice's methods.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.capsulelabs.xyz/protocol-overview/developer-walkthroughs/code-examples/shipment-poaps.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
