#include <string.h>
#include "os_io_seproxyhal.h"
#include "uint256.h"
#include "eth_plugin_interface.h"
#include "shared_context.h"       // TODO : rewrite as independant code
#include "eth_plugin_internal.h"  // TODO : rewrite as independant code
#include "stark_utils.h"
#include "plugin_utils.h"
#include "common_utils.h"
#include "apdu_constants.h"

#ifdef HAVE_STARKWARE

typedef enum {

    STARKWARE_REGISTER = 0,
    STARKWARE_DEPOSIT_TOKEN,
    STARKWARE_DEPOSIT_ETH,
    STARKWARE_DEPOSIT_CANCEL,
    STARKWARE_DEPOSIT_RECLAIM,
    STARKWARE_WITHDRAW,
    STARKWARE_FULL_WITHDRAW,
    STARKWARE_FREEZE,
    STARKWARE_ESCAPE,
    STARKWARE_VERIFY_ESCAPE,
    STARKWARE_WITHDRAW_TO,
    STARKWARE_DEPOSIT_NFT,
    STARKWARE_DEPOSIT_NFT_RECLAIM,
    STARKWARE_WITHDRAW_AND_MINT,
    STARKWARE_WITHDRAW_NFT,
    STARKWARE_WITHDRAW_NFT_TO,
    STARKWARE_REGISTER_AND_DEPOSIT_TOKEN,
    STARKWARE_REGISTER_AND_DEPOSIT_ETH,
    STARKWARE_PROXY_DEPOSIT_TOKEN,
    STARKWARE_PROXY_DEPOSIT_ETH,
} starkwareSelector_t;

#ifndef HAVE_TOKENS_EXTRA_LIST

static const uint8_t DEVERSIFI_CONTRACT[] = {
    0x02,

    0x5d, 0x22, 0x04, 0x5d, 0xac, 0xea, 0xb0, 0x3b, 0x15, 0x80,
    0x31, 0xec, 0xb7, 0xd9, 0xd0, 0x6f, 0xad, 0x24, 0x60, 0x9b,

    0x7d, 0xe1, 0xf0, 0x42, 0x04, 0xef, 0x29, 0x22, 0x9d, 0x84,
    0xe7, 0xc0, 0xc2, 0xd1, 0x21, 0x6c, 0x28, 0x64, 0x5a, 0x15};

#else

static const uint8_t DEVERSIFI_CONTRACT[] = {
    0x02, 0xe7, 0x3a, 0x39, 0x4a, 0xde, 0x4d, 0x94, 0xa0, 0x73, 0x50, 0x2d, 0xa8, 0x70,
    0x3e, 0xa2, 0x34, 0x90, 0xdc, 0x7b, 0x6a, 0x69, 0xC6, 0x39, 0x2E, 0xb0, 0x2a, 0x28,
    0x82, 0x31, 0x41, 0x34, 0xc9, 0x8D, 0xDC, 0xBF, 0x73, 0xB7, 0xAd, 0xBa, 0xb1};

#endif

// register : address (20), stark key (32), drop param 3
// Registration
// Contract Name
// From ETH address
// Master account
// deposit token : stark key (32), verify assetType (32), vaultId (4), quantized Amount (32)
// Deposit
// Contract Name
// Master Account
// Token Account
// Amount
// deposit : stark key (32), verify assetType (32), vaultId (4)
// Flow similar to deposit
// deposit cancel, deposit reclaim : stark key (32), assetType (reclaim) / assetId (cancel) (32)
// ignored, vaultId(4) full withdrawal, freeze : stark key (32), vaultId (4) Cancel Deposit |
// Reclaim Deposit | Full Withdrawal | Freeze Contract Name Master Account Token Account
// withdrawal : stark key (32), verify assetType (32)
// Withdrawal
// Contract Name
// Master Account
// To Eth Address
// Token Symbol
// withdrawal to : stark key (32), verify assetType (32), address (20)
// Withdrawal To
// Contract Name
// Master Account
// To Eth Address
// Token Symbol
// escape : stark key (32), vaultId (4), verify assetType (32), quantized Amount (32)
// Escape
// Contract Name
// Amount
// Master Account
// Token Account
// verify escape : escapeProof (ignore)
// Verify Escape
// Contract Name
// deposit NFT : stark key (32), verify assetType (32), vault id (4), token id (32)
// Deposit
// Contract Name
// Master Account
// Token Account
// NFT Contract
// Token ID
// deposit NFT reclaim : stark key (32), verify assetType (32), vault id (4), token id (32)
// Reclaim Deposit
// Contract Name
// Master Account
// Token Account
// NFT Contract
// Token ID
// withdraw and mint : stark key (32), verify assetType (32), mintable blob (ignored variable)
// Withdrawal
// Contract Name
// Master Account
// Asset Contract
// withdraw NFT : stark key (32), verify assetType (32), token id (32)
// Withdrawal
// Contract Name
// Master Account
// To Eth Address
// NFT Contract
// Token ID
// withdraw NFT To : stark key (32), verify assetType (32), token id (32), address (20)
// Withdrawal To
// Contract Name
// Master Account
// To Eth Address
// NFT Contract
// Token ID
// register and deposit token : stark key (32), variable signature, verify assetType (32), vault Id
// (4), quantized amount (32), token address (20), quantum (32) Register and deposit Contract Name
// From ETH address
// Master account
// Token Account
// Amount
// register and deposit : stark key (32), variable signature, verify assetType (32), vault Id (4)
// flow similar to register and deposit
// deposit token proxy : stark key (32), verify assetType (32), vault Id (4), quantized Amount (32),
// token address (20), quantum (32) flow similar to deposit deposit proxy : stark key (32), verify
// assetType (32), vault Id (4) flow similar to deposit

static const uint8_t STARKWARE_EXPECTED_DATA_SIZE[] = {0,
                                                       4 + 32 + 32 + 32 + 32,
                                                       4 + 32 + 32 + 32,
                                                       4 + 32 + 32 + 32,
                                                       4 + 32 + 32 + 32,
                                                       4 + 32 + 32,
                                                       4 + 32 + 32,
                                                       4 + 32 + 32,
                                                       4 + 32 + 32 + 32 + 32,
                                                       0,
                                                       4 + 32 + 32 + 32,
                                                       4 + 32 + 32 + 32 + 32,
                                                       4 + 32 + 32 + 32 + 32,
                                                       0,
                                                       4 + 32 + 32 + 32,
                                                       4 + 32 + 32 + 32 + 32,
                                                       0,
                                                       0,
                                                       4 + 32 + 32 + 32 + 32 + 32 + 32,
                                                       4 + 32 + 32 + 32};

static const uint8_t STARKWARE_NUM_SCREENS[] = {4 - 1, 5 - 1, 5 - 1, 4 - 1, 4 - 1, 5 - 1, 4 - 1,
                                                4 - 1, 5 - 1, 2 - 1, 5 - 1, 6 - 1, 6 - 1, 4 - 1,
                                                6 - 1, 6 - 1, 6 - 1, 6 - 1, 5 - 1, 5 - 1};

typedef struct starkware_parameters_t {
    uint8_t vaultId[4];
    uint8_t selectorIndex;
    uint8_t starkKey[32];
    uint8_t amount[32];
    uint8_t validToken;

} starkware_parameters_t;

#define STARK_KEY_LENGTH (2 + length * 2 + 1)
#define VAULT_ID_LENGTH  10

bool is_deversify_contract(const uint8_t *address) {
    uint32_t offset = 0;
    uint8_t i;

    for (i = 0; i < DEVERSIFI_CONTRACT[0]; i++) {
        if (memcmp(address, DEVERSIFI_CONTRACT + offset + 1, 20) == 0) {
            return true;
        }
        offset += 20;
    }
    return false;
}

// TODO : rewrite as independant code
bool starkware_verify_asset_id(uint8_t *tmp32, const uint8_t *tokenId, bool assetTypeOnly) {
    if (quantumSet) {
        cx_sha3_t sha3;
        tokenDefinition_t *currentToken = NULL;
        if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
            currentToken =
                &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
        }
        cx_keccak_init(&sha3, 256);
        compute_token_id(&sha3,
                         (currentToken != NULL ? currentToken->address : NULL),
                         dataContext.tokenContext.quantumType,
                         dataContext.tokenContext.quantum,
                         dataContext.tokenContext.mintingBlob,
                         assetTypeOnly,
                         tmp32);
        if (memcmp(tokenId, tmp32, 32) != 0) {
            PRINTF("Token ID not matching - computed %.*H\n", 32, tmp32);
            PRINTF("Current quantum %.*H\n", 32, dataContext.tokenContext.quantum);
            PRINTF("Requested %.*H\n", 32, tokenId);
            return false;
        }
    } else {
        PRINTF("Quantum not set\n");
        return false;
    }
    return true;
}

bool starkware_verify_token(const uint8_t *token) {
    if (quantumSet) {
        if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
            tokenDefinition_t *currentToken =
                &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
            if (memcmp(token + 32 - 20, currentToken->address, 20) != 0) {
                PRINTF("Token not matching got %.*H\n", 20, token + 32 - 20);
                PRINTF("Current token %.*H\n", 20, currentToken->address);
                return false;
            }
        } else {
            PRINTF("Quantum not set\n");
            return false;
        }
    } else {
        PRINTF("Quantum not set\n");
        return false;
    }
    return true;
}

bool starkware_verify_quantum(const uint8_t *quantum) {
    if (quantumSet) {
        if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
            if (memcmp(quantum, dataContext.tokenContext.quantum, 32) != 0) {
                PRINTF("Quantum not matching got %.*H\n", 32, quantum);
                PRINTF("Current quantum %.*H\n", 32, dataContext.tokenContext.quantum);
                return false;
            }
        } else {
            PRINTF("Quantum not set\n");
            return false;
        }
    } else {
        PRINTF("Quantum not set\n");
        return false;
    }
    return true;
}

bool starkware_verify_nft_token_id(const uint8_t *tokenId) {
    if (!quantumSet) {
        PRINTF("Quantum not set\n");
        return false;
    }
    switch (dataContext.tokenContext.quantumType) {
        case STARK_QUANTUM_ERC721:
        case STARK_QUANTUM_MINTABLE_ERC721:
            break;
        default:
            PRINTF("Unexpected quantum type for NFT token id check %d\n",
                   dataContext.tokenContext.quantumType);
            return false;
    }
    if (memcmp(dataContext.tokenContext.quantum, tokenId, 32) != 0) {
        PRINTF("Token ID not matching - expected %.*H\n", 32, dataContext.tokenContext.quantum);
        PRINTF("Current token ID %.*H\n", 32, tokenId);
        return false;
    }
    return true;
}

void starkware_print_vault_id(uint32_t vaultId, char *destination, size_t max_length) {
    if (VAULT_ID_LENGTH > max_length) {
        os_sched_exit(EXCEPTION_OVERFLOW);
    }
    snprintf(destination, max_length, "%d", vaultId);
}

void starkware_print_stark_key(uint8_t *starkKey,
                               size_t length,
                               char *destination,
                               size_t max_length) {
    if (STARK_KEY_LENGTH > max_length) {
        os_sched_exit(EXCEPTION_OVERFLOW);
    }
    snprintf(destination, max_length, "0x%.*H", length, starkKey);
}

// TODO : rewrite as independant code
void starkware_print_amount(uint8_t *amountData,
                            char *destination,
                            size_t destinationLength,
                            bool forEscape) {
    uint256_t amount, amountPre, quantum;
    uint8_t decimals;
    char *ticker = chainConfig->coinName;

    if ((amountData == NULL) ||
        (forEscape && (dataContext.tokenContext.quantumIndex == MAX_ITEMS))) {
        decimals = WEI_TO_ETHER;
        if (!forEscape) {
            convertUint256BE(tmpContent.txContent.value.value,
                             tmpContent.txContent.value.length,
                             &amount);
        } else {
            readu256BE(amountData, &amountPre);
        }
    } else {
        tokenDefinition_t *token =
            &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
        decimals = token->decimals;
        ticker = token->ticker;
        readu256BE(amountData, &amountPre);
    }
    if (amountData != NULL) {
        readu256BE(dataContext.tokenContext.quantum, &quantum);
        mul256(&amountPre, &quantum, &amount);
    }
    tostring256(&amount, 10, (char *) (G_io_apdu_buffer + 100), 100);
    strlcpy(destination, ticker, destinationLength);
    strlcat(destination, " ", destinationLength);
    adjustDecimals((char *) (G_io_apdu_buffer + 100),
                   strlen((char *) (G_io_apdu_buffer + 100)),
                   destination + strlen(ticker) + 1,
                   destinationLength - strlen(ticker) - 1,
                   decimals);
}

// TODO : rewrite as independant code
void starkware_print_ticker(char *destination, size_t destinationLength) {
    char *ticker = chainConfig->coinName;

    if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
        tokenDefinition_t *token =
            &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
        ticker = token->ticker;
    }
    strlcpy(destination, ticker, destinationLength);
}

// TODO : rewrite as independant code
void starkware_print_asset_contract(char *destination, size_t destinationLength) {
    // token has been validated to be present previously
    if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
        tokenDefinition_t *token =
            &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
        if (!getEthDisplayableAddress(token->address,
                                      destination,
                                      destinationLength,
                                      &global_sha3,
                                      chainConfig->chainId)) {
            THROW(APDU_RESPONSE_ERROR_NO_INFO);
        }
    } else {
        strlcpy(destination, "UNKNOWN", destinationLength);
    }
}

// TODO : rewrite as independant code
void starkware_get_source_address(char *destination) {
    uint8_t privateKeyData[INT256_LENGTH];
    cx_ecfp_private_key_t privateKey;
    cx_ecfp_public_key_t publicKey;
    os_perso_derive_node_bip32(CX_CURVE_256K1,
                               tmpCtx.transactionContext.bip32.path,
                               tmpCtx.transactionContext.bip32.length,
                               privateKeyData,
                               NULL);
    cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
    io_seproxyhal_io_heartbeat();
    cx_ecfp_generate_pair(CX_CURVE_256K1, &publicKey, &privateKey, 1);
    explicit_bzero(&privateKey, sizeof(privateKey));
    explicit_bzero(privateKeyData, sizeof(privateKeyData));
    io_seproxyhal_io_heartbeat();
    destination[0] = '0';
    destination[1] = 'x';
    if (!getEthAddressStringFromKey(&publicKey,
                                    destination + 2,
                                    &global_sha3,
                                    chainConfig->chainId)) {
        THROW(CX_INVALID_PARAMETER);
    }
    destination[42] = '\0';
}

void starkware_plugin_call(int message, void *parameters) {
    switch (message) {
        case ETH_PLUGIN_INIT_CONTRACT: {
            uint8_t i;
            ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters;
            starkware_parameters_t *context = (starkware_parameters_t *) msg->pluginContext;
            PRINTF("starkware plugin init\n");
            for (i = 0; i < NUM_STARKWARE_SELECTORS; i++) {
                if (memcmp((const void *) PIC(STARKWARE_SELECTORS[i]),
                           msg->selector,
                           SELECTOR_SIZE) == 0) {
                    context->selectorIndex = i;
                    break;
                }
            }
            if (i == NUM_STARKWARE_SELECTORS) {
                PRINTF("Unknown selector %.*H\n", SELECTOR_SIZE, msg->selector);
                break;
            }
            if (STARKWARE_EXPECTED_DATA_SIZE[context->selectorIndex] != 0) {
                if (msg->dataSize != STARKWARE_EXPECTED_DATA_SIZE[context->selectorIndex]) {
                    PRINTF("Unexpected data size for command %d expected %d got %d\n",
                           context->selectorIndex,
                           STARKWARE_EXPECTED_DATA_SIZE[context->selectorIndex],
                           msg->dataSize);
                    break;
                }
            }
            context->validToken = true;
            msg->result = ETH_PLUGIN_RESULT_OK;
        } break;

        case ETH_PLUGIN_PROVIDE_PARAMETER: {
            ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters;
            starkware_parameters_t *context = (starkware_parameters_t *) msg->pluginContext;
            PRINTF("starkware plugin provide parameter %d %.*H\n",
                   msg->parameterOffset,
                   32,
                   msg->parameter);
            // Ignore for verify escape
            if (context->selectorIndex == STARKWARE_VERIFY_ESCAPE) {
                msg->result = ETH_PLUGIN_RESULT_OK;
                break;
            }
            switch (msg->parameterOffset) {
                case 4:
                    switch (context->selectorIndex) {
                        case STARKWARE_REGISTER:
                            memmove(context->amount, msg->parameter + 32 - 20, 20);
                            break;

                        case STARKWARE_DEPOSIT_TOKEN:
                        case STARKWARE_DEPOSIT_ETH:
                        case STARKWARE_DEPOSIT_CANCEL:
                        case STARKWARE_DEPOSIT_RECLAIM:
                        case STARKWARE_WITHDRAW:
                        case STARKWARE_FULL_WITHDRAW:
                        case STARKWARE_FREEZE:
                        case STARKWARE_ESCAPE:
                        case STARKWARE_WITHDRAW_TO:
                        case STARKWARE_DEPOSIT_NFT:
                        case STARKWARE_DEPOSIT_NFT_RECLAIM:
                        case STARKWARE_WITHDRAW_AND_MINT:
                        case STARKWARE_WITHDRAW_NFT:
                        case STARKWARE_WITHDRAW_NFT_TO:
                        case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                        case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                        case STARKWARE_PROXY_DEPOSIT_TOKEN:
                        case STARKWARE_PROXY_DEPOSIT_ETH:
                            memmove(context->starkKey, msg->parameter, 32);
                            break;

                        default:
                            break;
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                case 4 + 32:
                    switch (context->selectorIndex) {
                        case STARKWARE_REGISTER:
                            memmove(context->starkKey, msg->parameter, 32);
                            break;

                        case STARKWARE_ESCAPE:
                            memmove(context->vaultId, msg->parameter + 32 - 4, 4);
                            break;

                        case STARKWARE_DEPOSIT_CANCEL:
                        case STARKWARE_DEPOSIT_RECLAIM:
                            break;

                        case STARKWARE_FULL_WITHDRAW:
                        case STARKWARE_FREEZE:
                            memmove(context->vaultId, msg->parameter + 32 - 4, 4);
                            break;

                        case STARKWARE_DEPOSIT_ETH:
                        case STARKWARE_DEPOSIT_TOKEN:
                        case STARKWARE_WITHDRAW:
                        case STARKWARE_WITHDRAW_TO:
                        case STARKWARE_DEPOSIT_NFT:
                        case STARKWARE_DEPOSIT_NFT_RECLAIM:
                        case STARKWARE_WITHDRAW_AND_MINT:
                        case STARKWARE_WITHDRAW_NFT:
                        case STARKWARE_WITHDRAW_NFT_TO:
                        case STARKWARE_PROXY_DEPOSIT_TOKEN:
                        case STARKWARE_PROXY_DEPOSIT_ETH:
                            context->validToken =
                                starkware_verify_asset_id(context->amount, msg->parameter, true);
                            break;

                        case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                        case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                            // drop variable byte array signature offset
                            break;

                        default:
                            break;
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                case 4 + 32 + 32:
                    switch (context->selectorIndex) {
                        case STARKWARE_ESCAPE:
                        case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                        case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                            context->validToken =
                                starkware_verify_asset_id(context->amount, msg->parameter, true);
                            break;

                        case STARKWARE_DEPOSIT_CANCEL:
                        case STARKWARE_DEPOSIT_RECLAIM:
                        case STARKWARE_DEPOSIT_ETH:
                        case STARKWARE_DEPOSIT_TOKEN:
                        case STARKWARE_DEPOSIT_NFT:
                        case STARKWARE_DEPOSIT_NFT_RECLAIM:
                        case STARKWARE_PROXY_DEPOSIT_TOKEN:
                        case STARKWARE_PROXY_DEPOSIT_ETH:
                            memmove(context->vaultId, msg->parameter + 32 - 4, 4);
                            break;

                        case STARKWARE_WITHDRAW_TO:
                            memmove(context->amount, msg->parameter + 32 - 20, 20);
                            break;

                        case STARKWARE_WITHDRAW_NFT:
                        case STARKWARE_WITHDRAW_NFT_TO:
                            context->validToken = starkware_verify_nft_token_id(msg->parameter);
                            break;

                        default:
                            break;
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                case 4 + 32 + 32 + 32:
                    switch (context->selectorIndex) {
                        case STARKWARE_ESCAPE:
                        case STARKWARE_DEPOSIT_TOKEN:
                        case STARKWARE_PROXY_DEPOSIT_TOKEN:
                            memmove(context->amount, msg->parameter, 32);
                            break;

                        case STARKWARE_WITHDRAW_NFT_TO:
                            memmove(context->amount, msg->parameter + 32 - 20, 20);
                            break;

                        case STARKWARE_DEPOSIT_NFT:
                        case STARKWARE_DEPOSIT_NFT_RECLAIM:
                            context->validToken = starkware_verify_nft_token_id(msg->parameter);
                            break;

                        case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                        case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                            memmove(context->vaultId, msg->parameter + 32 - 4, 4);
                            break;

                        default:
                            break;
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                case 4 + 32 + 32 + 32 + 32:
                    switch (context->selectorIndex) {
                        switch (context->selectorIndex) {
                            case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                                memmove(context->amount, msg->parameter, 32);
                                break;

                            case STARKWARE_PROXY_DEPOSIT_TOKEN:
                                context->validToken = starkware_verify_token(msg->parameter);
                                break;

                            default:
                                break;
                        }
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                case 4 + 32 + 32 + 32 + 32 + 32:
                    switch (context->selectorIndex) {
                        switch (context->selectorIndex) {
                            case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                                context->validToken = starkware_verify_token(msg->parameter);
                                break;

                            case STARKWARE_PROXY_DEPOSIT_TOKEN:
                                context->validToken = starkware_verify_quantum(msg->parameter);
                                break;

                            default:
                                break;
                        }
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                case 4 + 32 + 32 + 32 + 32 + 32 + 32:
                    switch (context->selectorIndex) {
                        switch (context->selectorIndex) {
                            case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                                context->validToken = starkware_verify_quantum(msg->parameter);
                                break;

                            default:
                                break;
                        }
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                default:
                    switch (context->selectorIndex) {
                        case STARKWARE_REGISTER:
                        case STARKWARE_VERIFY_ESCAPE:
                        case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                        case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                            msg->result = ETH_PLUGIN_RESULT_OK;
                            break;

                        default:
                            PRINTF("Unhandled parameter offset\n");
                            break;
                    }
            }
        } break;

        case ETH_PLUGIN_FINALIZE: {
            ethPluginFinalize_t *msg = (ethPluginFinalize_t *) parameters;
            starkware_parameters_t *context = (starkware_parameters_t *) msg->pluginContext;
            PRINTF("starkware plugin finalize\n");
            if (!context->validToken) {
                msg->result = ETH_PLUGIN_RESULT_FALLBACK;
            } else {
                msg->uiType = ETH_UI_TYPE_GENERIC;
                msg->numScreens = STARKWARE_NUM_SCREENS[context->selectorIndex];
                msg->result = ETH_PLUGIN_RESULT_OK;
            }
        } break;

        case ETH_PLUGIN_QUERY_CONTRACT_ID: {
            ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters;
            starkware_parameters_t *context = (starkware_parameters_t *) msg->pluginContext;
            PRINTF("starkware query contract id\n");
            switch (context->selectorIndex) {
                case STARKWARE_REGISTER:
                    strlcpy(msg->name, "Register", msg->nameLength);
                    break;
                case STARKWARE_DEPOSIT_TOKEN:
                case STARKWARE_DEPOSIT_ETH:
                case STARKWARE_DEPOSIT_NFT:
                case STARKWARE_PROXY_DEPOSIT_TOKEN:
                case STARKWARE_PROXY_DEPOSIT_ETH:
                    strlcpy(msg->name, "Deposit", msg->nameLength);
                    break;
                case STARKWARE_DEPOSIT_CANCEL:
                    strlcpy(msg->name, "Cancel Deposit", msg->nameLength);
                    break;
                case STARKWARE_DEPOSIT_RECLAIM:
                case STARKWARE_DEPOSIT_NFT_RECLAIM:
                    strlcpy(msg->name, "Reclaim Deposit", msg->nameLength);
                    break;
                case STARKWARE_WITHDRAW:
                case STARKWARE_WITHDRAW_NFT:
                case STARKWARE_WITHDRAW_AND_MINT:
                    strlcpy(msg->name, "Withdrawal", msg->nameLength);
                    break;
                case STARKWARE_FULL_WITHDRAW:
                    strlcpy(msg->name, "Full Withdrawal", msg->nameLength);
                    break;
                case STARKWARE_FREEZE:
                    strlcpy(msg->name, "Freeze", msg->nameLength);
                    break;
                case STARKWARE_ESCAPE:
                    strlcpy(msg->name, "Escape", msg->nameLength);
                    break;
                case STARKWARE_VERIFY_ESCAPE:
                    strlcpy(msg->name, "Verify Escape", msg->nameLength);
                    break;
                case STARKWARE_WITHDRAW_TO:
                case STARKWARE_WITHDRAW_NFT_TO:
                    strlcpy(msg->name, "Withdrawal To", msg->nameLength);
                    break;
                case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                    strlcpy(msg->name, "Register&Deposit", msg->nameLength);
                    break;

                default:
                    break;
            }
            strlcpy(
                msg->version,
                is_deversify_contract(tmpContent.txContent.destination) ? "DeversiFi" : "Starkware",
                msg->versionLength);
            msg->result = ETH_PLUGIN_RESULT_OK;
        } break;

        case ETH_PLUGIN_QUERY_CONTRACT_UI: {
            ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters;
            starkware_parameters_t *context = (starkware_parameters_t *) msg->pluginContext;
            switch (msg->screenIndex) {
                case 0:
                    strlcpy(msg->title, "Contract Name", msg->titleLength);
                    if (is_deversify_contract(tmpContent.txContent.destination)) {
                        strlcpy(msg->msg, "DeversiFi", msg->msgLength);
                    } else {
                        if (!getEthDisplayableAddress(tmpContent.txContent.destination,
                                                      msg->msg,
                                                      msg->msgLength,
                                                      &global_sha3,
                                                      chainConfig->chainId)) {
                            THROW(APDU_RESPONSE_ERROR_NO_INFO);
                        }
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;
                case 1:
                    switch (context->selectorIndex) {
                        case STARKWARE_REGISTER:
                        case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                        case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                            strlcpy(msg->title, "From ETH Address", msg->titleLength);
                            if (!getEthDisplayableAddress(context->amount,
                                                          msg->msg,
                                                          msg->msgLength,
                                                          &global_sha3,
                                                          chainConfig->chainId)) {
                                THROW(APDU_RESPONSE_ERROR_NO_INFO);
                            }
                            break;
                        case STARKWARE_ESCAPE:
                            strlcpy(msg->title, "Amount", msg->titleLength);
                            starkware_print_amount(context->amount, msg->msg, msg->msgLength, true);
                            break;
                        case STARKWARE_DEPOSIT_TOKEN:
                        case STARKWARE_DEPOSIT_ETH:
                        case STARKWARE_PROXY_DEPOSIT_TOKEN:
                        case STARKWARE_PROXY_DEPOSIT_ETH:
                        case STARKWARE_DEPOSIT_CANCEL:
                        case STARKWARE_DEPOSIT_RECLAIM:
                        case STARKWARE_WITHDRAW:
                        case STARKWARE_FULL_WITHDRAW:
                        case STARKWARE_FREEZE:
                        case STARKWARE_VERIFY_ESCAPE:
                        case STARKWARE_WITHDRAW_TO:
                        case STARKWARE_DEPOSIT_NFT:
                        case STARKWARE_DEPOSIT_NFT_RECLAIM:
                        case STARKWARE_WITHDRAW_AND_MINT:
                        case STARKWARE_WITHDRAW_NFT:
                        case STARKWARE_WITHDRAW_NFT_TO:
                            strlcpy(msg->title, "Master Account", msg->titleLength);
                            starkware_print_stark_key(context->starkKey,
                                                      sizeof(context->starkKey),
                                                      msg->msg,
                                                      msg->msgLength);
                            break;
                        default:
                            PRINTF("Unexpected screen %d for %d\n",
                                   msg->screenIndex,
                                   context->selectorIndex);
                            break;
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;
                case 2:
                    switch (context->selectorIndex) {
                        case STARKWARE_REGISTER:
                        case STARKWARE_ESCAPE:
                        case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                        case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                            strlcpy(msg->title, "Master Account", msg->titleLength);
                            starkware_print_stark_key(context->starkKey,
                                                      sizeof(context->starkKey),
                                                      msg->msg,
                                                      msg->msgLength);
                            break;

                        case STARKWARE_DEPOSIT_TOKEN:
                        case STARKWARE_DEPOSIT_ETH:
                        case STARKWARE_PROXY_DEPOSIT_TOKEN:
                        case STARKWARE_PROXY_DEPOSIT_ETH:
                        case STARKWARE_DEPOSIT_CANCEL:
                        case STARKWARE_DEPOSIT_RECLAIM:
                        case STARKWARE_FULL_WITHDRAW:
                        case STARKWARE_FREEZE:
                        case STARKWARE_DEPOSIT_NFT:
                        case STARKWARE_DEPOSIT_NFT_RECLAIM:
                            strlcpy(msg->title, "Token Account", msg->titleLength);
                            starkware_print_vault_id(U4BE(context->vaultId, 0),
                                                     msg->msg,
                                                     msg->msgLength);
                            break;
                        case STARKWARE_WITHDRAW:
                        case STARKWARE_WITHDRAW_NFT:
                            strlcpy(msg->title, "To ETH Address", msg->titleLength);
                            starkware_get_source_address(msg->msg);
                            break;
                        case STARKWARE_WITHDRAW_TO:
                        case STARKWARE_WITHDRAW_NFT_TO:
                            strlcpy(msg->title, "To ETH Address", msg->titleLength);
                            if (!getEthDisplayableAddress(context->amount,
                                                          msg->msg,
                                                          msg->msgLength,
                                                          &global_sha3,
                                                          chainConfig->chainId)) {
                                THROW(APDU_RESPONSE_ERROR_NO_INFO);
                            }
                            break;
                        case STARKWARE_WITHDRAW_AND_MINT:
                            strlcpy(msg->title, "Asset Contract", msg->titleLength);
                            starkware_print_asset_contract(msg->msg, msg->msgLength);
                            break;

                        default:
                            PRINTF("Unexpected screen %d for %d\n",
                                   msg->screenIndex,
                                   context->selectorIndex);
                            break;
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                case 3:
                    switch (context->selectorIndex) {
                        case STARKWARE_ESCAPE:
                            strlcpy(msg->title, "Token Account", msg->titleLength);
                            starkware_print_vault_id(U4BE(context->vaultId, 0),
                                                     msg->msg,
                                                     msg->msgLength);
                            break;
                        case STARKWARE_DEPOSIT_TOKEN:
                        case STARKWARE_DEPOSIT_ETH:
                        case STARKWARE_PROXY_DEPOSIT_TOKEN:
                        case STARKWARE_PROXY_DEPOSIT_ETH:
                            strlcpy(msg->title, "Amount", msg->titleLength);
                            starkware_print_amount(
                                (((context->selectorIndex == STARKWARE_DEPOSIT_ETH) ||
                                  (context->selectorIndex == STARKWARE_PROXY_DEPOSIT_ETH))
                                     ? NULL
                                     : context->amount),
                                msg->msg,
                                msg->msgLength,
                                false);
                            break;
                        case STARKWARE_WITHDRAW:
                        case STARKWARE_WITHDRAW_TO:
                            strlcpy(msg->title, "Token Symbol", msg->titleLength);
                            starkware_print_ticker(msg->msg, msg->msgLength);
                            break;

                        case STARKWARE_WITHDRAW_NFT:
                        case STARKWARE_WITHDRAW_NFT_TO:
                        case STARKWARE_DEPOSIT_NFT:
                        case STARKWARE_DEPOSIT_NFT_RECLAIM:
                            strlcpy(msg->title, "NFT Contract", msg->titleLength);
                            starkware_print_asset_contract(msg->msg, msg->msgLength);
                            break;

                        case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                        case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                            strlcpy(msg->title, "Token Account", msg->titleLength);
                            starkware_print_vault_id(U4BE(context->vaultId, 0),
                                                     msg->msg,
                                                     msg->msgLength);
                            break;

                        default:
                            PRINTF("Unexpected screen %d for %d\n",
                                   msg->screenIndex,
                                   context->selectorIndex);
                            break;
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                case 4:
                    switch (context->selectorIndex) {
                        case STARKWARE_WITHDRAW_NFT:
                        case STARKWARE_WITHDRAW_NFT_TO:
                        case STARKWARE_DEPOSIT_NFT:
                        case STARKWARE_DEPOSIT_NFT_RECLAIM:
                            strlcpy(msg->title, "TokenID", msg->titleLength);
                            starkware_print_stark_key(dataContext.tokenContext.quantum,
                                                      sizeof(dataContext.tokenContext.quantum),
                                                      msg->msg,
                                                      msg->msgLength);
                            break;

                        case STARKWARE_REGISTER_AND_DEPOSIT_TOKEN:
                        case STARKWARE_REGISTER_AND_DEPOSIT_ETH:
                            strlcpy(msg->title, "Amount", msg->titleLength);
                            starkware_print_amount(
                                ((context->selectorIndex == STARKWARE_REGISTER_AND_DEPOSIT_ETH)
                                     ? NULL
                                     : context->amount),
                                msg->msg,
                                msg->msgLength,
                                false);
                            break;

                        default:
                            PRINTF("Unexpected screen %d for %d\n",
                                   msg->screenIndex,
                                   context->selectorIndex);
                            break;
                    }
                    msg->result = ETH_PLUGIN_RESULT_OK;
                    break;

                default:
                    PRINTF("Unexpected screen %d for %d\n",
                           msg->screenIndex,
                           context->selectorIndex);
                    break;
            }
        } break;

        default:
            PRINTF("Unhandled message %d\n", message);
    }
}

#endif
