Skip to content

DIP-3: Partial Fills #5

@maxisacoder

Description

@maxisacoder
dip title author
3 Partial Fills Max Li <max>

Simple Summary

For debtor order with a big value of principleAmount, the debtor may want only fill part of it instead of the whole of it. This feature allows more agility and convenience for both parties of debt. The efficiency of the whole market will also increase.

This proposal introduces a straightforward design for this feature with the overhead of none-backward compatible NFT token. Whether there are more clever solutions for better compatibility is still remained to be explored by our communities.

Specification

Splitting out this issue, we need to deal with the following questions:

  1. Where and when the partial fills happen?
  2. How to record the partial filled?
  3. What the relationship between with NFT and the record?

Inspired by 0x Protocol, which do partial fill when call fillOder with a fillTakerTokenAmount for partial-fill-value for contract caller. A similar method can be considered by adding this parameter to fillOder. For supporting partial fills, DebtOrder struct need a minor revision by adding a minPrincipalAmount field.

    struct Issuance  {
        address version;
        address debtor;
        address underwriter;
        uint underwriterRiskRating;
        address termsContract;
        bytes32 termsContractParameters;
        uint salt;
        bytes32 agreementId;
    }

    struct DebtOrder {
        Issuance issuance;
        uint underwriterFee;
        uint relayerFee;
        uint principalAmount;
        + uint minPrincipalAmount; // value equal to principalAmount means no-partial-fill allowed
        address principalToken;
        uint creditorFee;
        uint debtorFee;
        address relayer;
        uint expirationTimestampInSec;
        bytes32 debtOrderHash;
    }

In original charta design, DebtRegistry contract records every occurred debt behavior on-chain. DebtToken contract get the hash of every record and mint token by the record hash. This proposal will follow this pattern for minimal changes. In DebtKernel contract, we save an orderRemain, which maps debtOder hash to current remaining for every order. This map can determine whether the remaining value is big enough for a fillDebtOder call.

    mapping (byte32 => Remainning) orderRemain;
    struct Remainning {
        uint total;
        uint min;
        uint remain;
    }
    function fillDebtOrder(
        address creditor,
        address[6] orderAddresses,
        uint[8] orderValues,
        + uint partialFillAmount, // for partial fill
        bytes32[1] orderBytes32,
        uint8[3] signaturesV,
        bytes32[3] signaturesR,
        bytes32[3] signaturesS
    )
        public
        whenNotPaused
        returns (bytes32 _agreementId)
    {
        + // check the exisitence of  order for given order hash
       DebtOrder memory debtOrder = getDebtOrder(orderAddresses, orderValues, orderBytes32);
       if (!orderRemain[orderBytes32]) {
            orderRemain[orderBytes32] =  Remainning (
                  debtOrder.principalAmount,
                  debtOrder.minPrincipalAmount,
                  debtOrder.principalAmount,
          }
       }
        
        + // Assert remain is enough for this fill
        require(orderRemain[orderBytes32].remain > partialFillAmount);

        // Assert order's validity & consensuality (some checking allowance value need also change)
         
        + // Mint debt token and finalize debt agreement
        issueDebtAgreement(creditor, debtOrder.issuance, partialFillAmount);
        orderRemain[orderBytes32].remain = orderRemain[orderBytes32].principalAmount - partialFillAmount;

        // Register debt agreement's start with terms contract
        // Transfer principal to debtor
        // Transfer underwriter fee to underwriter
        // Transfer relayer fee to relayer
    }

In the current design of NFT, the ERC-721 tokenId is set to agreementId(Issuance Id). In the context of partial fills, one order is corresponding to one issuance, but possibly multiple fills. Every occured fill event need a NTF for tokenlizing the creditor's right. So, there are avoidless breaking changes in DebtToken contract for minting this new NTF for every fill.

    function create(
        address _version,
        address _beneficiary,
        address _debtor,
        address _underwriter,
        uint _underwriterRiskRating,
        address _termsContract,
        bytes32 _termsContractParameters,
        uint _salt,
        uint _amount;
    )
        public
        whenNotPaused
        returns (uint _tokenId)
    {
        require(tokenCreationPermissions.isAuthorized(msg.sender));

        bytes32 entryHash = registry.insert(
            _version,
            _debtor,
            _underwriter,
            _underwriterRiskRating,
            _termsContract,
            _termsContractParameters,
            _salt
        );

       + // record the fill to registry
          // Fill struct record the related order, beneficiary, and partial-filled amount
              bytes32 fillHash = registry.insertFill(
             entryHash, (issurance hash)
             _beneficiary,
             _amount,
             _salt
        );
       
       + // mint by fillHash
        super._mint(_beneficiary, uint(fillHash));
        return uint(fillHash);
    } 

The meaning of agreementId is the unique key for all off-chain Order or on-chain Entry. Replacing this agreementId with fillHash for NFT minting, the NFT issued and the on-chain Entry can no long keep an one-to-one relationship. In this proposal, Fill is a structure looks like:

strtuct Fill {
   btyte32 agreementId;
   uint amount;
   uint salt;
   btyte32 fillHash;
}

TermsContract will retrieve order paraments from DebtRegisty by fillHash now. For example, querying _termsContractParameters by fillHash, DebtRegisty will get agreementId form Fill, than get _termsContractParameters from Entry.

Backwards Compatibility

This proposal introduce main changes including:

  • DebtKernal: adding partialFillAmount in DebtOrder definition. When this field is ignored, contract can treat the order with no-partial-fill supporting and keep compatibility.
  • NFT tokenId: this change need be considered with prudence. TokenId redifining will make all legacy minted tokens uncompatiable.

Copyright Waiver

Copyright and related rights waived via CC0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions