# refund-crowdloan (/docs/tx/refund-crowdloan)

Returns contributions (excluding the creator's) to their contributors.
Only the creator may call it. Each call refunds at most 50 contributors,
so large loans may need several `refund_crowdloan` calls before
everyone is paid back. Once all contributors are refunded, the creator
runs `dissolve_crowdloan`, which returns the creator's remaining
contribution (including the deposit) and removes the loan.

| Signer    | Origin                                 | Pallet    | Wraps                                                              |
| --------- | -------------------------------------- | --------- | ------------------------------------------------------------------ |
| `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.refund`](/code/pallets/crowdloan/src/lib.rs#L686-L750) |

## Parameters [#parameters]

| Parameter      | Type    | Required | Description                                                |
| -------------- | ------- | -------- | ---------------------------------------------------------- |
| `crowdloan_id` | integer | yes      | Identifier of the crowdloan, assigned when it was created. |

Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58
address, an address-book or proxy-book name, or a local wallet/hotkey name.

## CLI [#cli]

Preview with `--dry-run` (shows fee, effects, and policy result without
submitting), then submit:

```bash
btcli tx refund-crowdloan \
  --crowdloan-id <int> --dry-run
btcli tx refund-crowdloan \
  --crowdloan-id <int> -w my_coldkey
```

## Python [#python]

```python
import bittensor as bt
from bittensor.wallet import Wallet

wallet = Wallet(name="my_coldkey", hotkey="my_hotkey")
intent = bt.RefundCrowdloan(crowdloan_id=0)

sub = bt.Subtensor()
plan = sub.plan(intent, wallet)   # fee, effects, policy — no submission
result = sub.execute(intent, wallet)
if not result.success:
    print(result.error.code, result.error.remediation)
```

(`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:`
— see [The client](/docs/concepts/client).) Or build the intent by op name, as
an agent would:

```python
result = sub.execute_tool("refund_crowdloan", {...}, wallet)
```

## On-chain implementation [#on-chain-implementation]

`Crowdloan.refund` — [`pallets/crowdloan/src/lib.rs#L688`](/code/pallets/crowdloan/src/lib.rs#L686-L750):

```rust
#[pallet::call_index(4)]
#[pallet::weight(T::WeightInfo::refund(T::RefundContributorsLimit::get()))]
pub fn refund(
    origin: OriginFor<T>,
    #[pallet::compact] crowdloan_id: CrowdloanId,
) -> DispatchResultWithPostInfo {
    let who = ensure_signed(origin)?;

    let mut crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?;

    // Ensure the crowdloan is not finalized
    ensure!(!crowdloan.finalized, Error::<T>::AlreadyFinalized);

    // Only the creator can refund the crowdloan
    ensure!(who == crowdloan.creator, Error::<T>::InvalidOrigin);

    let mut refunded_contributors: Vec<T::AccountId> = vec![];
    let mut refund_count = 0;

    // Assume everyone can be refunded
    let mut all_refunded = true;

    // We try to refund all contributors (excluding the creator)
    let contributions = Contributions::<T>::iter_prefix(crowdloan_id)
        .filter(|(contributor, _)| *contributor != crowdloan.creator);
    for (contributor, amount) in contributions {
        if refund_count >= T::RefundContributorsLimit::get() {
            // Not everyone can be refunded
            all_refunded = false;
            break;
        }

        CurrencyOf::<T>::transfer(
            &crowdloan.funds_account,
            &contributor,
            amount,
            Preservation::Expendable,
        )?;

        refunded_contributors.push(contributor);
        crowdloan.raised = crowdloan.raised.saturating_sub(amount);
        refund_count = refund_count.checked_add(1).ok_or(Error::<T>::Overflow)?;
    }

    crowdloan.contributors_count = crowdloan
        .contributors_count
        .checked_sub(refund_count)
        .ok_or(Error::<T>::Underflow)?;
    Crowdloans::<T>::insert(crowdloan_id, &crowdloan);

    // Clear refunded contributors
    for contributor in refunded_contributors {
        Contributions::<T>::remove(crowdloan_id, &contributor);
    }

    if all_refunded {
        Self::deposit_event(Event::<T>::AllRefunded { crowdloan_id });
        // The loop didn't run fully, we refund the unused weights.
        Ok(Some(T::WeightInfo::refund(refund_count)).into())
    } else {
        Self::deposit_event(Event::<T>::PartiallyRefunded { crowdloan_id });
        // The loop ran fully, we don't refund anything.
        Ok(().into())
    }
}
```

Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/<path>` (index: [`/code/index.json`](/code/index.json)).
