Transfers

https://github.com/BitBadges/bitbadgeschain/blob/master/proto/badges/transfers.proto
syntax = "proto3";
package badges;

import "gogoproto/gogo.proto";
import "badges/permissions.proto";
import "badges/address_lists.proto";
import "badges/balances.proto";
import "cosmos/base/v1beta1/coin.proto";

option go_package = "github.com/bitbadges/bitbadgeschain/x/badges/types";

/*
  UserBalanceStore is the store for the user balances for a collection.

  It consists of a list of balances, a list of approved outgoing transfers, and a list of approved incoming transfers,
  as well as the permissions for updating the approved incoming/outgoing transfers.

  Upon initialization, all fields (minus the balances) are set to the defaults specified by the collection.

  The outgoing transfers can be used to allow / disallow transfers which are sent from this user.
  If a transfer has no match, then it is disallowed by default, unless from == initiatedBy (i.e. initiated by this user)
  and autoApproveSelfInitiatedOutgoingTransfers is set to true.

  The incoming transfers can be used to allow / disallow transfers which are sent to this user.
  If a transfer has no match, then it is disallowed by default, unless to == initiatedBy (i.e. initiated by this user)
  and autoApproveSelfInitiatedIncomingTransfers is set to true.

  Note that the user approved transfers are only checked if the collection approved transfers do not specify to override
  the user approved transfers. 

  The permissions are used to determine whether the user can update the approved incoming/outgoing transfers and auto approvals.
*/
message UserBalanceStore {
  // The list of balances associated with this user.
  repeated Balance balances = 1;

  // The list of approved outgoing transfers for this user.
  repeated UserOutgoingApproval outgoingApprovals = 2;

  // The list of approved incoming transfers for this user.
  repeated UserIncomingApproval incomingApprovals = 3;

  // Whether to auto-approve self-initiated outgoing transfers for this user (i.e. from == initiatedBy).
  bool autoApproveSelfInitiatedOutgoingTransfers = 4;

  // Whether to auto-approve self-initiated incoming transfers for this user (i.e. to == initiatedBy).
  bool autoApproveSelfInitiatedIncomingTransfers = 5;

  // Whether to auto-approve all incoming transfers by default. 
  // This is just shorthand for adding an accept everything incoming approval
  // with no restrictions.
  bool autoApproveAllIncomingTransfers = 6;

  // The permissions for this user's actions and transfers.
  UserPermissions userPermissions = 7;
}

/*
  Challenges define a rule for the approval in the form of a Merkle challenge.

  A Merkle challenge is a challenge where the user must provide a Merkle proof to a Merkle tree. If they provide a valid proof,
  then the challenge is met. All challenges must be met with valid solutions for the transfer to be approved.

  IMPORTANT: Merkle challenges currently are limited to SHA256 hashes. See documentation for MerkleChallenge for more details and tutorials.

  IMPORTANT: We track the number of uses per leaf according to the challengeTrackerId specified by the parent approval of this challenge.
  If you update the challenge ID, then the used leaves tracker will reset and start a new tally.
  We recommend using a unique challenge ID for each challenge to prevent overlap and unexpected behavior.
*/
message MerkleChallenge {
  // The root hash of the Merkle tree to which the Merkle path must lead for verification.
  string root = 1;

  // The expected length of the Merkle path for verification. Used to prevent Merkle path truncation attacks.
  string expectedProofLength = 2 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];

  // If true, we will override the user's leaf for their proof with their creator address. Used for whitelist trees where all leaves are valid BitBadges addresses.
  bool useCreatorAddressAsLeaf = 3;

  // The maximum number of times each leaf can be used. Must be 1 if useCreatorAddressAsLeaf is false to prevent replay attacks.
  string maxUsesPerLeaf = 4 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];

  // The URI associated with this Merkle challenge, optionally providing metadata about the challenge.
  string uri = 5;

  // Arbitrary custom data associated with this Merkle challenge.
  string customData = 6;

  // The ID of this Merkle challenge for tracking the number of uses per leaf.
  string challengeTrackerId = 7;

  // Leaf must be signed by. Used to protect against man in the middle attacks.`
  // Scheme we use is sign(leaf + "-" +
  string leafSigner = 8;
}

/*
  ETHSignatureChallenge defines a rule for the approval in the form of an Ethereum signature challenge.

  An ETH signature challenge is a challenge where the user must provide a valid Ethereum signature for a specific nonce.
  The signature scheme is ETHSign(nonce + "-" + creatorAddress) and each signature can only be used once.
  All challenges must be met with valid solutions for the transfer to be approved.

  IMPORTANT: We track the usage of each signature to prevent replay attacks. Each signature can only be used once.
  If you update the challenge ID, then the used signatures tracker will reset and start a new tally.
  We recommend using a unique challenge ID for each challenge to prevent overlap and unexpected behavior.
*/
message ETHSignatureChallenge {
  // The Ethereum address that must sign the nonce for verification.
  string signer = 1;

  // The ID of this ETH signature challenge for tracking the number of uses per signature.
  string challengeTrackerId = 2;

  // The URI associated with this ETH signature challenge, optionally providing metadata about the challenge.
  string uri = 3;

  // Arbitrary custom data associated with this ETH signature challenge.
  string customData = 4;
}


// UserOutgoingApproval defines the rules for the approval of an outgoing transfer from a user.
message UserOutgoingApproval {
  // The list ID for the recipient of the transfer.
  string toListId = 1;

  // The list ID for the user who initiated the transfer.
  string initiatedByListId = 2;

  // The allowed range of transfer times for approval.
  repeated UintRange transferTimes = 3;

  // The allowed range of token IDs for approval.
  repeated UintRange badgeIds = 4;

  // The allowed range of ownership times for approval.
  repeated UintRange ownershipTimes = 5;

  // The URI associated with this approval, optionally providing metadata about the approval.
  string uri = 8;

  // Arbitrary custom data associated with this approval.
  string customData = 9;

  // The ID of this approval. Must be unique per level (i.e. collection, outgoing, incoming).
  string approvalId = 10;

  // The criteria that must be met for this approval to be considered.
  OutgoingApprovalCriteria approvalCriteria = 11;

  // Version of the approval. Maintained internally.
  string version = 12 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
}

// UserIncomingApproval defines the rules for the approval of an incoming transfer to a user.
message UserIncomingApproval {
  // The list ID for the sender of the transfer.
  string fromListId = 1;

  // The list ID for the user who initiated the transfer.
  string initiatedByListId = 2;

  // The allowed range of transfer times for approval.
  repeated UintRange transferTimes = 3;

  // The allowed range of token IDs for approval.
  repeated UintRange badgeIds = 4;

  // The allowed range of ownership times for approval.
  repeated UintRange ownershipTimes = 5;

  // The URI associated with this approval, optionally providing metadata about the approval.
  string uri = 8;

  // Arbitrary custom data associated with this approval.
  string customData = 9;

  // The ID of this approval. Must be unique per level (i.e. collection, outgoing, incoming).
  string approvalId = 10;

  // The criteria that must be met for this approval to be considered.
  IncomingApprovalCriteria approvalCriteria = 11;
  

  // Version of the approval. Maintained internally.
  string version = 12 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
}




// CollectionApproval defines the rules for the approval of a transfer.
// Each transfer can be broken down into a (from, to, initiatedBy, transferTime, badgeId) tuple.
// We check the approvals for first match of this tuple, using the approvals. Subsequent matches are ignored.
//
// If the first match is disallowed, the transfer is disallowed.
// If the first match is allowed, then we check the rest of the restrictions. If any restrictions fail, then the transfer is disallowed. 
// We do not proceed to the next match.
//
// Challenges defines the challenges that must be met with valid solutions for the transfer to be approved.
//
// requireTo/From(DoesNot)EqualsInitiatedBy defines whether the to/from address must equal the initiatedBy address or not. If it doesn't, then the transfer is disallowed.
//  
// overallApprovals defines the overall approvals for the transfer (i.e. the running tally of the number of transfers and amounts transferred by all addresses).
// perAddressApprovals


// CollectionApproval defines the rules for the approval of a transfer.
// Each transfer can be broken down into a (from, to, initiatedBy, transferTime, badgeId) tuple.
// We check the approvals for first match of this tuple, using the approvals. Subsequent matches are ignored.
//
// If the first match is disallowed, the transfer is disallowed.
// If the first match is allowed, then we check the rest of the restrictions. If any restrictions fail, then the transfer is disallowed. 
// We do not proceed to the next match.
//
// Challenges defines the challenges that must be met with valid solutions for the transfer to be approved.
//
// requireTo/From(DoesNot)EqualsInitiatedBy defines whether the to/from address must equal the initiatedBy address or not. If it doesn't, then the transfer is disallowed.
//  
// overallApprovals defines the overall approvals for the transfer (i.e. the running tally of the number of transfers and amounts transferred by all addresses).
// perAddressApprovals defines the approvals per unique from, to, and/or initiatedBy address.
// If any of these are nil, we assume unlimited approvals.
//
// IMPORTANT: We track the number of transfers and amounts transferred according to a tracker ID. This is a running tally that increments over time.
// Whenever a transfer is processed that maps to a specific tracker ID, we increment the number of transfers and amounts transferred.
// If the number of transfers or amounts transferred exceeds the corresponding overall or per address approvals, then the transfer is disallowed.
// Note we only track if overallApprovals or to/from/intiiatedByApprovals is not nil.
// If you want to reset the tracker tally, update the tracker ID to a new unique tracker ID.
// Tracker IDs are unique to their timelines. A tracker ID "abc" can be used for the collection, outgoing, and incoming timelines without overlap or overwriting one another.
//
// Ex: If overallApprovals maxNumTransfers = 20 and trackerID = "abc", then the first 20 transfers that map to trackerID = "abc" will be approved. The 21st transfer will be disallowed.
//
// IMPORTANT: Be very careful when updating an approved transfer but keeping the same tracker ID. 
// For example, if you change the corresponding token IDs and the old token IDs overlap, then the overlapping token IDs will already have existing tallies.
//    
// Another common area of confusion is what is actually being tallied. The tally is based on the approved transfer rules that are set.
// For example, if you don't have per address rules set and you update the approved transfer rules to include per address rules, 
// then the tally doesn't retroactively apply the per address rules to the previous transfers. It starts at that time.
//
// Lastly, we have overridesFromOutgoingApprovals and overridesToIncomingApprovals.
// If these are set to true, we ignore the from / to user's approved outgoing / incoming transfers, respectively.
// This is useful, for example, for forcefully revoking tokens.
// If these are set to false, the transfer must also be approved by the from /to user's approved outgoing / incoming transfers, respectively.


// ManualBalances represents a list of manual balances entered for the predetermined balances criteria. Order is calculated according to the calculation method set. 
message ManualBalances {
  repeated Balance balances = 1;
}

// RecurringOwnershipTimes represents a list of recurring ownership times.
message RecurringOwnershipTimes {
  // The original start time of the first interval.
  string startTime = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // The interval length in unix milliseconds.
  string intervalLength = 2 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Grace period length where you can charge the next interval (nextStartTime - chargePeriodLength) until (nextStartTime) = charge period
  string chargePeriodLength = 3 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
}

// IncrementedBalances represents balances that are incremented by specific amounts, according to the order calculation method.
message IncrementedBalances {
  repeated Balance startBalances = 1;
  // The amount by which to increment token IDs.
  string incrementBadgeIdsBy = 2 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // The amount by which to increment ownership times. Incompatible with approveStartingFromNowBy.
  string incrementOwnershipTimesBy = 3 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // The amount of unix milliseconds to approve starting from now. Incompatible with incrementOwnershipTimesBy.
  string durationFromTimestamp = 4 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Whether to allow overriding the timestamp for the balances (only applicable with durationFromTimestamp set).
  bool allowOverrideTimestamp = 5;
  // Recurring ownership times.
  RecurringOwnershipTimes recurringOwnershipTimes = 6;
  // Allow override of any valid ID
  bool allowOverrideWithAnyValidBadge = 7;
}

// PredeterminedOrderCalculationMethod defines the method to calculate predetermined balances order.
message PredeterminedOrderCalculationMethod {
  // Use the overall number of transfers to calculate the order. Ex: First transfer gets the first balance, second transfer gets the second balance, etc.
  bool useOverallNumTransfers = 1;
  // Use the number of transfers per "to" address to calculate the order. Ex: First transfer to address A gets the first balance, second transfer to address A gets the second balance, etc.
  bool usePerToAddressNumTransfers = 2;
  // Use the number of transfers per "from" address to calculate the order. Ex: First transfer from address A gets the first balance, second transfer from address A gets the second balance, etc.
  bool usePerFromAddressNumTransfers = 3;
  // Use the number of transfers per "initiated by" address to calculate the order. Ex: First transfer initiated by address A gets the first balance, second transfer initiated by address A gets the second balance, etc.
  bool usePerInitiatedByAddressNumTransfers = 4;
  // Use the Merkle challenge leaf index to calculate the order. Ex: Transfer that uses leaf index 0 gets the first balance, transfer that uses leaf index 1 gets the second balance, etc.
  bool useMerkleChallengeLeafIndex = 5;
  // If useMerkleChallengeLeafIndex is set, then this is the ID of the challenge tracker associated with this calculation method.
  string challengeTrackerId = 6;
}

// PredeterminedBalances represents balances with predetermined order calculation.
message PredeterminedBalances {
  // Manual balances that can be entered. If this is nil, then we use the incremented balances.
  repeated ManualBalances manualBalances = 1;
  // Balances that have a starting amount and increment. If this is nil, then we use the manual balances.
  IncrementedBalances incrementedBalances = 2;
  // The method to calculate the order of predetermined balances.
  PredeterminedOrderCalculationMethod orderCalculationMethod = 3;
}

// AutoDeletionOptions defines the options for auto-deletion of approvals.
message AutoDeletionOptions {
  // After one use?
  bool afterOneUse = 1;
  // After overall max number of uses threshold is met?
  bool afterOverallMaxNumTransfers = 2;
  // Allow counterparty to purge this approval if they are the only initiator
  bool allowCounterpartyPurge = 3;
  // Allow others to call PurgeApprovals on behalf of this approval owner
  bool allowPurgeIfExpired = 4;
}

// ApprovalAmounts defines approval amounts per unique "from," "to," and/or "initiated by" address.
// If any of these are nil or "0", we assume unlimited approvals.
// If they are set to a value, then the running tally of the amounts transferred for the specified token IDs and ownership times 
// must not exceed the corresponding value.
message ApprovalAmounts {
  // Overall approval amount.
  string overallApprovalAmount = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Approval amount per "to" address.
  string perToAddressApprovalAmount = 2 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Approval amount per "from" address.
  string perFromAddressApprovalAmount = 3 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Approval amount per "initiated by" address.
  string perInitiatedByAddressApprovalAmount = 4 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // The ID of the amount tracker associated with this approval.
  // We use this ID to track the number of transfers and amounts transferred.
  string amountTrackerId = 6;
  // Time intervals to reset the trackers at.
  ResetTimeIntervals resetTimeIntervals = 7;
}

// Time intervals to reset the trackers at.
message ResetTimeIntervals {
  // Original start time of the first interval.
  string startTime = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Interval length in unix milliseconds.
  string intervalLength = 2 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
}

// MaxNumTransfers defines the maximum number of transfers per unique "from," "to," and/or "initiated by" address.
// If any of these are nil or "0", we assume unlimited approvals.
// If they are set to a value, then the running tally of the number of transfers for the specified token IDs and ownership times
// must not exceed the corresponding value.
message MaxNumTransfers {
  // Overall maximum number of transfers.
  string overallMaxNumTransfers = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Maximum number of transfers per "to" address.
  string perToAddressMaxNumTransfers = 2 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Maximum number of transfers per "from" address.
  string perFromAddressMaxNumTransfers = 3 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Maximum number of transfers per "initiated by" address.
  string perInitiatedByAddressMaxNumTransfers = 4 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // The ID of the amount tracker associated with this approval.
  // We use this ID to track the number of transfers and amounts transferred.
  string amountTrackerId = 6;
  // Time intervals to reset the trackers at.
  ResetTimeIntervals resetTimeIntervals = 7;
}

// ApprovalTracker defines the tracker for approvals. This tracks the cumulative number of transfers and associated balances transferred.
message ApprovalTracker {
  // The number of transfers that have been processed.
  string numTransfers = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Cumulative balances associated with the transfers that have been processed.
  repeated Balance amounts = 2;
  // Last updated at time.
  string lastUpdatedAt = 3 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
}

message CoinTransfer {
  // The address of the recipient of the transfer.
  string to = 1;
  // The sdk.Coins to be transferred.
  repeated cosmos.base.v1beta1.Coin coins = 2;
  // By default, the from address is the initiator of the transaction.
  // If this is set to true, we will override the from address with the approver address.
  // Note: This is not applicable for collection approvals (since approverAddress == '').
  bool overrideFromWithApproverAddress = 3;
  // By default, the to address is what is specified in the coin transfer.
  // If this is set to true, we will override the to address with the initiator of the transaction.
  bool overrideToWithInitiator = 4;
}

/* 
  MustOwnBadges represents a condition where a user must own specific tokens
  to be approved to transfer.

  - collectionId: The ID of the collection for the tokens that must be owned
  - amountRange: The range of amounts the user must own (min to max)
  - ownershipTimes: The time ranges during which the user must own the tokens.
  - badgeIds: The token IDs the user must own.
  - overrideWithCurrentTime: If true, auto override ownershipTimes with the current time.
  - mustSatisfyForAllAssets: If true, the user must own all specified tokens; otherwise, owning any one for >= 1 millisecond is sufficient.
*/
message MustOwnBadges {
  // The ID of the collection.
  string collectionId = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];

  // The range of amounts the user must own (min to max).
  UintRange amountRange = 2;

  // The time ranges during which the user must own the tokens.
  repeated UintRange ownershipTimes = 3;

  // The token IDs the user must own.
  repeated UintRange badgeIds = 4;

  // If true, override ownershipTimes with the current time.
  bool overrideWithCurrentTime = 5;

  // If true, the user must meet ownership requirements for all specified tokens; else, must meet requirements for any single token.
  bool mustSatisfyForAllAssets = 6;

  // The party to check ownership for. Options are "initiator", "sender", or "recipient". Defaults to "initiator" if empty.
  string ownershipCheckParty = 7;
}

// DynamicStoreChallenge defines a challenge that requires the initiator to pass a dynamic store check.
message DynamicStoreChallenge {
  // The ID of the dynamic store to check.
  string storeId = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
}

// ApprovalCriteria defines the criteria for approving transfers.
message ApprovalCriteria {
  // Merkle challenge that must be satisfied for approval.
  repeated MerkleChallenge merkleChallenges = 1;
  // Predetermined balances for eeach approval.
  PredeterminedBalances predeterminedBalances = 2;
  // Threshold limit of amounts that can be transferred using this approval.
  ApprovalAmounts approvalAmounts = 3;
  // Maximum number of transfers that can be processed using this approval.
  MaxNumTransfers maxNumTransfers = 4;
  // The sdk.Coins that need to be transferred for approval.
  repeated CoinTransfer coinTransfers = 5;

  // Require the "to" address to be equal to the "initiated by" address for approval.
  bool requireToEqualsInitiatedBy = 6;
  // Require the "from" address to be equal to the "initiated by" address for approval.
  bool requireFromEqualsInitiatedBy = 7;
  // Require the "to" address to not be equal to the "initiated by" address for approval.
  bool requireToDoesNotEqualInitiatedBy = 8;
  // Require the "from" address to not be equal to the "initiated by" address for approval.
  bool requireFromDoesNotEqualInitiatedBy = 9;

  // Overrides the user's outgoing approvals for approval.
  bool overridesFromOutgoingApprovals = 10;
  // Overrides the user's incoming approvals for approval.
  bool overridesToIncomingApprovals = 11;

  // Auto-deletion options.
  AutoDeletionOptions autoDeletionOptions = 12;

  // User level royalties to apply to the transfer.
  UserRoyalties userRoyalties = 13;

  // Must own tokens for approval.
  repeated MustOwnBadges mustOwnBadges = 14;
  // Dynamic store challenges that the initiator must pass for approval.
  repeated DynamicStoreChallenge dynamicStoreChallenges = 15;
  // ETH signature challenges that the initiator must pass for approval.
  repeated ETHSignatureChallenge ethSignatureChallenges = 16;
}

// UserRoyalties defines the royalties for a user.
message UserRoyalties {
  // Percentage of the transfer amount to apply as royalties. 1 to 10000 represents basis points.
  string percentage = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // Payout address for the royalties.
  string payoutAddress = 2;
}

// OutgoingApprovalCriteria defines the criteria for approving outgoing transfers.
message OutgoingApprovalCriteria {
  // Merkle challenge that must be satisfied for approval.
  repeated MerkleChallenge merkleChallenges = 1;
  // Predetermined balances for eeach approval.
  PredeterminedBalances predeterminedBalances = 2;
  // Threshold limit of amounts that can be transferred using this approval.
  ApprovalAmounts approvalAmounts = 3;
  // Maximum number of transfers that can be processed using this approval.
  MaxNumTransfers maxNumTransfers = 4;
  // The sdk.Coins that need to be transferred for approval.
  repeated CoinTransfer coinTransfers = 5;

  // Require the "to" address to be equal to the "initiated by" address for approval.
  bool requireToEqualsInitiatedBy = 6;
  // Require the "to" address to not be equal to the "initiated by" address for approval.
  bool requireToDoesNotEqualInitiatedBy = 7;

  // Auto-deletion options.
  AutoDeletionOptions autoDeletionOptions = 8;

  // Must own tokens for approval.
  repeated MustOwnBadges mustOwnBadges = 9;
  // Dynamic store challenges that the initiator must pass for approval.
  repeated DynamicStoreChallenge dynamicStoreChallenges = 10;
  // ETH signature challenges that the initiator must pass for approval.
  repeated ETHSignatureChallenge ethSignatureChallenges = 11;
}

// IncomingApprovalCriteria defines the criteria for approving incoming transfers.
message IncomingApprovalCriteria {
  // Merkle challenge that must be satisfied for approval.
  repeated MerkleChallenge merkleChallenges= 1;
  // Predetermined balances for eeach approval.
  PredeterminedBalances predeterminedBalances = 2;
  // Threshold limit of amounts that can be transferred using this approval.
  ApprovalAmounts approvalAmounts = 3;
  // Maximum number of transfers that can be processed using this approval.
  MaxNumTransfers maxNumTransfers = 4;
  // The sdk.Coins that need to be transferred for approval.
  repeated CoinTransfer coinTransfers = 5;

  // Require the "from" address to be equal to the "initiated by" address for approval.
  bool requireFromEqualsInitiatedBy = 6;
  // Require the "from" address to not be equal to the "initiated by" address for approval.
  bool requireFromDoesNotEqualInitiatedBy = 7;

  // Auto-deletion options.
  AutoDeletionOptions autoDeletionOptions = 8;

  // Must own tokens for approval.
  repeated MustOwnBadges mustOwnBadges = 9;
  // Dynamic store challenges that the initiator must pass for approval.
  repeated DynamicStoreChallenge dynamicStoreChallenges = 10;
  // ETH signature challenges that the initiator must pass for approval.
  repeated ETHSignatureChallenge ethSignatureChallenges = 11;
}


// CollectionApproval defines the rules for the approval of a transfer on the collection level
message CollectionApproval {
  // The list ID for the sender of the transfer.
  string fromListId = 1;
  // The list ID for the recipient of the transfer.
  string toListId = 2;
  // The list ID for the user who initiated the transfer.
  string initiatedByListId = 3;
  // The allowed range of transfer times for approval.
  repeated UintRange transferTimes = 4;
  // The allowed range of token IDs for approval.
  repeated UintRange badgeIds = 5;
  // The allowed range of ownership times for approval.
  repeated UintRange ownershipTimes = 6;
  // The URI associated with this approval, optionally providing metadata about the approval.
  string uri = 9;
  // Arbitrary custom data associated with this approval.
  string customData = 10;
  // The ID of this approval. Must be unique per level (i.e. collection, outgoing, incoming).
  string approvalId = 11;
  // The criteria that must be met for this approval to be considered.
  ApprovalCriteria approvalCriteria = 12;

  // Version of the approval. Maintained internally.
  string version = 13 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
}

// ApprovalIdentifierDetails defines the details to identify a specific approval.
message ApprovalIdentifierDetails {
  // The ID of the approval.
  string approvalId = 1;
  // The level of the approval. Can be "collection", "incoming", or "outgoing".
  string approvalLevel = 2;
  // The address of the approver. Leave blank "" if approvalLevel == "collection".
  string approverAddress = 3;
  // The version of the approval.
  string version = 4 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
}


// Transfer defines the details of a transfer of tokens.
message Transfer {
  // The address of the sender of the transfer.
  string from = 1;
  // The addresses of the recipients of the transfer.
  repeated string toAddresses = 2;
  // The balances to be transferred.
  repeated Balance balances = 3;
  // If defined, we will use the predeterminedBalances from the specified approval to calculate the balances at execution time.
  // We will override the balances field with the precalculated balances. Only applicable for approvals with predeterminedBalances set.
  ApprovalIdentifierDetails precalculateBalancesFromApproval = 4;
  // The Merkle proofs / solutions for all Merkle challenges required for the transfer.
  repeated MerkleProof merkleProofs = 5;
  // The ETH signature proofs / solutions for all ETH signature challenges required for the transfer.
  repeated ETHSignatureProof ethSignatureProofs = 6;
  // The memo for the transfer.
  string memo = 7;
  // The prioritized approvals for the transfer. By default, we scan linearly through the approvals and use the first match.
  // This field can be used to prioritize specific approvals and scan through them first.
  repeated ApprovalIdentifierDetails prioritizedApprovals = 8;
  // Whether to only check prioritized approvals for the transfer. 
  // If true, we will only check the prioritized approvals and fail if none of them match (i.e. do not check any non-prioritized approvals).
  // If false, we will check the prioritized approvals first and then scan through the rest of the approvals. 
  bool onlyCheckPrioritizedCollectionApprovals = 9;
  // Whether to only check prioritized approvals for the transfer. 
  // If true, we will only check the prioritized approvals and fail if none of them match (i.e. do not check any non-prioritized approvals).
  // If false, we will check the prioritized approvals first and then scan through the rest of the approvals. 
  bool onlyCheckPrioritizedIncomingApprovals = 10;
  // Whether to only check prioritized approvals for the transfer. 
  // If true, we will only check the prioritized approvals and fail if none of them match (i.e. do not check any non-prioritized approvals).
  // If false, we will check the prioritized approvals first and then scan through the rest of the approvals. 
  bool onlyCheckPrioritizedOutgoingApprovals = 11;
  // The options for precalculating the balances.
  PrecalculationOptions precalculationOptions = 12;
  // Affiliate address for the transfer.
  string affiliateAddress = 13;
  // The number of times to attempt approval validation. If 0 / not specified, we default to only one.
  string numAttempts = 14 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
}

// PrecalculationOptions defines the options for precalculating the balances.
message PrecalculationOptions {
  // The timestamp to override with when calculating the balances.
  string overrideTimestamp = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  // The IDs to override for the transfer. Only applicable if using this option in precalculation.
  repeated UintRange badgeIdsOverride = 2;
}

// MerklePathItem represents an item in a Merkle path.
message MerklePathItem {
  // The hash of the sibling node (aunt) in the Merkle path.
  string aunt = 1;
  // Indicates whether the aunt node is on the right side of the path.
  bool onRight = 2;
}

// MerkleProof represents a Merkle proof, consistent with Tendermint/Crypto Merkle tree.
message MerkleProof {
  // The hash of the leaf node for which the proof is generated.
  string leaf = 1;
  // List of Merkle path items (aunts) that make up the proof.
  repeated MerklePathItem aunts = 2;
  // The signature of the leaf node tying the address to the leaf node.
  string leafSignature = 3;
}

// ETHSignatureProof represents an Ethereum signature proof for a challenge.
message ETHSignatureProof {
  // The nonce that was signed. The signature scheme is ETHSign(nonce + "-" + creatorAddress).
  string nonce = 1;
  // The Ethereum signature of the nonce.
  string signature = 2;
}

Last updated