# Ваша первая транзакция

В этом руководстве в пошаговом порядке описывается создание, отправка и проверка транзакций, отправленных в Aptos Blockchain:

1. Создайте представление учетной записи.

Каждая учетная запись Aptos имеет уникальный адрес учетной записи. Владелец этой учетной записи владеет парой открытого и закрытого ключей, которая сопоставляется с адресом учетной записи Aptos, и, в свою очередь, ключом аутентификации, хранящимся в этой учетной записи.

{% hint style="info" %}
**ПРИМЕЧАНИЕ**

Подробнее об учетных записях Aptos см. в разделе [Учетные записи](https://cr-nepos.gitbook.io/aptos-ru/osnovy/uchyotnye-zapisi-akkaunty).
{% endhint %}

2\. Подготовьте оболочку для интерфейсов REST.

Aptos предоставляет [REST API](https://fullnode.devnet.aptoslabs.com/spec.html) для взаимодействия с блокчейном. Эти шаги подготавливают оболочки вокруг этого API для получения информации об учетной записи, а также для создания транзакции, ее подписания и отправки.

3\. Подготовьте оболочку для интерфейса Faucet.

Используя интерфейс Faucet в Aptos devnet, этот обучающий код автоматически создает аккаунт с адресом `0x1` и пополняет его.

4\. Объедините вышеуказанные оболочки в приложение, выполните и проверьте.

### Прежде чем начать

Сначала убедитесь, что вы выполнили следующие шаги, чтобы вы могли запустить руководство.

1. Клонируйте репозиторий Aptos.

```
git clone https://github.com/aptos-labs/aptos-core.git
```

2\. Перейдите`cd` в каталог `aptos-core` .

```
cd aptos-core
```

3\. Проверьте ветку devnet, используя `git checkout --track origin/devnet`.

4\. Запустите bash-скрипт `scripts/dev_setup.sh` , как показано ниже. Это подготовит вашу среду разработки.

```
./scripts/dev_setup.sh
```

5\. Обновите текущую среду оболочки.

```
source ~/.cargo/env
```

Теперь, когда ваша среда разработки готова, вы можете запустить это руководство.

### Источник GitHub <a href="#github-source" id="github-source"></a>

Перейдите по ссылкам ниже, чтобы получить доступ к исходному коду руководства:

{% tabs %}
{% tab title="Python" %}
См. `first_transaction.py` код в [Python version](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/python) данного руководства.
{% endtab %}

{% tab title=" Rust" %}
См. `first_transaction.rs` код в [Rust project](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/rust) данного руководства.
{% endtab %}

{% tab title="Typescript" %}
См. `first_transaction.ts` код в [Typescript project](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/typescript) данного руководства.
{% endtab %}
{% endtabs %}

### Шаг 1: Создайте представление учетной записи <a href="#step-1-create-a-representation-of-an-account" id="step-1-create-a-representation-of-an-account"></a>

Эти шаги создают представление учетной записи. См. также разделы [Учетные записи](https://cr-nepos.gitbook.io/aptos-ru/osnovy/uchyotnye-zapisi-akkaunty) и [Creating a Signed Transaction](https://aptos.dev/guides/creating-a-signed-transaction).

{% tabs %}
{% tab title="Python" %}

```
class Account:
    """Represents an account as well as the private, public key-pair for the Aptos blockchain."""

    def __init__(self, seed: bytes = None) -> None:
        if seed is None:
            self.signing_key = SigningKey.generate()
        else:
            self.signing_key = SigningKey(seed)

    def address(self) -> str:
        """Returns the address associated with the given account"""

        return self.auth_key()

    def auth_key(self) -> str:
        """Returns the auth_key for the associated account"""

        hasher = hashlib.sha3_256()
        hasher.update(self.signing_key.verify_key.encode() + b'\x00')
        return hasher.hexdigest()

    def pub_key(self) -> str:
        """Returns the public key for the associated account"""

        return self.signing_key.verify_key.encode().hex()
```

{% endtab %}

{% tab title="Rust" %}

```
pub struct Account {
    signing_key: SecretKey,
}

impl Account {
    /// Represents an account as well as the private, public key-pair for the Aptos blockchain.
    pub fn new(priv_key_bytes: Option<Vec<u8>>) -> Self {
        let signing_key = match priv_key_bytes {
            Some(key) => SecretKey::from_bytes(&key).unwrap(),
            None => SecretKey::generate(&mut rand::rngs::StdRng::from_seed(OsRng.gen())),
        };

        Account { signing_key }
    }
    /// Returns the address associated with the given account
    pub fn address(&self) -> String {
        self.auth_key()
    }

    /// Returns the auth_key for the associated account
    pub fn auth_key(&self) -> String {
        let mut sha3 = Sha3::v256();
        sha3.update(PublicKey::from(&self.signing_key).as_bytes());
        sha3.update(&vec![0u8]);

        let mut output = [0u8; 32];
        sha3.finalize(&mut output);
        hex::encode(output)
    }

    /// Returns the public key for the associated account
    pub fn pub_key(&self) -> String {
        hex::encode(PublicKey::from(&self.signing_key).as_bytes())
    }
}
```

{% endtab %}

{% tab title="Typescript" %}

```
/** A subset of the fields of a TransactionRequest, for this tutorial */
export type TxnRequest = Record<string, any> & { sequence_number: string };

/** Represents an account as well as the private, public key-pair for the Aptos blockchain */
export class Account {
  signingKey: Nacl.SignKeyPair;

  constructor(seed?: Uint8Array | undefined) {
    if (seed) {
      this.signingKey = Nacl.sign.keyPair.fromSeed(seed);
    } else {
      this.signingKey = Nacl.sign.keyPair();
    }
  }

  /** Returns the address associated with the given account */
  address(): string {
    return this.authKey();
  }

  /** Returns the authKey for the associated account */
  authKey(): string {
    let hash = SHA3.sha3_256.create();
    hash.update(Buffer.from(this.signingKey.publicKey));
    hash.update("\x00");
    return hash.hex();
  }

  /** Returns the public key for the associated account */
  pubKey(): string {
    return Buffer.from(this.signingKey.publicKey).toString("hex");
  }
}
```

{% endtab %}
{% endtabs %}

### Шаг 2: REST интерфейс <a href="#step-2-rest-interface" id="step-2-rest-interface"></a>

Хотя данные из интерфейса REST можно считывать напрямую, следующие примеры кода демонстрируют более эргономичный подход при использовании интерфейса REST для:

* Получение данных реестра из полной ноды, включая данные учетной записи и ресурсов учетной записи.
* Построение подписанных транзакций, представленных в формате JSON.

{% tabs %}
{% tab title="Python" %}

```
class RestClient:
    """A wrapper around the Aptos-core Rest API"""

    def __init__(self, url: str) -> None:
        self.url = url
```

{% endtab %}

{% tab title="Rust" %}

```
#[derive(Clone)]
pub struct RestClient {
    url: String,
}

impl RestClient {
    /// A wrapper around the Aptos-core Rest API
    pub fn new(url: String) -> Self {
        Self { url }
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```
/** A wrapper around the Aptos-core Rest API */
export class RestClient {
  url: string;

  constructor(url: string) {
    this.url = url;
  }

```

{% endtab %}
{% endtabs %}

#### Шаг 2.1: Чтение аккаунта <a href="#step-21-reading-an-account" id="step-21-reading-an-account"></a>

Ниже приведены оболочки для запроса данных учетной записи.

{% tabs %}
{% tab title="Python" %}

```
 def account(self, account_address: str) -> Dict[str, str]:
        """Returns the sequence number and authentication key for an account"""

        response = requests.get(f"{self.url}/accounts/{account_address}")
        assert response.status_code == 200, f"{response.text} - {account_address}"
        return response.json()

    def account_resource(self, account_address: str, resource_type: str) -> Optional[Dict[str, Any]]:
        response = requests.get(f"{self.url}/accounts/{account_address}/resource/{resource_type}")
        if response.status_code == 404:
            return None
        assert response.status_code == 200, response.text
        return response.json()
```

{% endtab %}

{% tab title="Rust" %}

```
/// Returns the sequence number and authentication key for an account
    pub fn account(&self, account_address: &str) -> serde_json::Value {
        let res =
            reqwest::blocking::get(format!("{}/accounts/{}", self.url, account_address)).unwrap();

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                account_address,
            );
        }

        res.json().unwrap()
    }

    /// Returns all resources associated with the account
    pub fn account_resource(
        &self,
        account_address: &str,
        resource_type: &str,
    ) -> Option<serde_json::Value> {
        let res = reqwest::blocking::get(format!(
            "{}/accounts/{}/resource/{}",
            self.url, account_address, resource_type,
        ))
        .unwrap();

        if res.status() == 404 {
            None
        } else if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                account_address,
            );
            unreachable!()
        } else {
            Some(res.json().unwrap())
        }
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```
/** Returns the sequence number and authentication key for an account */
  async account(accountAddress: string): Promise<Record<string, string> & { sequence_number: string }> {
    const response = await fetch(`${this.url}/accounts/${accountAddress}`, { method: "GET" });
    if (response.status != 200) {
      assert(response.status == 200, await response.text());
    }
    return await response.json();
  }

  /** Returns all resources associated with the account */
  async accountResource(accountAddress: string, resourceType: string): Promise<any> {
    const response = await fetch(`${this.url}/accounts/${accountAddress}/resource/${resourceType}`, { method: "GET" });
    if (response.status == 404) {
      return null;
    }
    if (response.status != 200) {
      assert(response.status == 200, await response.text());
    }
    return await response.json();
  }

  
```

{% endtab %}
{% endtabs %}

#### Шаг 2.2: Отправка транзакции <a href="#step-22-submitting-a-transaction" id="step-22-submitting-a-transaction"></a>

Далее демонстрируются основные функции построения, подписания и ожидания транзакции.

{% tabs %}
{% tab title="Python" %}

```
def generate_transaction(self, sender: str, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Generates a transaction request that can be submitted to produce a raw transaction that
        can be signed, which upon being signed can be submitted to the blockchain. """

        account_res = self.account(sender)
        seq_num = int(account_res["sequence_number"])
        txn_request = {
            "sender": f"0x{sender}",
            "sequence_number": str(seq_num),
            "max_gas_amount": "2000",
            "gas_unit_price": "1",
            "gas_currency_code": "XUS",
            "expiration_timestamp_secs": str(int(time.time()) + 600),
            "payload": payload,
        }
        return txn_request

    def sign_transaction(self, account_from: Account, txn_request: Dict[str, Any]) -> Dict[str, Any]:
        """Converts a transaction request produced by `generate_transaction` into a properly signed
        transaction, which can then be submitted to the blockchain."""

        res = requests.post(f"{self.url}/transactions/signing_message", json=txn_request)
        assert res.status_code == 200, res.text
        to_sign = bytes.fromhex(res.json()["message"][2:])
        signature = account_from.signing_key.sign(to_sign).signature
        txn_request["signature"] = {
            "type": "ed25519_signature",
            "public_key": f"0x{account_from.pub_key()}",
            "signature": f"0x{signature.hex()}",
        }
        return txn_request

    def submit_transaction(self, txn: Dict[str, Any]) -> Dict[str, Any]:
        """Submits a signed transaction to the blockchain."""

        headers = {'Content-Type': 'application/json'}
        response = requests.post(f"{self.url}/transactions", headers=headers, json=txn)
        assert response.status_code == 202, f"{response.text} - {txn}"
        return response.json()
    
    def execute_transaction_with_payload(self, account_from: Account, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Execute a transaction for the given payload."""
        
        txn_request = self.generate_transaction(account_from.address(), payload)
        signed_txn = self.sign_transaction(account_from, txn_request)
        return self.submit_transaction(signed_txn)

    def transaction_pending(self, txn_hash: str) -> bool:
        response = requests.get(f"{self.url}/transactions/{txn_hash}")
        if response.status_code == 404:
            return True
        assert response.status_code == 200, f"{response.text} - {txn_hash}"
        return response.json()["type"] == "pending_transaction"

    def wait_for_transaction(self, txn_hash: str) -> None:
        """Waits up to 10 seconds for a transaction to move past pending state."""

        count = 0
        while self.transaction_pending(txn_hash):
            assert count < 10, f"transaction {txn_hash} timed out"
            time.sleep(1)
            count += 1
        response = requests.get(f"{self.url}/transactions/{txn_hash}")
        assert "success" in response.json(), f"{response.text} - {txn_hash}"
```

{% endtab %}

{% tab title="Rust" %}

```
    /// Generates a transaction request that can be submitted to produce a raw transaction that can be signed, which upon being signed can be submitted to the blockchain.
    pub fn generate_transaction(
        &self,
        sender: &str,
        payload: serde_json::Value,
    ) -> serde_json::Value {
        let account_res = self.account(sender);

        let seq_num = account_res
            .get("sequence_number")
            .unwrap()
            .as_str()
            .unwrap()
            .parse::<u64>()
            .unwrap();

        // Unix timestamp, in seconds + 10 minutes
        let expiration_time_secs = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("Time went backwards")
            .as_secs()
            + 600;

        serde_json::json!({
            "sender": format!("0x{}", sender),
            "sequence_number": seq_num.to_string(),
            "max_gas_amount": "1000",
            "gas_unit_price": "1",
            "gas_currency_code": "XUS",
            "expiration_timestamp_secs": expiration_time_secs.to_string(),
            "payload": payload,
        })
    }

    /// Converts a transaction request produced by `generate_transaction` into a properly signed transaction, which can then be submitted to the blockchain.
    pub fn sign_transaction(
        &self,
        account_from: &mut Account,
        mut txn_request: serde_json::Value,
    ) -> serde_json::Value {
        let res = reqwest::blocking::Client::new()
            .post(format!("{}/transactions/signing_message", self.url))
            .body(txn_request.to_string())
            .send()
            .unwrap();

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                txn_request.as_str().unwrap_or(""),
            );
        }
        let body: serde_json::Value = res.json().unwrap();
        let to_sign_hex = Box::new(body.get("message").unwrap().as_str()).unwrap();
        let to_sign = hex::decode(&to_sign_hex[2..]).unwrap();
        let signature: String = ExpandedSecretKey::from(&account_from.signing_key)
            .sign(&to_sign, &PublicKey::from(&account_from.signing_key))
            .encode_hex();

        let signature_payload = serde_json::json!({
            "type": "ed25519_signature",
            "public_key": format!("0x{}", account_from.pub_key()),
            "signature": format!("0x{}", signature),
        });
        txn_request
            .as_object_mut()
            .unwrap()
            .insert("signature".to_string(), signature_payload);
        txn_request
    }

    /// Submits a signed transaction to the blockchain.
    pub fn submit_transaction(&self, txn_request: &serde_json::Value) -> serde_json::Value {
        let res = reqwest::blocking::Client::new()
            .post(format!("{}/transactions", self.url))
            .body(txn_request.to_string())
            .header("Content-Type", "application/json")
            .send()
            .unwrap();

        if res.status() != 202 {
            assert_eq!(
                res.status(),
                202,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                txn_request.as_str().unwrap_or(""),
            );
        }
        res.json().unwrap()
    }

    /// Submits a signed transaction to the blockchain.
    pub fn execution_transaction_with_payload(
        &self,
        account_from: &mut Account,
        payload: serde_json::Value,
    ) -> String {
        let txn_request = self.generate_transaction(&account_from.address(), payload);
        let signed_txn = self.sign_transaction(account_from, txn_request);
        let res = self.submit_transaction(&signed_txn);
        res.get("hash").unwrap().as_str().unwrap().to_string()
    }

    pub fn transaction_pending(&self, transaction_hash: &str) -> bool {
        let res = reqwest::blocking::get(format!("{}/transactions/{}", self.url, transaction_hash))
            .unwrap();

        if res.status() == 404 {
            return true;
        }

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                transaction_hash,
            );
        }

        res.json::<serde_json::Value>()
            .unwrap()
            .get("type")
            .unwrap()
            .as_str()
            .unwrap()
            == "pending_transaction"
    }

    /// Waits up to 10 seconds for a transaction to move past pending state.
    pub fn wait_for_transaction(&self, txn_hash: &str) {
        let mut count = 0;
        while self.transaction_pending(txn_hash) {
            assert!(count < 10, "transaction {} timed out", txn_hash);
            thread::sleep(Duration::from_secs(1));
            count += 1;
        }
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```
  /** Generates a transaction request that can be submitted to produce a raw transaction that
   can be signed, which upon being signed can be submitted to the blockchain. */
  async generateTransaction(sender: string, payload: Record<string, any>): Promise<TxnRequest> {
    const account = await this.account(sender);
    const seqNum = parseInt(account["sequence_number"]);
    return {
      sender: `0x${sender}`,
      sequence_number: seqNum.toString(),
      max_gas_amount: "2000",
      gas_unit_price: "1",
      // Unix timestamp, in seconds + 10 minutes
      expiration_timestamp_secs: (Math.floor(Date.now() / 1000) + 600).toString(),
      payload: payload,
    };
  }

  /** Converts a transaction request produced by `generate_transaction` into a properly signed
   transaction, which can then be submitted to the blockchain. */
  async signTransaction(accountFrom: Account, txnRequest: TxnRequest): Promise<TxnRequest> {
    const response = await fetch(`${this.url}/transactions/signing_message`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(txnRequest),
    });
    if (response.status != 200) {
      assert(response.status == 200, (await response.text()) + " - " + JSON.stringify(txnRequest));
    }
    const result: Record<string, any> & { message: string } = await response.json();
    const toSign = Buffer.from(result["message"].substring(2), "hex");
    const signature = Nacl.sign(toSign, accountFrom.signingKey.secretKey);
    const signatureHex = Buffer.from(signature).toString("hex").slice(0, 128);
    txnRequest["signature"] = {
      type: "ed25519_signature",
      public_key: `0x${accountFrom.pubKey()}`,
      signature: `0x${signatureHex}`,
    };
    return txnRequest;
  }

  /** Submits a signed transaction to the blockchain. */
  async submitTransaction(txnRequest: TxnRequest): Promise<Record<string, any>> {
    const response = await fetch(`${this.url}/transactions`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(txnRequest),
    });
    if (response.status != 202) {
      assert(response.status == 202, (await response.text()) + " - " + JSON.stringify(txnRequest));
    }
    return await response.json();
  }

  async executeTransactionWithPayload(accountFrom: Account, payload: Record<string, any>): Promise<string> {
    const txnRequest = await this.generateTransaction(accountFrom.address(), payload);
    const signedTxn = await this.signTransaction(accountFrom, txnRequest);
    const res = await this.submitTransaction(signedTxn);
    return res["hash"];
  }

  async transactionPending(txnHash: string): Promise<boolean> {
    const response = await fetch(`${this.url}/transactions/${txnHash}`, { method: "GET" });
    if (response.status == 404) {
      return true;
    }
    if (response.status != 200) {
      assert(response.status == 200, await response.text());
    }
    return (await response.json())["type"] == "pending_transaction";
  }

  /** Waits up to 10 seconds for a transaction to move past pending state */
  async waitForTransaction(txnHash: string) {
    let count = 0;
    while (await this.transactionPending(txnHash)) {
      assert(count < 10);
      await new Promise((resolve) => setTimeout(resolve, 1000));
      count += 1;
      if (count >= 10) {
        throw new Error(`Waiting for transaction ${txnHash} timed out!`);
      }
    }
  }

  
```

{% endtab %}
{% endtabs %}

#### Шаг 2.3: Логика, специфичная для конкретного приложения

Ниже показано, как считывать данные из блокчейна и как отправлять конкретную транзакцию.

{% tabs %}
{% tab title="Python" %}

```
def account_balance(self, account_address: str) -> Optional[int]:
        """Returns the test coin balance associated with the account"""
        return self.account_resource(account_address, "0x1::Coin::CoinStore<0x1::TestCoin::TestCoin>")

    def transfer(self, account_from: Account, recipient: str, amount: int) -> str:
        """Transfer a given coin amount from a given Account to the recipient's account address.
        Returns the sequence number of the transaction used to transfer."""

        payload = {
            "type": "script_function_payload",
            "function": "0x1::Coin::transfer",
            "type_arguments": ["0x1::TestCoin::TestCoin"],
            "arguments": [
                f"0x{recipient}",
                str(amount),
            ]
        }
        txn_request = self.generate_transaction(account_from.address(), payload)
        signed_txn = self.sign_transaction(account_from, txn_request)
        res = self.submit_transaction(signed_txn)
        return str(res["hash"])
```

{% endtab %}

{% tab title="Rust" %}

```
/// Returns the test coin balance associated with the account
pub fn account_balance(&self, account_address: &str) -> Option<u64> {
    self.account_resource(account_address, "0x1::Coin::CoinStore<0x1::TestCoin::TestCoin>")
        .unwrap()["data"]["coin"]["value"]
        .as_str()
        .and_then(|s| s.parse::<u64>().ok())
}

/// Transfer a given coin amount from a given Account to the recipient's account address.
/// Returns the sequence number of the transaction used to transfer
pub fn transfer(&self, account_from: &mut Account, recipient: &str, amount: u64) -> String {
    let payload = serde_json::json!({
        "type": "script_function_payload",
        "function": "0x1::Coin::transfer",
        "type_arguments": ["0x1::TestCoin::TestCoin"],
        "arguments": [format!("0x{}", recipient), amount.to_string()]
    });
    let txn_request = self.generate_transaction(&account_from.address(), payload);
    let signed_txn = self.sign_transaction(account_from, txn_request);
    let res = self.submit_transaction(&signed_txn);

    res.get("hash").unwrap().as_str().unwrap().to_string()
}
```

}
{% endtab %}

{% tab title="Typescript" %}

```
  /** Returns the test coin balance associated with the account */
  async accountBalance(accountAddress: string): Promise<number | null> {
    const resource = await this.accountResource(accountAddress, "0x1::Coin::CoinStore<0x1::TestCoin::TestCoin>");
    if (resource == null) {
      return null;
    }
    return parseInt(resource["data"]["coin"]["value"]);
  }

  /** Transfer a given coin amount from a given Account to the recipient's account address.
   Returns the sequence number of the transaction used to transfer. */
  async transfer(accountFrom: Account, recipient: string, amount: number): Promise<string> {
    const payload: { function: string; arguments: string[]; type: string; type_arguments: any[] } = {
      type: "script_function_payload",
      function: "0x1::Coin::transfer",
      type_arguments: ["0x1::TestCoin::TestCoin"],
      arguments: [`0x${recipient}`, amount.toString()],
    };
    const txnRequest = await this.generateTransaction(accountFrom.address(), payload);
    const signedTxn = await this.signTransaction(accountFrom, txnRequest);
    const res = await this.submitTransaction(signedTxn);
    return res["hash"].toString();
  }
}
```

{% endtab %}
{% endtabs %}

### Шаг 3: Faucet интерфейс <a href="#step-3-faucet-interface" id="step-3-faucet-interface"></a>

Сборщики Aptos Blockchain выдают учетным записям тестовые токены. Эти тестовые токены можно использовать для тестирования, например, для оплаты газа или передачи токенов между пользователями. Aptos Faucet также может создавать учетные записи, если они не существуют. Для интерфейса Aptos Faucet требуется открытый ключ, представленный в виде строки в шестнадцатеричном коде.

{% tabs %}
{% tab title="Python" %}

```
class FaucetClient:
    """Faucet creates and funds accounts. This is a thin wrapper around that."""

    def __init__(self, url: str, rest_client: RestClient) -> None:
        self.url = url
        self.rest_client = rest_client

    def fund_account(self, address: str, amount: int) -> None:
        """This creates an account if it does not exist and mints the specified amount of
        coins into that account."""
        txns = requests.post(f"{self.url}/mint?amount={amount}&address={address}")
        assert txns.status_code == 200, txns.text
        for txn_hash in txns.json():
            self.rest_client.wait_for_transaction(txn_hash)
```

{% endtab %}

{% tab title=" Rust" %}

```
pub struct FaucetClient {
    url: String,
    rest_client: RestClient,
}

impl FaucetClient {
    /// Faucet creates and funds accounts. This is a thin wrapper around that.
    pub fn new(url: String, rest_client: RestClient) -> Self {
        Self { url, rest_client }
    }

    /// This creates an account if it does not exist and mints the specified amount of coins into that account.
    pub fn fund_account(&self, auth_key: &str, amount: u64) {
        let res = reqwest::blocking::Client::new()
            .post(format!(
                "{}/mint?amount={}&auth_key={}",
                self.url, amount, auth_key
            ))
            .send()
            .unwrap();

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{}",
                res.text().unwrap_or("".to_string()),
            );
        }
        for txn_hash in res.json::<serde_json::Value>().unwrap().as_array().unwrap() {
            self.rest_client
                .wait_for_transaction(txn_hash.as_str().unwrap())
        }
    }
}
```

{% endtab %}

{% tab title="Typescript" %}

```
/** Faucet creates and funds accounts. This is a thin wrapper around that. */
export class FaucetClient {
  url: string;
  restClient: RestClient;

  constructor(url: string, restClient: RestClient) {
    this.url = url;
    this.restClient = restClient;
  }

  /** This creates an account if it does not exist and mints the specified amount of
   coins into that account */
  async fundAccount(address: string, amount: number) {
    const url = `${this.url}/mint?amount=${amount}&address=${address}`;
    const response = await fetch(url, { method: "POST" });
    if (response.status != 200) {
      assert(response.status == 200, await response.text());
    }
    const tnxHashes = (await response.json()) as Array<string>;
    for (const tnxHash of tnxHashes) {
      await this.restClient.waitForTransaction(tnxHash);
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Шаг 4: Запустите приложение <a href="#step-4-run-the-application" id="step-4-run-the-application"></a>

Наконец, мы можем запустить приложение и проверить вывод.

{% tabs %}
{% tab title="Python" %}
&#x20;Для Python3:

1. Убедитесь, что выполнены предварительные требования, описанные в разделе [Прежде чем начать](#prezhde-chem-nachat).
2. Перейдите `cd` в каталог `aptos-core/developer-docs-site/static/examples/python` .
3. Установите необходимые библиотеки: `pip3 install -r requirements.txt`.
4. Запустите пример: `python3 first_transaction.py`.
   {% endtab %}

{% tab title="Rust" %}
&#x20;Для Rust:

1. Убедитесь, что выполнены предварительные требования, описанные в разделе [Прежде чем начать](#prezhde-chem-nachat).
2. Перейдите `cd` в каталог `aptos-core/developer-docs-site/static/examples/rust` .
3. Выполните пример: `cargo run --bin first-transaction` (убедитесь, что вы используете `first-transaction` , а не `first_transaction`).
   {% endtab %}

{% tab title="Typescript" %}
Для Typescript:

1. Убедитесь, что выполнены предварительные требования, описанные в разделе [Прежде чем начать](#prezhde-chem-nachat).
2. Перейдите `cd` в каталог `aptos-core/developer-docs-site/static/examples/typescript` .
3. Установите необходимые библиотеки:  `yarn install`.
4. Выполните пример: `yarn first_transaction`.
   {% endtab %}
   {% endtabs %}

#### Результат

Результат после выполнения:

```
=== Addresses ===
Alice: e26d69b8d3ff12874358da6a4082a2ac
Bob: c8585f009c8a90f22c6b603f28b9ed8c

=== Initial Balances ===
Alice: 1000000000
Bob: 0

=== Final Balances ===
Alice: 999998957
Bob: 1000
```

Результат показывает, что Боб получил 1000 монет от Алисы. Алиса заплатила 43 монеты за газ.

#### Проверка

Данные можно проверить, посетив либо интерфейс REST, либо программу анализа:

* Учетная запись Алисы через [Aptos REST interface](https://fullnode.devnet.aptoslabs.com/accounts/e26d69b8d3ff12874358da6a4082a2ac/resources).
* Учетная запись Боба через [Aptos Explorer](https://aptos-explorer.netlify.app/account/c8585f009c8a90f22c6b603f28b9ed8c).

{% hint style="info" %}
**ПРИМЕЧАНИЕ**

Aptos devnet периодически перезапускается, поэтому приведенные выше ссылки могут не работать. Попробуйте выполнить инструкцию самостоятельно, а затем проверьте учетные записи в [Aptos Explorer](https://aptos-explorer.netlify.app/account/c8585f009c8a90f22c6b603f28b9ed8c).
{% endhint %}
