Vault Covenant

The covenant which mainly manages the voucher system and claiming. Each merchant has its own vault. All cashdrop lock NFTs belonging to that merchant gets sent to and stays in the vault, until a user sends his or her key NFT to the vault during claiming.

FUNCTIONS

  • refund = unclaimed vouchers get refunded after 30 days since collection.
    • inputs
      1. lock NFT from vault
    • outputs
      1. lock NFT embedded amount - dust (fee)
  • emergencyRefund = refunds any BCH sent by users towards the vault by accident.
    • inputs
      1. vault UTXO that has the same amount as the one accidentally sent
    • outputs
      1. Accidentally sent amount - dust (fee)
  • claim = primary function for claiming vouchers
    • inputs
      1. Lock NFT
      2. Key NFT
    • outputs
      1. Lock NFT which has the voucher embedded BCH amount gets burned and sent to the merchant recipient address
        1. Key NFT which has the dust amount, gets burned as the transaction fee
pragma cashscript ^0.8.0;

contract Vault (
    pubkey merchantReceiverPK,
    pubkey merchantSignerPK
) {

    function refund (sig merchantSignerSig) {
        require(checkSig(merchantSignerSig, merchantSignerPK));

        // 2 inputs: lock nft & fee funder utxo
        // 1 output: satoshis refund reward from burned lock nft
        require(tx.inputs.length >= 1);
        require(tx.outputs.length >= 1);

        // sent amount must be equal to the commitment data
        bytes claimExpiryTimestampBytes, bytes claimAmountBytes = tx.inputs[0].nftCommitment.split(20);
        int refundedAmount = int(claimAmountBytes) - 1000;
        int claimExpiryTimestamp = int(claimExpiryTimestampBytes);
        require(tx.outputs[0].value == refundedAmount);

        // lock nft must be expired
        require(tx.time >= claimExpiryTimestamp);

        // claim recipient must be quest owner embedded inside nft commitment
        bytes20 merchantPKH = hash160(merchantReceiverPK);
        bytes25 merchant = new LockingBytecodeP2PKH(merchantPKH);
        require(tx.outputs[0].lockingBytecode == merchant);
    }

    function emergencyRefund (pubkey senderPk) {
        // ensure utxo is from sender and gets sent back to that wallets
        bytes25 sender = new LockingBytecodeP2PKH(hash160(senderPk));
        require(tx.inputs[0].lockingBytecode == sender);
        require(tx.outputs[0].lockingBytecode == sender);
    }

    function claim (
        bytes32 voucherCategory,
        sig merchantSignerSig
    ) {
        require(checkSig(merchantSignerSig, merchantSignerPK));

        // 2 inputs: lock & key nft
        // 1 output: recipient of BCH stored in lock NFT
        require(tx.inputs.length >= 2);
        require(tx.outputs.length >= 1);

        // key NFT must be an immutable NFT & lock/key should be the same category
        require(tx.inputs[0].tokenCategory == voucherCategory);
        require(tx.inputs[1].tokenCategory == voucherCategory);

        // lock & key nft must have the same commitment
        // 20 bytes - claim expiration timestamp
        // 20 bytes - claim amount
        require(tx.inputs[0].nftCommitment == tx.inputs[1].nftCommitment);

        // sent amount must be equal to the commitment data
        bytes claimAmountBytes = tx.inputs[1].nftCommitment.split(20)[1];
        int claimAmount = int(claimAmountBytes);
        require(tx.outputs[0].value == claimAmount);

        // the amount sent must be from lock nft's input
        require(tx.inputs[0].value == claimAmount);

        // the funds must be sent to the merchant receiving address
        bytes20 merchantPKH = hash160(merchantReceiverPK);
        bytes25 merchant = new LockingBytecodeP2PKH(merchantPKH);
        require(tx.outputs[0].lockingBytecode == merchant);
    }

}