# Part 1: Smart Contracts

The entire booking system of Fiat24 is managed transparently on the <mark style="color:purple;">Arbitrum</mark> and <mark style="color:green;">Mantle</mark> blockchain, both are leading Layer 2 rollup solution for Ethereum.

## Tokens

Fiat24 users hold Fiat24 Cash Tokens — USD24, EUR24, CHF24, and CNH24 — which are ERC-20 tokens backed one-to-one by cash deposits. These tokens are issued by SR Saphirstein AG, a Swiss licensed deposit-taking institution. Customers can interact with their Fiat24 accounts either through the official Fiat24 dApp or via any compatible native crypto wallet.

{% tabs %}
{% tab title="Arbitrum" %}
{% code title="ERC20 Contract Addresses of Fiat24 Cash Tokens (in Arbitrum)" %}

```javascript
0xbE00f3db78688d9704BCb4e0a827aea3a9Cc0D62 // USD24
0xd41F1f0cf89fD239ca4c1F8E8ADA46345c86b0a4 // CHF24
0x2c5d06f591D0d8cd43Ac232c2B654475a142c7DA // EUR24
0x7288Ac74d211735374A23707D1518DCbbc0144fd // CNH24
```

{% endcode %}
{% endtab %}

{% tab title="Mantle" %}
{% code title="ERC20 Contract Addresses of Fiat24 Cash Tokens (in Mantle)" %}

```javascript
0xD598839598bBF508b97697b7D9e80054D4bcaaCC //USD24
0x0578be9C858e6562dd8cd11a738b89Ca48194dA5 //EUR24
0x53587A05ccDdCE555C2Cd7cE4C9c5Bc3D912E2f3 //CHF24
0xa0af0C397CB0A52F5E8Bc7BB89068dDDfaE9F211 //CNH24
0x3bC9fC0460cAC2DdD352848ECc0BFe204c220717 //JPY24
0x8F7F92F2A0247cc8660C4C4EF69582Bc6849B4d9 //SGD24
0x64266a15432004708e5fCA0239f664d069853374 //HKD24
```

{% endcode %}
{% endtab %}
{% endtabs %}

Fiat24 uses ERC721 based NFT to represent a Bank Account. If a user owns the NFT in their wallet, the wallet have the control to all Fiat24 services. The id of the NFT represents the account number of Fiat24.

{% tabs %}
{% tab title="Arbitrum" %}
{% code title="NFT ERC-721 Contract Addresses (in Arbitrum)" %}

```solidity
0x133CAEecA096cA54889db71956c7f75862Ead7A0
```

{% endcode %}
{% endtab %}

{% tab title="Mantle" %}
{% code title="NFT ERC-721 Contract Addresses (in Mantle)" %}

```javascript
0x4a05148119683E0A41b52fb973EEF0EE81536c47
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Functions

In this section, we introduce various types of smart contract interactions using JavaScript. The JS code shown below is implemented using **`ethers.js`**.

### 1. Mint an NFT&#x20;

Minting a Fiat24 NFT for the user is the first step in the process. The wallet provider should generate a random number with at least five digits to serve as the user’s NFT ID. Before minting, the wallet provider must verify that the NFT ID has not already been taken; otherwise, the transaction will fail with an error. A successful minting typically costs around 0.01 USD in gas fees.

We have 2 ways to mint the NFT:

1. End user mints the NFT

Wallet users call the `mintByClient()`  to mint the NFT. The NFT id is his/her Fiat24 account number, which maps to the unique Swiss IBAN account. The IBAN mapping logic refers [here](https://docs.fiat24.com/developer/part-2-api-reference#id-1.-iban).

<pre class="language-javascript" data-title="Mint NFT by users" data-line-numbers><code class="lang-javascript"><strong>// check whether the NFT id is taken
</strong>if(!(await nft.exists(10365))) {
   await nft.mintByClient(10365);
}
</code></pre>

2. Wallet provider mints the NFT for the user&#x20;

The wallet provider may choose this approach to gain two key benefits:

1. The ability to issue **customized** **debit cards** to users.
2. The ability to receive **1% referral fees (in USDC)** from each crypto conversion transaction.

To implement this approach, each wallet provider must have:

1. A **Fiat24 developer NFT** — an NFT ID in range **8000 to 8999.** This NFT will be registered as the wallet provider account.
2. A certain amount of **F24** tokens to burn, which is the ERC-20 utility token of the Fiat24 ecosystem.<br>

   <pre class="language-javascript" data-title="F24 Token Contract in Arbitrum"><code class="lang-javascript">0x22043fDdF353308B4F2e7dA2e5284E4D087449e1
   </code></pre>

Therefore, an ERC-20 `approve` transaction is required from the wallet provider’s address to the Fiat24Account contract. This means the wallet provider NFT holder must hold a sufficient balance of F24 tokens in their account.

The `mintByWallet()` function mints the client NFT and sets the wallet provider ID in the Fiat24Account NFT.

{% code title="Mint NFT #10365 by wallet" lineNumbers="true" %}

```javascript
// check whether the NFT id is taken
if(!(await nft.exists(10365))) {
    await f24.approve(0x133CAEecA096cA54889db71956c7f75862Ead7A0, 100);
    await nft.mintByWallet(0xC85251d663E69f6296ED6ca46721e28fA15Cd0B1, 10365);
}
```

{% endcode %}

{% hint style="warning" %}
By end of 2024, all 5 digits NFT were already minted by wallets, so **only 6 digits are available**.

Please only call `mintByClient` or `mintByWallet` function with a 6-digits NFT number, and starts with 1..7.

So the following number should **NOT** be mint:

1. 2563, which is not a 5-digits-number
2. 89052, which doesn't start with 1 to 7.
3. 91023, which doesn't start with 1 to 7.
4. 5623147, which is not a 6-digits-number
   {% endhint %}

### 2. Upgrade NFT

The wallet provider can mint NFTs with five or more digits for **standard clients**, who have a restricted transaction volume.

To upgrade to a **Premium client** status — represented by an NFT with one to four digits — the user must spend F24 tokens to complete the upgrade. Typically, upgrading to a four-digit NFT requires burning **1,500 F24 tokens**. The steps for this process are detailed in the script below:

{% code title="Upgrade NFT to #1025" lineNumbers="true" %}

```javascript
// Check the NFT is still available
if(!(await nft.exists(1025))) {
    //approve the 1500 F24
    await f24.approve(0x133CAEecA096cA54889db71956c7f75862Ead7A0, 150000);
    //upgrade the number and burn F24
    await nft.upgradeWithF24(1025);
}
```

{% endcode %}

<mark style="color:red;">Notice:</mark> The function `upgradeWithF24(tokenId)` is called by client's address, not from wallet provider's address.

### 3. Get Balances

Retrieve the user's multi-currency account balance accounts. It returns all balance accounts from the address which holds the client NFT.

All Fiat24 cash tokens have decimal of 2.

{% code title="Read balances" lineNumbers="true" %}

```javascript
//Read from Address
parseFloat((parseInt(await usd24.balanceOf(address))/100).toFixed(2));

//Read from NFT id #10365
parseFloat((parseInt(await usd24.balanceOfByAccountId(10365))/100).toFixed(2));
```

{% endcode %}

### **4. Get Account Status** <a href="#payments" id="payments"></a>

The user should be informed by their account status from the NFT.

{% code lineNumbers="true" %}

```javascript
const status = await fiat24account.status(tokenId);
```

{% endcode %}

The status number is an integer between 1 and 5 and stands for:

1. **Soft-Blocked**, the user can receive money only, but can't send/spend anything.
2. **Tourist** status as <mark style="color:purple;">unverified</mark> user
3. **Hard-Blocked**, the user can neither send nor receive any money.
4. **Closed**. Closed account.
5. **Live**. The user is fully verified as client.

### **5. Get limit**

Each Fiat24 user will have an overall transactional limit for 30-days <mark style="color:purple;">rolling basis</mark>. This limit value is given by Fiat24 internal risk function from our risk-based evaluation. A standard individual client with low risk profile will get 100,000 CHF (around 120,000 USD) limit for 30 days.

Partner can read this limit from NFT. Any transaction exceed this limit will throw an error from the smart contract calls.

{% code title="Get limits" lineNumbers="true" %}

```javascript
const MONTH_TIME_MS = 86400000 * 30;
const limit = await fiat24account.limit(tokenId);
const now = Date.now();
let restartLimitDate = (Number(limit.startLimitDate) * 1000) + MONTH_TIME_MS;
let usedLimit = Number(limit.usedLimit);

// Limit will not reset to new date, until user does a transaction
if (now > restartLimitDate) {
    restartLimitDate = now + MONTH_TIME_MS;
    usedLimit = 0;
}

// total 30 days client limit
console.log("clientLimit:", limit.clientLimit/100);
// current used client limit within 30 days
console.log("usedLimit:", usedLimit/100);
// restart date of the 30 days period
console.log("restartLimitDate:", restartLimitDate);
```

{% endcode %}

### **6. P2P Payments** <a href="#payments" id="payments"></a>

Standard ERC20 tokens allow users to transfer money to the recipient or beneficiary directly by an Arbitrum address&#x20;

{% code title="Send 100.00 USD24 to an address" lineNumbers="true" %}

```javascript
try {
    const tx = await usd24.transfer("0x123....03123", 10000);
    const receipt = await tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

or by NFT id.

{% code title="Send 200.00 USD24 to NFT #10365" lineNumbers="true" %}

```javascript
try {
    const tx = await usd24.transferByAccountId(10365, 20000);
    const receipt = await tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

Notice that Solidity doesn't support decimals, so always use the integer to specify the amount. All Fiat24 cash tokens have 2 decimals, so user the money amount \* 100 to represent the solidity amount. For example, if you want to transfer 122.45 USD24, then the parameter in the call will be 12245.

### **7. Cash Deposit** <a href="#asset-management" id="asset-management"></a>

Cash Deposit booking are managed by Fiat24 internal. Once a user makes a bank deposit, a Cash Deposit booking will be credited from **#9101 Cash In Desk**.

To calculate the deposit account, please refer the [/iban](https://docs.fiat24.com/developer/part-2-api-reference#id-1.-iban) API.

### **8. Card Approval** <a href="#asset-management" id="asset-management"></a>

The debit card payments are booked by the contract Fiat24CardAuthorization by the function `authorize()`. Therefore, the card owner (address) needs to approve a certain limit amount for future card spendings.

{% tabs %}
{% tab title="Arbitrum" %}
{% code title="Approve the card authoriser in Arbitrum" lineNumbers="true" %}

```javascript
// JS code to approve (from owner address) EUR 5000.00 to spend by card
try {
    const tokenContract = eur24; // Values: eur24, chf24, usd24, cnh24
    const tx = await tokenContract.approve('0xe2e3B88B9893e18D0867c08f9cA93f8aB5935b14', 500000);
    const receipt = await tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

In Arbitrum, four currencies (`USD24`, `EUR24`, `CHF24`, `CNH24`) are supported for debit card. The card authorisation address is `0xe2e3B88B9893e18D0867c08f9cA93f8aB5935b14`
{% endtab %}

{% tab title="Mantle" %}
{% code title="Approve the card authoriser in Mantle" lineNumbers="true" %}

```javascript
// JS code to approve (from owner address) SGD 20000.00 to spend by card
try {
    const tokenContract = sgd24; // Values: eur24, chf24, usd24, cnh24, hkd24, jpy24, sgd24
    const tx = await tokenContract.approve('0xb9d38DDE25f67D57af5b91C254F869F90d483d05', 2000000);
    const receipt = await tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

In Mantle, more Asian currencies (`HKD24`, `SGD24`, `JPY24`) are supported as well. The card authorisation address is `0xb9d38DDE25f67D57af5b91C254F869F90d483d05`
{% endtab %}
{% endtabs %}

Once the approved amount is used, the approval needs to be repeated.

### **9. Cash Payout**

Cash Payout is a cash withdrawal transaction to move client's money from Fiat24 account to one of his/her associated bank accounts.

The following code shows how to withdraw 100.55 CHF to user's registered bank account, which has the id of `EA-0000001`, which can be retrieved from [Get Client Profile](https://docs.fiat24.com/developer/part-2-api-reference#payout-bank-account-details)

**NOTE:** there is a minimum amount for the payouts, currently the value is set to **10 EUR /CHF**. But the value can be found in the minimalPayoutAmount method in the smart contract.

{% code title="Payout cash (bank wire), withdraw 100.55 CHF" lineNumbers="true" %}

```javascript
try {
    const tx = await chf24.clientPayout(10055, 'EA-0000001');
    const receipt = await tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

**Special payouts to selected platforms**

For most regulated trading platforms, such as Securities Borker or Crypto Exchanges, normally the cash in operation must be from the account with the same account holder, together with a Reference, which is given by the platform.

The special method `clientPayoutRef()` can send EUR or CHF with such reference to specific platforms.

Here is the sample code:

{% code title="Payout cash (bank wire), send 111.45 EUR to Kraken Exchange" lineNumbers="true" %}

```javascript
const contactId = 'EA-00000739'; // Kraken
const headers = { ... } // Same auth headers as /br
// https://api.fiat24.com/verify?ref= => Contacts/whitelisted contacts with reference
// https://api.fiat24.com/verify?sp= => New Payments (Requires to add bank details)
// https://api.fiat24.com/verify?qr= => QR payments
const response = await fetch(`https://api.fiat24.com/verify?ref=${inputReference}`, headers);
const data = await response.json();
if (!!data && !!data.clientPayoutRefParams) {
    try {
        const purpose = data.clientPayoutRefParams.purposeId;
        const reference = data.clientPayoutRefParams.ref;
        const tx = await chf24.clientPayoutRef(11145, contactId, purpose, reference);
        const receipt = await tx.wait();
    } catch(err) {
        console.error(err);
    }
}
```

{% endcode %}

Fiat24 maintains the list of whitelisted platforms, for the value of `contactId` above.

Here are some samples of these type of accounts, which some might be outdated, for getting the up to date account please fetch them in the RESTful API **GET** **/br**, using the **whitelistedContacts** field.

**EUR Payouts (sample)**

| Contact ID  | Recipient Name                  | Platform         |
| ----------- | ------------------------------- | ---------------- |
| EA-00000739 | Payward Ltd.                    | Kraken           |
| EA-00000740 | UAB Bifinity                    | Binance          |
| EA-00000741 | CB Payments, Ltd                | Coinbase         |
| EA-00000742 | Bitstamp Europe S.A.            | Bitstamp         |
| EA-00000743 | Tiger Brokers Singapore Pte Ltd | Tiger Securities |

**CHF Payouts (sample)**

| Contact ID  | Recipient Name       | Platform         |
| ----------- | -------------------- | ---------------- |
| EA-00000738 | Payward Trading Ltd. | Kraken           |
| EA-00000759 | Casino Zürichsee AG  | Casino Zürichsee |

### **10. Crypto Top-up** <a href="#crypto-conversion" id="crypto-conversion"></a>

This function is the main integration point between wallets and Fiat24, it converts the cryptocurrency from the wallet into our fiat holdings, which are in the form of ERC20 tokens (USD24, CHF24 and EUR24) in the Arbitrum network.

{% hint style="info" %}
The minimal top-up value is 5 USD. If the total token has less than this minimal value, the smart contract call will revert.

The maximal top-up value is 50,000 USD. If the total token has great than this threshold, the smart contract call will revert.
{% endhint %}

We return 100% of the commission to the wallet provider. The commission is transferred as USDC (<https://arbiscan.io/token/0xaf88d065e77c8cC2239327C5EDb3A432268e5831>) to the address of the wallet provider, the same address which is used to call `mintByWallet()`. The commission is paid by each single top-up transaction, so there won't be any settlement or reconciliation effort for the wallet provider.&#x20;

{% tabs %}
{% tab title="Arbitrum" %}
{% code title="Topup contract in Arbitrum" %}

```javascript
0x4582f67698843Dfb6A9F195C0dDee05B0A8C973F
```

{% endcode %}

The commission is currently set to 1.0% of the top-up volume, as published in the [Pricing](https://app.gitbook.com/s/AESj9td4f1kX1mQP0tOZ/pricing-for-individuals) page.
{% endtab %}

{% tab title="Mantle" %}
{% code title="Topup Relay in Mantle" %}

```javascript
0xd08B421A33F9b09A59E2ebf72afEF2365ce5b083
```

{% endcode %}

The commission is currently set to 0% of the top-up volume.
{% endtab %}
{% endtabs %}

**Swap Ether into Fiat24 Cash Token**

{% code title="Deposit 0.003 ETH for EUR24" lineNumbers="true" %}

```javascript
try {
    const tx = await fiat24cryptodeposit.depositETH(EUR24_ADDRESS, {value: "30000000000000000"})
    const receipt = await tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

**Swap ERC20 into Fiat24 Cash Token, through ERC20/USDC or ERC20/ETH Uniswap V3 Pool**

```solidity
function depositTokenViaUsdc(
    address _inputToken
  , address _outputToken
  , uint256 _amount
) external returns(uint256);

function depositTokenViaEth(
    address _inputToken
  , address _outputToken
  , uint256 _amount
) external returns(uint256);
```

{% code title="Deposit 0.09 GMX for EUR24 via ERC20/USDC or ERC20/ETH" lineNumbers="true" %}

```javascript
try {
    const WETH_ADDRESS = "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1";
    const USDC_ADDRESS = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";
    const [ poolFeeWithETH, poolFeeWithUSDC ] = await Promise.all([
        fiat24cryptodeposit.getPoolFeeOfMostLiquidPool(GMX_ADDRESS, WETH_ADDRESS),
        fiat24cryptodeposit.getPoolFeeOfMostLiquidPool(GMX_ADDRESS, USDC_ADDRESS)
    ]);

    const existsETHPool = poolFeeWithETH > 0;
    const existsUSDCPool =  poolFeeWithUSDC > 0;
        
    var tx = await gmx.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, "90000000000000000");
    var receipt = tx.wait();
    if (existsUSDCPool) {
        tx = await fiat24cryptodeposit.depositTokenViaUsdc(GMX_ADDRESS, EUR24_ADDRESS, "90000000000000000");
    } else {
        tx = await fiat24cryptodeposit.depositTokenViaEth(GMX_ADDRESS, EUR24_ADDRESS, "90000000000000000");
    }
    receipt = await tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

**Swap USDC into Fiat24 Cash Token by Wallet Provider**

For certain wallet providers, there is a common requirement to convert assets not present on Arbitrum into Fiat24 cash tokens, which can be achieved in two steps.&#x20;

In the first step, the wallet initiates a cross-chain swap to convert any token (such as Bitcoin or Solana) into USDC on Arbitrum, depositing it into the end-user's address.

{% hint style="info" %}
The swapped token must specifically be **Official** **USDC on the Arbitrum** network.

<https://arbiscan.io/token/0xaf88d065e77c8cc2239327c5edb3a432268e5831>
{% endhint %}

The second step involves the wallet provider initiating a smart contract call to convert the USDC into a Fiat24 token like EUR24. This action is performed by the wallet provider, requiring no user intervention for the top-up. Thus, from the user's perspective, the entire swap process appears seamless and is effectively completed in a single step.

Here is the smart contract function being called by wallet side:

```solidity
function depositByWallet(
    address _client
  , address _outputToken
  , uint256 _usdcAmount
) external returns(uint256);
```

**End-User Side:** The code provided below is on the end user's side to authorise the contract, allowing it to proceed with USDC the next step.

{% code title="Approve 5000 USDC to the topup contract" lineNumbers="true" %}

```javascript
try {
    var tx = await usdc.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, "5000000000");
    var receipt = tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

**Wallet Side:** The following code will be executed by wallet provider's side.&#x20;

{% hint style="warning" %}
The message sender (or caller) must hold the wallet provider's NFT.
{% endhint %}

{% code title="Deposit 100 USDC for EUR24, called by wallet provider" lineNumbers="true" %}

```javascript
try {
    var tx = await fiat24cryptodeposit.depositByWallet(CLIENT_ADDRESS, EUR24_ADDRESS, "100000000");
    receipt = await tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

### **11. Money Exchange**  <a href="#money-exchange" id="money-exchange"></a>

Within their Fiat24 account, users can exchange between the available currencies — CHF, EUR, CNH and USD.

You can perform an exchange in either of the following cases:

• **When you know the amount to sell**\
For example, exchange 100.0 USD to EUR. In this case, specify the input amount as 100 USD.

• **When you know the amount to buy**\
For example, exchange USD to receive 100.0 EUR. In this case, specify the output amount as 100 EUR.

{% tabs %}
{% tab title="Arbitrum" %}
{% code title="FX contract in Arbitrum" %}

```javascript
0x4582f67698843Dfb6A9F195C0dDee05B0A8C973F
```

{% endcode %}

{% endtab %}

{% tab title="Mantle" %}
{% code title="FX contract in Mantle" %}

```javascript
0xd08B421A33F9b09A59E2ebf72afEF2365ce5b083
```

{% endcode %}

{% endtab %}
{% endtabs %}

To complete the transfer, follow these two steps:

{% stepper %}
{% step %}
**Get the exchange rate**

Use the `getExchangeRate()` function to obtain a quote for the exchange rate.

Generating the quote requires providing the contract addresses of both the input and output currency tokens. The function returns the client rate, which includes a spread calculated based on the interbank market rate.

{% code title="Interface" lineNumbers="true" %}

```solidity
function getExchangeRate(
    address _inputToken
  , address _outputToken
) public view returns(uint256);
```

{% endcode %}
{% endstep %}

{% step %}
**Execute the transaction**

**Method 1:** You know the amount of currency to <mark style="color:purple;">sell</mark> with `moneyExchangeExactIn`

{% code title="Sell exact 10.00 USD24 for EUR24" lineNumbers="true" %}

```javascript
try {
    var tx = await usd24.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, 1000);
    var receipt = tx.wait();
    tx = await fiat24cryptodeposit.moneyExchangeExactIn(USD24_ADDRESS, EUR24_ADDRESS, 1000);
    receipt = tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}

**Method 2:** You know the amount of currency to <mark style="color:purple;">buy</mark> with `moneyExchangeExactOut`

{% code title="Buy 10 EUR24 from USD24" lineNumbers="true" %}

```javascript
// Get exchange rate, here USD24/EUR24
const fx_eur24_usd24 = parseInt(await fiat24cryptodeposit.getExchangeRate(EUR24_ADDRESS, USD24_ADDRESS));

// Get reversed spread
const reversespread_usd24_eur24 = parseInt(await fiat24cryptodeposit.getSpread(USD24_ADDRESS, EUR24_ADDRESS, true));

// Get estimated input amount in USD24 for 10.00 EUR
var usd24_input_amount = Math.floor((1000*fx_eur24_usd24)/10000);
usd24_input_amount = Math.floor((usd24_input_amount*reversespread_usd24_eur24)/10000);

try {
    var tx = await usd24.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, usd24_input_amount);
    var receipt = tx.wait();
    tx = await fiat24cryptodeposit.moneyExchangeExactOut(USD24_ADDRESS, EUR24_ADDRESS, 1000);
    receipt = tx.wait();
} catch(err) {
    console.error(err);
}
```

{% endcode %}
{% endstep %}

{% step %}
Done

{% endstep %}
{% endstepper %}

## Appendix: Code Example:

{% code title="A full example" lineNumbers="true" %}

```javascript
const ethers = require('ethers');
const fs = require('fs');
const alchemyKey = "<your Alchemy key>";
// private key of user
const userKey = '<the user wallet private key>';
// users wallet address 
const userAddress = '0xC0c8A5B44fa4b5a3dcECC5324cFE23B1A78FcBB8';
// to user address to receive xxx24 payments
const toUserAddress = '0x61Fff28B7115e41E1C537fa7e51352080B7Ff9B8'
// to user accountId (NFT) to receive xxx24 payments
const toUserTokenId = 901;

// FIAT24 addresses (ArbitrumOne)
const FIAT24ACCOUNT_ADDRESS = '0x133CAEecA096cA54889db71956c7f75862Ead7A0';
const USD24_ADDRESS = '0xbE00f3db78688d9704BCb4e0a827aea3a9Cc0D62';
const EUR24_ADDRESS = '0x2c5d06f591D0d8cd43Ac232c2B654475a142c7DA';
const CHF24_ADDRESS = '0xd41F1f0cf89fD239ca4c1F8E8ADA46345c86b0a4';
const FIAT24_CRYPTO_TOP_UP_ADDRESS = '0x4582f67698843Dfb6A9F195C0dDee05B0A8C973F';

// Other contract addresses (ArbitrumOne)
const USDC_ADDRESS = '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8';
const GMX_ADDRESS = '0xfc5A1A6EB076a2C7aD06eD22C90d7E710E35ad0a';
const WETH_ADDRESS = '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1';


async function fiat24() {
    const provider = new ethers.providers.AlchemyProvider(
        "arbitrum",
        alchemyKey
    );
    const wallet = new ethers.Wallet(userKey, provider);
    
    // F I A T 2 4  A C C O U N T
    const abiFiat24Account = JSON.parse(fs.readFileSync('./contracts/Fiat24Account.json'));
    const fiat24account = new ethers.Contract(FIAT24ACCOUNT_ADDRESS, abiFiat24Account, wallet);
    
    // check availability of Fiat24 account NFT (max. NFT per address = 1)
    const balance = parseInt(await fiat24account.balanceOf(userAddress));
    // if balance = 1, get tokenId of Fiat24 account NFT
    const tokenId =  parseInt(await fiat24account.tokenOfOwnerByIndex(userAddress, 0));
    // get status of Fiat24 account: 0=Na, 1=SoftBlocked, 2=Tourist, 3=Blocked, 4=Closed, 5=Live
    const status = await fiat24account.status(tokenId);
    console.log("balance:", balance);
    console.log("tokenId:", tokenId);
    console.log("status:", status);

    // get client's 30 day limit numbers 
    const limit = await fiat24account.limit(tokenId);
    // total 30 days client limit
    console.log("clientLimit:",parseFloat((limit.clientLimit/100).toFixed(2)));
    // current used client limit within 30 days
    console.log("usedLimit", parseFloat((limit.usedLimit/100).toFixed(2)));
    // start of the 30 days period. After 30 days gets the total 30 days client 
    console.log("startLimitDate", parseInt(limit.startLimitDate));



    // F I A T 2 4  X X X 2 4  T O K E N  R E A D
    const abiXXX24 = JSON.parse(fs.readFileSync('./contracts/xxx24.json'));
    const usd24 = new ethers.Contract(USD24_ADDRESS, abiXXX24, wallet);

    // get USD24 balance of tokenId
    const usd24Balance = parseFloat((parseInt(await usd24.balanceOfByAccountId(tokenId))/100).toFixed(2));
    console.log("USD24 balance:", usd24Balance);
    // check if tokenTransfer from, to, amount is allowed (various checks: balance, limits, sanctions, etc.)
    const tokenTransferAllowed = await usd24.tokenTransferAllowed(userAddress, toUserAddress, 10000);
    console.log("tokenTransferAllowed:", tokenTransferAllowed)
    

    // F I A T 2 4  X X X 2 4  T O K E N  W R I T E
    // P2P transfer USD24 100.00 from user to another Fiat24 user
    try {
        const tx = await usd24.transferByAccountId(toUserTokenId, 10000);
        const receipt = await tx.wait();
    } catch(err) {
        console.error(err);
    }

    // Cash Payout, send to 9102 USD24 100.00 from user account.
    try {
        const tx = await usd24.transferByAccountId(9102, 10000);
        const receipt = await tx.wait();
    } catch(err) {
        console.error(err);
    }



    // F I A T 2 4  C R Y P T O  T O P - U P  R E A D
    const abiFiat24CryptoDeposit = JSON.parse(fs.readFileSync('./contracts/Fiat24CryptoDeposit.json'));
    const fiat24cryptodeposit = new ethers.Contract(FIAT24_CRYPTO_TOP_UP_ADDRESS, abiFiat24CryptoDeposit, wallet);
    const abiGmx = JSON.parse(fs.readFileSync('./contracts/gmx.json'));
    const gmx = new ethers.Contract(GMX_ADDRESS, abiGmx, wallet);
    

    // Get FX rates of Fiat24 Cryto top-up contract
    const usd_eur = parseFloat(((await fiat24cryptodeposit.getExchangeRate(USD24_ADDRESS, EUR24_ADDRESS))/10000).toFixed(4));
    console.log("USD24/EUR24:", usd_eur);
    const eur_usd = parseFloat(((await fiat24cryptodeposit.getExchangeRate(EUR24_ADDRESS, USD24_ADDRESS))/10000).toFixed(4));
    console.log("EUR24/USD24:", eur_usd);
    const eur_chf = parseFloat(((await fiat24cryptodeposit.getExchangeRate(EUR24_ADDRESS, CHF24_ADDRESS))/10000).toFixed(4));
    console.log("EUR24/CHF24:", eur_chf);
    

    // Get FX spread of Fiat24 crypto top-up contract for exact in, i.e. 
    // moneyExchangeExactIn(inputToken, outputToken, amount)
    // or crypto top-up functions depositETH(), depositTokenViaUsdc(), depositTokenViaEth() to convert from USD to EUR,CHF
    const spread = parseFloat(((await fiat24cryptodeposit.getSpread(EUR24_ADDRESS, USD24_ADDRESS, false))/10000).toFixed(4));
    console.log("spread:", spread);


    // Get FX reversed spread of Fiat24 crypto top-up contract for exact out, i.e. 
    // moneyExchangeExactOut(inputToken, outputToken, amount)
    const reversespread = parseFloat(((await fiat24cryptodeposit.getSpread(EUR24_ADDRESS, USD24_ADDRESS, true))/10000).toFixed(4));
    console.log("reverse spread:", reversespread);
    

    // Get Uniswap pool fee to get the pool address with the most liquidity for a crypto/USDC or WETH/USDC pair,
    // here the fee of the most liquid pool for GMX/USDC
    const poolFee = await fiat24cryptodeposit.getPoolFeeOfMostLiquidPool(GMX_ADDRESS, USDC_ADDRESS);
    console.log("Uniswap pool fee:", poolFee);


    // Get Uniswap quote for crypto/USDC, here GMX/USDC. The pool address is identified by inputToken, outputToken and poolFee
    const gmx_usdc_quote = parseFloat(((await fiat24cryptodeposit.callStatic.getQuote(GMX_ADDRESS, USDC_ADDRESS, poolFee, "90000000000000000"))/1000000).toFixed(2));
    console.log("USDC for 0.09 GMX:",gmx_usdc_quote);


    // Get the correct xxx24 output amount of a crypto top-up depositTokenViaUsdc(), here GMX/EUR24
    // 1. Get Uniswap pool fee of most liquid pool
    var poolFee_uniswap = await fiat24cryptodeposit.getPoolFeeOfMostLiquidPool(GMX_ADDRESS, USDC_ADDRESS);
    // 2. Get quote for Uniswap GMX/USDC
    var usdc_amount = Math.floor((await fiat24cryptodeposit.callStatic.getQuote(GMX_ADDRESS, USDC_ADDRESS, poolFee_uniswap, "90000000000000000"))/10000);
    // 3. Get exchange rate USD24/EUR24
    var fx_usd24_eur24 = parseInt(await fiat24cryptodeposit.getExchangeRate(USD24_ADDRESS, EUR24_ADDRESS));
    // 4. Get spread
    var spread_usd24_eur24 = parseInt(await fiat24cryptodeposit.getSpread(USD24_ADDRESS, EUR24_ADDRESS, false));
    // 5. Get fee
    var fee = parseInt(await fiat24cryptodeposit.getFee(tokenId, usdc_amount));
    // 6. Get estimated output amount in EUR24 for 0.09 GMX
    var eur24_amount = Math.floor((usdc_amount-fee)*fx_usd24_eur24/10000);
    eur24_amount = Math.floor(eur24_amount*spread_usd24_eur24/10000);
    eur24_amount = parseFloat((eur24_amount/100).toFixed(2));
    console.log("EUR24 amount for 0.09 GMX:", eur24_amount);
    // OR just do a static call of depositTokenViaUsdc()
    try {
        const tx = await gmx.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, "90000000000000000");
        const receipt = await tx.wait();
    } catch(err) {
        console.error(err);
    }
    eur24_amount = parseFloat((parseInt(await fiat24cryptodeposit.callStatic.depositTokenViaUsdc(GMX_ADDRESS, EUR24_ADDRESS, "90000000000000000"))/100).toFixed(2));
    console.log("EUR24 amount for 0.09 GMX depositTokenViaUsdc() call:", eur24_amount);


    // Get the correct xxx24 output amount of a crypto top-up depositTokenViaEth(), here GMX/EUR24
    // 1. Get Uniswap pool fee of most liquid pool for GMX/WETH
    var poolFee_gmx_weth = await fiat24cryptodeposit.getPoolFeeOfMostLiquidPool(GMX_ADDRESS, WETH_ADDRESS);
    // 2. Get quote for Uniswap GMX/USDC
    var weth_amount =  await fiat24cryptodeposit.callStatic.getQuote(GMX_ADDRESS, WETH_ADDRESS, poolFee_gmx_weth, "90000000000000000");
    // 3. Get Uniswap pool fee of most liquid pool for WETH/USDC
    var poolFee_weth_usdc = await fiat24cryptodeposit.getPoolFeeOfMostLiquidPool(WETH_ADDRESS, USDC_ADDRESS);
    // 4. Get quote for Uniswap GMX/USDC
    usdc_amount = Math.floor((await fiat24cryptodeposit.callStatic.getQuote(WETH_ADDRESS, USDC_ADDRESS, poolFee_weth_usdc, weth_amount))/10000);
    // 3. Get exchange rate USD24/EUR24
    fx_usd24_eur24 = parseInt(await fiat24cryptodeposit.getExchangeRate(USD24_ADDRESS, EUR24_ADDRESS));
    // 4. Get spread
    spread_usd24_eur24 = parseInt(await fiat24cryptodeposit.getSpread(USD24_ADDRESS, EUR24_ADDRESS, false));
    // 5. Get fee
    fee = parseInt(await fiat24cryptodeposit.getFee(tokenId, usdc_amount));
    // 6. Get estimated output amount in EUR24 for 0.09 GMX
    var eur24_amount = Math.floor((usdc_amount-fee)*fx_usd24_eur24/10000);
    eur24_amount = Math.floor(eur24_amount*spread_usd24_eur24/10000);
    eur24_amount = parseFloat((eur24_amount/100).toFixed(2));
    console.log("EUR24 amount for 0.09 GMX:", eur24_amount);
    // OR just do a static call of depositTokenViaUsdc()
    try {
        const tx = await gmx.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, "90000000000000000");
        const receipt = await tx.wait();
    } catch(err) {
        console.error(err);
    }
    eur24_amount = parseFloat((parseInt(await fiat24cryptodeposit.callStatic.depositTokenViaEth(GMX_ADDRESS, EUR24_ADDRESS, "90000000000000000"))/100).toFixed(2));
    console.log("EUR24 amount for 0.09 GMX depositTokenViaUsdc() call:", eur24_amount);


    // Get the correct xxx24 output amount of a crypto top-up depositETH(), here ETH/EUR24
    // 1. Get Uniswap pool fee of most liquid pool for WETH/USDC
    var poolFee_weth_usdc = await fiat24cryptodeposit.getPoolFeeOfMostLiquidPool(GMX_ADDRESS, WETH_ADDRESS);
    // 2. Get quote for Uniswap WETH/USDC
    usdc_amount =  Math.floor(parseInt(await fiat24cryptodeposit.callStatic.getQuote(WETH_ADDRESS, USDC_ADDRESS, poolFee_weth_usdc, "30000000000000000"))/10000);
    // 3. Get exchange rate USD24/EUR24
    var fx_usd24_eur24 = parseInt(await fiat24cryptodeposit.getExchangeRate(USD24_ADDRESS, EUR24_ADDRESS));
    // 4. Get spread
    var spread_usd24_eur24 = parseInt(await fiat24cryptodeposit.getSpread(USD24_ADDRESS, EUR24_ADDRESS, false));
    // 5. Get fee
    var fee = parseInt(await fiat24cryptodeposit.getFee(tokenId, usdc_amount));
    // 6. Get estimated output amount in EUR24 for 0.003 ETH
    var eur24_amount = Math.floor((usdc_amount-fee)*fx_usd24_eur24/10000);
    eur24_amount = Math.floor(eur24_amount*spread_usd24_eur24/10000);
    eur24_amount = parseFloat((eur24_amount/100).toFixed(2));
    //var eur24_amount = parseFloat(((usdc_amount-fee)*fx_usd24_eur24*spread_usd24_eur24/10000/10000/100).toFixed(2));
    console.log("EUR24 amount for 0.09 GMX:", eur24_amount);
    // OR just do a static call of depositETH()
    eur24_amount = parseFloat((parseInt(await fiat24cryptodeposit.callStatic.depositETH(EUR24_ADDRESS, {value: "30000000000000000"}))/100).toFixed(2));
    console.log("EUR24 amount for 0.003 ETH depositETH() call:", eur24_amount);
    

    // Get correct xxx24 output amount for money exchange moneExchangeExactIn(), here CHF24/EUR24
    // 1. Get exchange rate, here USD24/EUR24
    fx_usd24_eur24 = parseInt(await fiat24cryptodeposit.getExchangeRate(USD24_ADDRESS, EUR24_ADDRESS));
    // 2. Get spread
    spread_usd24_eur24 = parseInt(await fiat24cryptodeposit.getSpread(USD24_ADDRESS, EUR24_ADDRESS, false));
    // 3. Get estimated output amount in EUR24 for 10 USD24
    eur24_amount = Math.floor((1000*fx_usd24_eur24)/10000);
    eur24_amount = Math.floor((eur24_amount*spread_usd24_eur24)/10000);
    eur24_amount = parseFloat((eur24_amount/100).toFixed(2));
    console.log("EUR24 amount for 10 USD24:", eur24_amount);
    //OR just do a static call of moneyExchangeExactIn()
    try {
        const tx = await usd24.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, 1000);
        const receipt = await tx.wait();
    } catch(err) {
        console.error(err);
    }
    eur24_amount = parseFloat((parseInt(await fiat24cryptodeposit.callStatic.moneyExchangeExactIn(USD24_ADDRESS, EUR24_ADDRESS, 1000))/100).toFixed(2));
    console.log("EUR24 amount for 10.00 USD24 moneyExchangeExactIn() call:", eur24_amount);


    // Get correct xxx24 input amount for money exchange moneyExchangeExactOut(), here EUR24 (output) / USD24 (input)
    // 1. Get exchange rate, here USD24/EUR24
    var fx_eur24_usd24 = parseInt(await fiat24cryptodeposit.getExchangeRate(EUR24_ADDRESS, USD24_ADDRESS));
    // 2. Get reversed spread
    var reversespread_usd24_eur24 = parseInt(await fiat24cryptodeposit.getSpread(USD24_ADDRESS, EUR24_ADDRESS, true));
    // Get estimated input amount in USD24 for 10.00 EUR
    var usd24_input_amount = Math.floor((1000*fx_eur24_usd24)/10000);
    usd24_input_amount = Math.floor((usd24_input_amount*reversespread_usd24_eur24)/10000);
    usd24_input_amount = parseFloat((usd24_input_amount/100).toFixed(2));
    console.log("USD24 input amount for 10 EUR24:", usd24_input_amount);


    // F I A T 2 4  C R Y P T O  T O P - U P  W R I T E
    // Top-up crypto for xxx24 using depositTokenViaUsdc(), here GMX/EUR24
    try {
        var tx = await gmx.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, "90000000000000000");
        var receipt = tx.wait();
        tx = await fiat24cryptodeposit.depositTokenViaUsdc(GMX_ADDRESS, EUR24_ADDRESS, "90000000000000000");
        receipt = await tx.wait();
    } catch(err) {
        console.error(err);
    }


    // Top-up crypto for xxx24 using depositTokenViaEth(), here GMX/EUR24
    try {
        var tx = await gmx.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, "90000000000000000");
        var receipt = tx.wait();
        const tx = await fiat24cryptodeposit.depositTokenViaEth(GMX_ADDRESS, EUR24_ADDRESS, "90000000000000000");
        const receipt = await tx.wait();
    } catch(err) {
        console.error(err);
    }


    //Top-up ETH for xxx24 using depositETH(), here ETH/EUR24
    try {
        const tx = await fiat24cryptodeposit.depositETH(EUR24_ADDRESS, {value: "30000000000000000"})
        const receipt = await tx.wait();
    } catch(err) {
        console.error(err);
    }
    

    //Exchange money xxx24/xxx24 using moneyExchangeExactIn(), here USD24/EUR24
    try {
        var tx = await usd24.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, 1000);
        var receipt = tx.wait();
        tx = await fiat24cryptodeposit.moneyExchangeExactIn(USD24_ADDRESS, EUR24_ADDRESS, 1000);
        receipt = tx.wait();
    } catch(err) {
        console.error(err);
    }


    //Exchange money xxx24/xxx24 for exact output using moneyExchangeExactIn(), USD24 (input) for 10 EUR24 (output)
    // 1. Get exchange rate, here USD24/EUR24
    fx_eur24_usd24 = parseInt(await fiat24cryptodeposit.getExchangeRate(EUR24_ADDRESS, USD24_ADDRESS));
    // 2. Get reversed spread
    reversespread_usd24_eur24 = parseInt(await fiat24cryptodeposit.getSpread(USD24_ADDRESS, EUR24_ADDRESS, true));
    // Get estimated input amount in USD24 for 10.00 EUR
    usd24_input_amount = Math.floor((1000*fx_eur24_usd24)/10000);
    usd24_input_amount = Math.floor((usd24_input_amount*reversespread_usd24_eur24)/10000);
    try {
        var tx = await usd24.approve(FIAT24_CRYPTO_TOP_UP_ADDRESS, usd24_input_amount);
        var receipt = tx.wait();
        tx = await fiat24cryptodeposit.moneyExchangeExactOut(USD24_ADDRESS, EUR24_ADDRESS, 1000);
        receipt = tx.wait();
    } catch(err) {
        console.error(err);
    }
}

fiat24();
```

{% endcode %}
