# MsgTransferTokens

Executes token transfers between addresses.

## Proto Definition

```protobuf
message MsgTransferTokens {
  string creator = 1; // Address initiating the transfer
  string collectionId = 2; // Collection containing tokens to transfer
  repeated Transfer transfers = 3; // Transfer operations (must pass approvals)
}

message MsgTransferTokensResponse {
  repeated ApprovalUsed approvalsUsed = 1;
  repeated CoinTransferProto coinTransfers = 2;
  repeated Balance balancesTransferred = 3;
  repeated string reviewItems = 4;
}

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.
  PrecalculateBalancesFromApprovalDetails 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;
}

message PrecalculateBalancesFromApprovalDetails {
  string approvalId = 1;
  string approvalLevel = 2;  // "collection", "incoming", or "outgoing"
  string approverAddress = 3;  // "" if collection-level
  string version = 4 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  PrecalculationOptions precalculationOptions = 5;
}

message PrecalculationOptions {
  string overrideTimestamp = 1 [(gogoproto.customtype) = "Uint", (gogoproto.nullable) = false];
  repeated UintRange tokenIdsOverride = 2;
}
```

## Response

The response includes structured data about the transfer execution:

* **`approvalsUsed`**: Details of which approvals were matched and consumed for the transfer
* **`coinTransfers`**: Any coin transfers that occurred as side effects (e.g., from approval criteria with coin transfer requirements)
* **`balancesTransferred`**: The actual token balances that were transferred
* **`reviewItems`**: Advisory strings about the transaction (see [Review Items](https://github.com/trevormil/bitbadges-docs/blob/master/x-tokenization/concepts/approval-change-events.md#review-items))

## Auto-Scan vs Prioritized Approvals

The transfer approval system operates in two modes to balance efficiency and precision:

### Auto-Scan Mode (Default)

By default, the system automatically scans through available approvals to find a match for the transfer. This mode:

* **Works with**: Approvals using [Empty Approval Criteria](https://docs.bitbadges.io/token-standard/examples/empty-approval-criteria) (no side effects)
* **Behavior**: Automatically finds and uses the first matching approval
* **Use case**: Simple transfers without custom logic or side effects
* **No versioning required**: The system handles approval selection automatically

### Prioritized Approvals (Required for Side Effects)

**CRITICAL REQUIREMENT**: Any transfer with side effects or custom approval criteria MUST always be prioritized with proper versioning set. No exceptions.

#### Race Condition Protection

The versioning control ensures that before submitting, the user knows the exact approval they are using:

```typescript
"prioritizedApprovals": [
    {
        "approvalId": "abc123",
        "approvalLevel": "collection",
        "approverAddress": "",
        "version": "2" // Must specify exact version
    }
]
```

#### Example: Coin Transfer Approval

```typescript
// MUST be prioritized - has coin transfer side effects
"prioritizedApprovals": [
    {
        "approvalId": "reward-approval",
        "approvalLevel": "collection",
        "approverAddress": "",
        "version": "1"
    }
],
"onlyCheckPrioritizedCollectionApprovals": true
```

#### Example: Auto-Scan Safe Transfer

```typescript
// Can use auto-scan - no side effects
"prioritizedApprovals": [], // Empty - will auto-scan

// Only will succeed if it finds an approval has empty approval criteria with no custom logic
```

### Control Flags

* `onlyCheckPrioritizedCollectionApprovals`: If true, only check prioritized approvals
* `onlyCheckPrioritizedIncomingApprovals`: If true, only check prioritized incoming approvals
* `onlyCheckPrioritizedOutgoingApprovals`: If true, only check prioritized outgoing approvals

**Setting these to `true` is recommended when using prioritized approvals to ensure deterministic behavior.**

### Related Documentation

* [Empty Approval Criteria](https://docs.bitbadges.io/token-standard/examples/empty-approval-criteria) - Template for auto-scan compatible approvals
* [Approval Criteria](https://docs.bitbadges.io/token-standard/learn/approval-criteria) - Understanding approval complexity
* [Coin Transfers](https://docs.bitbadges.io/token-standard/learn/approval-criteria/usdbadge-transfers) - Side effect examples

## Transfer Validation Process

Each transfer undergoes a systematic validation process to ensure security and proper authorization:

### Validation Steps

```
PRE. CALCULATE BALANCES (if needed)
  └── If precalculateBalancesFromApproval is specified, we will use the predeterminedBalances from the specified approval to pre-calculate the balances at execution time.

1. BALANCE CHECK
   └── Verify sender has sufficient balances for the transfer including ownership times
   └── FAIL if insufficient balances

2. COLLECTION APPROVAL CHECK
   └── Scan collection-level approvals (prioritized first, then auto-scan) to find a match for the entire transfer
   └── If match found:
       ├── Check approval criteria (merkle proofs, amounts, timing, etc.) and constraints
       ├── Check if it overrides sender approvals (overridesFromOutgoingApprovals)
       ├── Check if it overrides recipient approvals (overridesToIncomingApprovals)
       └── PROCEED with override flags set
   └── Else:
        └── Continue scanning
   └── If some attempted transfer balances have no valid collection approval: FAIL

3. SENDER APPROVAL CHECK (if not overridden)
   └── Check sender's outgoing approvals for this transfer
   └── Verify approval criteria and constraints
   └── FAIL if no valid outgoing approval found

4. RECIPIENT APPROVAL CHECK (if not overridden)
   └── Check each recipient's incoming approvals
   └── Verify approval criteria and constraints
   └── FAIL if any recipient lacks valid incoming approval

5. EXECUTE TRANSFER
   └── Update balances
   └── Execute any approved side effects
   └── Emit transfer events
   └── SUCCESS
```

### Override Behavior

Collection approvals can override user-level approvals:

* **`overridesFromOutgoingApprovals: true`** - Forcefully skips sender approval check
* **`overridesToIncomingApprovals: true`** - Forcefully skips recipient approval checks

This allows collection managers to enable transfers that would otherwise be blocked by user settings.

### Failure Points

Transfers fail at the first validation step that doesn't pass:

1. **Insufficient Balances** - Sender doesn't own the tokens
2. **No Collection Approval** - No valid collection-level approval found
3. **Blocked by Sender** - Sender's outgoing approvals reject the transfer
4. **Blocked by Recipient** - Recipient's incoming approvals reject the transfer

### ETH Signature Proofs

ETH Signature Proofs are required when transfers use [ETH Signature Challenges](https://docs.bitbadges.io/token-standard/learn/approval-criteria/eth-signature-challenges). Each proof contains:

* **`nonce`**: The unique identifier that was signed
* **`signature`**: The Ethereum signature of the message `nonce + "-" + creatorAddress`

**Important**: Each signature can only be used once per challenge tracker. The system tracks used signatures to prevent replay attacks.

### Related Documentation

* [Transferability](https://docs.bitbadges.io/token-standard/learn/transferability) - Approval system overview
* [Collection Approvals](https://docs.bitbadges.io/token-standard/examples/building-collection-approvals) - Collection-level controls
* [User Approvals](https://docs.bitbadges.io/token-standard/examples/building-user-approvals) - User-level settings
* [ETH Signature Challenges](https://docs.bitbadges.io/token-standard/learn/approval-criteria/eth-signature-challenges) - Ethereum signature requirements

## Precalculating Balances

When using `precalculateBalancesFromApproval`, you can override certain calculation parameters using `precalculationOptions`. These options only apply when the corresponding flags are enabled in the approval's `IncrementedBalances`.

### PrecalculationOptions

| Field               | Type          | Description                                                      |
| ------------------- | ------------- | ---------------------------------------------------------------- |
| `overrideTimestamp` | string (Uint) | Override timestamp for ownership time calculation (milliseconds) |
| `tokenIdsOverride`  | UintRange\[]  | Override token IDs (must be single ID if provided)               |

**overrideTimestamp**:

* Only applies when `IncrementedBalances.durationFromTimestamp` is set and `allowOverrideTimestamp` is `true`
* If zero or not provided, uses current block time
* Used to calculate ownership times as `[overrideTimestamp, overrideTimestamp + durationFromTimestamp - 1]`

**tokenIdsOverride**:

* Only applies when `IncrementedBalances.allowOverrideWithAnyValidToken` is `true`
* Must contain exactly one `UintRange` with `start == end` (single token ID)
* Token ID must be in the collection's `validTokenIds`

For detailed documentation, see [Predetermined Balances](https://docs.bitbadges.io/learn/approval-criteria/predetermined-balances#precalculation-options).

## Collection ID Auto-Lookup

If you specify `collectionId` as `"0"`, it will automatically lookup the latest collection ID created. This can be used if you are creating a collection and do not know the official collection ID yet but want to perform a multi-message transaction.

## Usage Example

```bash
# CLI command
bitbadgeschaind tx tokenization transfer-tokens '[tx-json]' --from sender-key
```

### JSON Example

```json
{
    "creator": "bb1initiator123...",
    "collectionId": "1",
    "transfers": [
        {
            "from": "bb1sender123...",
            "toAddresses": ["bb1recipient123..."],
            // Balances to transfer (can be left blank if you are using precalculateBalancesFromApproval)
            "balances": [
                {
                    "amount": "10",
                    "ownershipTimes": [
                        { "start": "1", "end": "18446744073709551615" }
                    ],
                    "tokenIds": [{ "start": "1", "end": "5" }]
                }
            ],
            // Specific approval to calculate balances dynamically for (from the approvalCriteria.predeterminedBalances)
            "precalculateBalancesFromApproval": {
                "approvalId": "approval-1",
                "approvalLevel": "collection",
                "approverAddress": "",
                "version": "1",
                "precalculationOptions": {
                    "overrideTimestamp": "0", // Optional: override timestamp (milliseconds)
                    "tokenIdsOverride": [] // Optional: override token IDs (must be single ID if provided)
                }
            },
            // Supply all merkle proofs for any merkle challenges that need to be satisfied
            "merkleProofs": [],
            // Supply all ETH signature proofs for any ETH signature challenges that need to be satisfied
            "ethSignatureProofs": [],
            // Memo for the transfer (can be left blank)
            "memo": "",

            // Any approval IDs that you want to prioritize for this transfer
            // Note: All approvals with side effects must be prioritized with proper versioning
            "prioritizedApprovals": [
                {
                    "approvalId": "abc123",
                    "approvalLevel": "collection",
                    "approverAddress": "", // blank for collection, otherwise the address of the approver
                    "version": "0"
                }
            ],

            // If specified, we will stop checking after the prioritized approvals list.
            // If false, we will check prioritized first, but then continue to check the rest of the approvals in auto-scan mode
            "onlyCheckPrioritizedCollectionApprovals": false,
            "onlyCheckPrioritizedIncomingApprovals": false,
            "onlyCheckPrioritizedOutgoingApprovals": false
        }
    ]
}
```
