# Leases

A lease is a mechanism in Algorand that reserves exclusive rights to submit transactions with a specific identifier for a defined period, preventing duplicate or competing transactions from the same account during that time.

Leases provide security for transactions in three ways: they enable exclusive transaction execution (useful for recurring payments), help mitigate fee variability, and secure long-running smart contracts. When a transaction includes a _Lease_ value (\[32\]byte), the network creates a `{ Sender : Lease }` pair that persists on the validation node until the transaction’s _LastValid_ round expires. This creates a “lock” that prevents any future transaction from using the same `{ Sender : Lease }` pair until expiration.

The typical one-time _payment_ or _asset_ “send” transaction is short-lived and may not necessarily benefit from including a _Lease_ value, but failing to define one within certain smart contract designs may leave an account vulnerable to a denial-of-service attack.

## How Leases Work

Every transaction in Algorand includes a _Header_ with required and optional validation fields. The required fields _FirstValid_ and _LastValid_ define a time window of up to 1000 rounds during which the transaction can be validated by the network. On MainNet, this creates a validity window of up to 70 minutes. Smart contracts often calculate a specific validity window and include a _Lease_ value in their validation logic to enable secure transactions for payments, key management and other scenarios.

Let’s take a look at why you may want to use the _Lease_ field and when you definitely should.

## Step by Step

Let’s examine a simple example where Alice sends Algo to Bob. This basic transaction is short-lived and typically wouldn’t need a lease under normal network conditions.

```
$ goal clerk send –from $ALICE –to $BOB –amount $AMOUNT
```

Under normal network conditions, this transaction will be confirmed in the next round. Bob gets his money from Alice and there are no further concerns.

However, now let’s assume the network is congested, fees are higher than normal and Alice desires to minimize her fee spend while ensuring only a single payment transaction to Bob is confirmed by the network. Alice may construct a series of transactions to Bob, each defining identical _Lease_, _FirstValid_ and _LastValid_ values but increasing _Fee_ amounts, then broadcast them to the network.

```
# Define transaction fields

$ LEASE_VALUE=$(echo "Lease value (at most 32-bytes)" | xxd -p | base64)

$ FIRST_VALID=$(goal node status | grep "Last committed block:" | awk '{ print $4 }')

$ VALID_ROUNDS=1000

$ LAST_VALID=$(($FIRST_VALID+$VALID_ROUNDS))

$ FEE=1000

# Create the initial signed transaction and write it out to a file

$ goal clerk send –-from $ALICE –-to $BOB –-amount $AMOUNT \
                  –-lease $LEASE_VALUE --firstvalid $FIRST_VALID –-lastvalid $LAST_VALID \
                  –-fee $FEE –-out $FEE.stxn --sign
```

Above, Alice defined values to use within her transactions. The `$LEASE_VALUE` must be base64 encoded and not exceed 32-bytes, typically using a hash value. The `$FIRST_VALID` value is obtained from the network and `$VALID_ROUNDS` is set to its maximum value of 1000 to calculate `$LAST_VALID`. Initially `$FEE` is set to the minimum and will be the only value modified in subsequent transactions.

Alice now broadcasts the initial transaction with `goal clerk rawsend –-filename 1000.stxn` but due to network congestion and high fees, `goal` will continue awaiting confirmation until `$LAST_VALID`. During the validation window Alice may construct additional nearly identical transactions with _only_ higher fees and broadcast each one concurrently.

```
# Redefine ONLY the FEE value

$ FEE=$(($FEE+1000))

# Broadcast additional signed transaction

$ goal clerk send –-from $ALICE –-to $BOB –-amount $AMOUNT \
                  –-lease $LEASE_VALUE --firstvalid $FIRST_VALID –-lastvalid $LAST_VALID \
                  –-fee $FEE
```

Alice will continue to increase the `$FEE` value with each subsequent transaction. At some point, one of the transactions will be approved, likely the one with the highest fee at that time, and the “lock” is now set for `{ $ALICE : $LEASE_VALUE }` until `$LAST_VALID`. Alice is assured that none of her previously submitted pending transaction can be validated. Bob is paid just one time.

## Potential Pitfalls

That was a rather simple scenario and unlikely during normal network conditions. Next, let’s uncover some security concerns Alice needs to guard against. Once Alice broadcasts her initial transaction, she must ensure all subsequent transactions utilize the exact same values for _FirstValid_, _LastValid_ and _Lease_. Notice in the second transaction only the _Fee_ is incremented, ensuring the other values remain static. If Alice executes the initial code block twice, the `$FIRST_VALID` value will be updated by querying the network presently, thus extending the validation window for `$LEASE_VALUE$ to be evaluated.

Similarly, if the `$LEASE_VALUE` is changed within a static validation window, multiple transactions may be confirmed. Remember, the “lock” is a mutual exclusion on `{ Sender : Lease }`; changing either creates a new lock.

After the validation window expires, Alice is free to reuse the `$LEASE_VALUE` in any new transaction. This is a common practice for recurring payments.

## Code implementation

Following you will find an example of implementing leases using Algokit Utils in Python and Typescript

```typescript
// Create a lease value - this could be any unique string or Uint8Array

const lease: string | Uint8Array = 'unique-lease-value'

// Send a payment transaction with a lease

// If another transaction with the same lease and sender tries to execute within the validity window,
// it will be rejected

await algorand.send.payment({
  sender: randomAccountA,
  receiver: randomAccountB,
  amount: algo(1),
  lease: lease,
  // Optional: Set a custom validity window for the lease
  validityWindow: 100, // Number of rounds the lease is valid for
})

// Attempting to send another transaction with the same lease and sender within the validity window
// will cause the transaction to be rejected
try {
  await algorand.send.payment({
    sender: randomAccountA, // Same sender as first transaction
    receiver: randomAccountC,
    amount: algo(10),
    lease: lease,
    suppressLog: true, // Prevent AlgoKit Utils from logging the expected error
  })
} catch (_error) {
  console.log('Transaction rejected due to active lease')
}
```

```python
# Create a lease value - this could be any unique string or Uint8Array

lease: bytes = b"unique-lease-value"

"""
Send a payment transaction with a lease
If another transaction with the same lease and sender tries to execute within the validity window,
it will be rejected
"""
result = algorand_client.send.payment(
    PaymentParams(
        sender=account_a.address,
        receiver=account_a.address,
        amount=AlgoAmount(algo=1),
        lease=lease,
        # Optional: Set a custom validity window for the lease
        validity_window=2,  # Number of rounds the lease is valid for
    )
)

"""
Attempting to send another transaction with the same lease and sender within the validity window
will cause the transaction to be rejected
"""
try:
    algorand_client.send.payment(
        PaymentParams(
            sender=account_a.address,  # Same sender as first transaction
            receiver=account_a.address,
            amount=AlgoAmount(algo=1),
            lease=lease,
        ),
        send_params=SendParams(
            suppress_log=True,
        ),
    )
except Exception:
    print("Transaction rejected due to active lease")

"""
Sending empty payment txn to progress the block round
"""
algorand_client.send.payment(
    PaymentParams(
        sender=account_a.address,
        receiver=account_a.address,
        amount=AlgoAmount(algo=0),
    )
)

"""
This payment transaction with the same lease and sender will be accepted
because the first_valid_round is outside the validity window of the first transaction
"""
pay_txn = algorand_client.send.payment(
    PaymentParams(
        sender=account_a.address,  # Same sender as first transaction
        receiver=account_a.address,
        amount=AlgoAmount(algo=1),
        lease=lease,
    )
)
```
