Externalize ERC 20 support
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
Ethereum application : Common Technical Specifications
|
Ethereum application : Common Technical Specifications
|
||||||
=======================================================
|
=======================================================
|
||||||
Ledger Firmware Team <hello@ledger.fr>
|
Ledger Firmware Team <hello@ledger.fr>
|
||||||
Application version 1.2 - 19th of August 2017
|
Application version 1.1.10 - 4th of February 2019
|
||||||
|
|
||||||
## 1.0
|
## 1.0
|
||||||
- Initial release
|
- Initial release
|
||||||
@@ -13,6 +13,9 @@ Application version 1.2 - 19th of August 2017
|
|||||||
## 1.2
|
## 1.2
|
||||||
- Add SIGN ETH PERSONAL MESSAGE
|
- Add SIGN ETH PERSONAL MESSAGE
|
||||||
|
|
||||||
|
## 1.1.10
|
||||||
|
- Add PROVIDE ERC 20 TOKEN INFORMATION
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
This application describes the APDU messages interface to communicate with the Ethereum application.
|
This application describes the APDU messages interface to communicate with the Ethereum application.
|
||||||
@@ -188,6 +191,47 @@ The input data is the message to sign, streamed to the device in 255 bytes maxim
|
|||||||
|==============================================================================================================================
|
|==============================================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
### PROVIDE ERC 20 TOKEN INFORMATION
|
||||||
|
|
||||||
|
#### Description
|
||||||
|
|
||||||
|
This commands provides a trusted description of an ERC 20 token to associate a contract address with a ticker and number of decimals.
|
||||||
|
|
||||||
|
It shall be run immediately before performing a transaction involving a contract calling this contract address to display the proper token information to the user if necessary, as marked in GET APP CONFIGURATION flags.
|
||||||
|
|
||||||
|
The signature is computed on
|
||||||
|
|
||||||
|
ticker || address || number of decimals (uint4be) || chainId (uint4be)
|
||||||
|
|
||||||
|
signed by the following secp256k1 public key 0482bbf2f34f367b2e5bc21847b6566f21f0976b22d3388a9a5e446ac62d25cf725b62a2555b2dd464a4da0ab2f4d506820543af1d242470b1b1a969a27578f353
|
||||||
|
|
||||||
|
#### Coding
|
||||||
|
|
||||||
|
'Command'
|
||||||
|
|
||||||
|
[width="80%"]
|
||||||
|
|==============================================================================================================================
|
||||||
|
| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le*
|
||||||
|
| E0 | 0A | 00 | 00 | variable | 00
|
||||||
|
|==============================================================================================================================
|
||||||
|
|
||||||
|
'Input data'
|
||||||
|
|
||||||
|
[width="80%"]
|
||||||
|
|==============================================================================================================================
|
||||||
|
| *Description* | *Length*
|
||||||
|
| Length of ERC 20 ticker | 1
|
||||||
|
| ERC 20 ticker | variable
|
||||||
|
| ERC 20 contract address | 20
|
||||||
|
| Number of decimals (big endian encoded) | 4
|
||||||
|
| Chain ID (big endian encoded) | 4
|
||||||
|
| Token information signature | variable
|
||||||
|
|==============================================================================================================================
|
||||||
|
|
||||||
|
'Output data'
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
### GET APP CONFIGURATION
|
### GET APP CONFIGURATION
|
||||||
|
|
||||||
#### Description
|
#### Description
|
||||||
@@ -215,6 +259,8 @@ None
|
|||||||
| *Description* | *Length*
|
| *Description* | *Length*
|
||||||
| Flags
|
| Flags
|
||||||
0x01 : arbitrary data signature enabled by user
|
0x01 : arbitrary data signature enabled by user
|
||||||
|
|
||||||
|
0x02 : ERC 20 Token information needs to be provided externally
|
||||||
| 01
|
| 01
|
||||||
| Application major version | 01
|
| Application major version | 01
|
||||||
| Application minor version | 01
|
| Application minor version | 01
|
||||||
|
|||||||
110
src/main.c
110
src/main.c
@@ -51,11 +51,15 @@ void finalizeParsing(bool);
|
|||||||
|
|
||||||
#define MAX_BIP32_PATH 10
|
#define MAX_BIP32_PATH 10
|
||||||
|
|
||||||
|
#define APP_FLAG_DATA_ALLOWED 0x01
|
||||||
|
#define APP_FLAG_EXTERNAL_TOKEN_NEEDED 0x02
|
||||||
|
|
||||||
#define CLA 0xE0
|
#define CLA 0xE0
|
||||||
#define INS_GET_PUBLIC_KEY 0x02
|
#define INS_GET_PUBLIC_KEY 0x02
|
||||||
#define INS_SIGN 0x04
|
#define INS_SIGN 0x04
|
||||||
#define INS_GET_APP_CONFIGURATION 0x06
|
#define INS_GET_APP_CONFIGURATION 0x06
|
||||||
#define INS_SIGN_PERSONAL_MESSAGE 0x08
|
#define INS_SIGN_PERSONAL_MESSAGE 0x08
|
||||||
|
#define INS_PROVIDE_ERC20_TOKEN_INFORMATION 0x0A
|
||||||
#define P1_CONFIRM 0x01
|
#define P1_CONFIRM 0x01
|
||||||
#define P1_NON_CONFIRM 0x00
|
#define P1_NON_CONFIRM 0x00
|
||||||
#define P2_NO_CHAINCODE 0x00
|
#define P2_NO_CHAINCODE 0x00
|
||||||
@@ -73,6 +77,22 @@ void finalizeParsing(bool);
|
|||||||
#define WEI_TO_ETHER 18
|
#define WEI_TO_ETHER 18
|
||||||
|
|
||||||
static const uint8_t const TOKEN_TRANSFER_ID[] = { 0xa9, 0x05, 0x9c, 0xbb };
|
static const uint8_t const TOKEN_TRANSFER_ID[] = { 0xa9, 0x05, 0x9c, 0xbb };
|
||||||
|
|
||||||
|
static const uint8_t const TOKEN_SIGNATURE_PUBLIC_KEY[] = {
|
||||||
|
// production key 2019-01-11 03:07PM (erc20signer)
|
||||||
|
0x04,
|
||||||
|
|
||||||
|
0x5e,0x6c,0x10,0x20,0xc1,0x4d,0xc4,0x64,
|
||||||
|
0x42,0xfe,0x89,0xf9,0x7c,0x0b,0x68,0xcd,
|
||||||
|
0xb1,0x59,0x76,0xdc,0x24,0xf2,0x4c,0x31,
|
||||||
|
0x6e,0x7b,0x30,0xfe,0x4e,0x8c,0xc7,0x6b,
|
||||||
|
|
||||||
|
0x14,0x89,0x15,0x0c,0x21,0x51,0x4e,0xbf,
|
||||||
|
0x44,0x0f,0xf5,0xde,0xa5,0x39,0x3d,0x83,
|
||||||
|
0xde,0x53,0x58,0xcd,0x09,0x8f,0xce,0x8f,
|
||||||
|
0xd0,0xf8,0x1d,0xaa,0x94,0x97,0x91,0x83
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct tokenContext_t {
|
typedef struct tokenContext_t {
|
||||||
uint8_t data[4 + 32 + 32];
|
uint8_t data[4 + 32 + 32];
|
||||||
uint32_t dataFieldPos;
|
uint32_t dataFieldPos;
|
||||||
@@ -95,6 +115,7 @@ typedef struct transactionContext_t {
|
|||||||
uint8_t pathLength;
|
uint8_t pathLength;
|
||||||
uint32_t bip32Path[MAX_BIP32_PATH];
|
uint32_t bip32Path[MAX_BIP32_PATH];
|
||||||
uint8_t hash[32];
|
uint8_t hash[32];
|
||||||
|
tokenDefinition_t currentToken;
|
||||||
} transactionContext_t;
|
} transactionContext_t;
|
||||||
|
|
||||||
typedef struct messageSigningContext_t {
|
typedef struct messageSigningContext_t {
|
||||||
@@ -128,6 +149,7 @@ volatile uint8_t contractDetails;
|
|||||||
volatile char addressSummary[32];
|
volatile char addressSummary[32];
|
||||||
volatile bool dataPresent;
|
volatile bool dataPresent;
|
||||||
volatile bool tokenProvisioned;
|
volatile bool tokenProvisioned;
|
||||||
|
volatile bool currentTokenSet;
|
||||||
|
|
||||||
bagl_element_t tmp_element;
|
bagl_element_t tmp_element;
|
||||||
|
|
||||||
@@ -1394,6 +1416,7 @@ uint32_t splitBinaryParameterPart(char *result, uint8_t *parameter) {
|
|||||||
|
|
||||||
tokenDefinition_t* getKnownToken() {
|
tokenDefinition_t* getKnownToken() {
|
||||||
tokenDefinition_t *currentToken = NULL;
|
tokenDefinition_t *currentToken = NULL;
|
||||||
|
#ifdef HAVE_TOKENS_LIST
|
||||||
uint32_t numTokens = 0;
|
uint32_t numTokens = 0;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
switch(chainConfig->kind) {
|
switch(chainConfig->kind) {
|
||||||
@@ -1531,10 +1554,15 @@ tokenDefinition_t* getKnownToken() {
|
|||||||
return currentToken;
|
return currentToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (currentTokenSet && (os_memcmp(tmpCtx.transactionContext.currentToken.address, tmpContent.txContent.destination, 20) == 0)) {
|
||||||
|
return &tmpCtx.transactionContext.currentToken;
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
customStatus_e customProcessor(txContext_t *context) {
|
customStatus_e customProcessor(txContext_t *context) {
|
||||||
if ((context->currentField == TX_RLP_DATA) &&
|
if ((context->currentField == TX_RLP_DATA) &&
|
||||||
(context->currentFieldLength != 0)) {
|
(context->currentFieldLength != 0)) {
|
||||||
@@ -1666,7 +1694,7 @@ void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t da
|
|||||||
uint32_t bip32Path[MAX_BIP32_PATH];
|
uint32_t bip32Path[MAX_BIP32_PATH];
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint8_t bip32PathLength = *(dataBuffer++);
|
uint8_t bip32PathLength = *(dataBuffer++);
|
||||||
cx_ecfp_private_key_t privateKey;
|
cx_ecfp_private_key_t privateKey;
|
||||||
|
|
||||||
if ((bip32PathLength < 0x01) ||
|
if ((bip32PathLength < 0x01) ||
|
||||||
(bip32PathLength > MAX_BIP32_PATH)) {
|
(bip32PathLength > MAX_BIP32_PATH)) {
|
||||||
@@ -1680,9 +1708,7 @@ void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t da
|
|||||||
THROW(0x6B00);
|
THROW(0x6B00);
|
||||||
}
|
}
|
||||||
for (i = 0; i < bip32PathLength; i++) {
|
for (i = 0; i < bip32PathLength; i++) {
|
||||||
bip32Path[i] = (dataBuffer[0] << 24) |
|
bip32Path[i] = U4BE(dataBuffer, 0);
|
||||||
(dataBuffer[1] << 16) |
|
|
||||||
(dataBuffer[2] << 8) | (dataBuffer[3]);
|
|
||||||
dataBuffer += 4;
|
dataBuffer += 4;
|
||||||
}
|
}
|
||||||
tmpCtx.publicKeyContext.getChaincode = (p2 == P2_CHAINCODE);
|
tmpCtx.publicKeyContext.getChaincode = (p2 == P2_CHAINCODE);
|
||||||
@@ -1847,6 +1873,53 @@ void finalizeParsing(bool direct) {
|
|||||||
#endif // #if TARGET_ID
|
#endif // #if TARGET_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) {
|
||||||
|
UNUSED(p1);
|
||||||
|
UNUSED(p2);
|
||||||
|
UNUSED(flags);
|
||||||
|
uint32_t offset = 0;
|
||||||
|
uint8_t tickerLength;
|
||||||
|
uint32_t chainId;
|
||||||
|
uint8_t hash[32];
|
||||||
|
cx_ecfp_public_key_t tokenKey;
|
||||||
|
if (dataLength < 1) {
|
||||||
|
THROW(0x6A80);
|
||||||
|
}
|
||||||
|
tickerLength = workBuffer[offset++];
|
||||||
|
dataLength--;
|
||||||
|
if (tickerLength >= sizeof(tmpCtx.transactionContext.currentToken.ticker)) {
|
||||||
|
THROW(0x6A80);
|
||||||
|
}
|
||||||
|
if (dataLength < tickerLength + 20 + 4 + 4) {
|
||||||
|
THROW(0x6A80);
|
||||||
|
}
|
||||||
|
cx_hash_sha256(workBuffer + offset, tickerLength + 20 + 4 + 4, hash);
|
||||||
|
os_memmove(tmpCtx.transactionContext.currentToken.ticker, workBuffer + offset, tickerLength);
|
||||||
|
tmpCtx.transactionContext.currentToken.ticker[tickerLength] = '\0';
|
||||||
|
offset += tickerLength;
|
||||||
|
dataLength -= tickerLength;
|
||||||
|
os_memmove(tmpCtx.transactionContext.currentToken.address, workBuffer + offset, 20);
|
||||||
|
offset += 20;
|
||||||
|
dataLength -= 20;
|
||||||
|
tmpCtx.transactionContext.currentToken.decimals = U4BE(workBuffer, offset);
|
||||||
|
offset += 4;
|
||||||
|
dataLength -= 4;
|
||||||
|
chainId = U4BE(workBuffer, offset);
|
||||||
|
if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) {
|
||||||
|
PRINTF("ChainId token mismatch\n");
|
||||||
|
THROW(0x6A80);
|
||||||
|
}
|
||||||
|
offset += 4;
|
||||||
|
dataLength -= 4;
|
||||||
|
cx_ecfp_init_public_key(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey);
|
||||||
|
if (!cx_ecdsa_verify(&tokenKey, CX_LAST, CX_SHA256, hash, 32, workBuffer + offset, dataLength)) {
|
||||||
|
PRINTF("Invalid token signature\n");
|
||||||
|
THROW(0x6A80);
|
||||||
|
}
|
||||||
|
currentTokenSet = true;
|
||||||
|
THROW(0x9000);
|
||||||
|
}
|
||||||
|
|
||||||
void handleSign(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) {
|
void handleSign(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) {
|
||||||
UNUSED(tx);
|
UNUSED(tx);
|
||||||
parserStatus_e txResult;
|
parserStatus_e txResult;
|
||||||
@@ -1861,14 +1934,13 @@ void handleSign(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength
|
|||||||
workBuffer++;
|
workBuffer++;
|
||||||
dataLength--;
|
dataLength--;
|
||||||
for (i = 0; i < tmpCtx.transactionContext.pathLength; i++) {
|
for (i = 0; i < tmpCtx.transactionContext.pathLength; i++) {
|
||||||
tmpCtx.transactionContext.bip32Path[i] =
|
tmpCtx.transactionContext.bip32Path[i] = U4BE(workBuffer, 0);
|
||||||
(workBuffer[0] << 24) | (workBuffer[1] << 16) |
|
|
||||||
(workBuffer[2] << 8) | (workBuffer[3]);
|
|
||||||
workBuffer += 4;
|
workBuffer += 4;
|
||||||
dataLength -= 4;
|
dataLength -= 4;
|
||||||
}
|
}
|
||||||
dataPresent = false;
|
dataPresent = false;
|
||||||
tokenProvisioned = false;
|
tokenProvisioned = false;
|
||||||
|
currentTokenSet = false;
|
||||||
initTx(&txContext, &sha3, &tmpContent.txContent, customProcessor, NULL);
|
initTx(&txContext, &sha3, &tmpContent.txContent, customProcessor, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1910,7 +1982,10 @@ void handleGetAppConfiguration(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint
|
|||||||
UNUSED(workBuffer);
|
UNUSED(workBuffer);
|
||||||
UNUSED(dataLength);
|
UNUSED(dataLength);
|
||||||
UNUSED(flags);
|
UNUSED(flags);
|
||||||
G_io_apdu_buffer[0] = (N_storage.dataAllowed ? 0x01 : 0x00);
|
G_io_apdu_buffer[0] = (N_storage.dataAllowed ? APP_FLAG_DATA_ALLOWED : 0x00);
|
||||||
|
#ifndef HAVE_TOKENS_LIST
|
||||||
|
G_io_apdu_buffer[0] |= APP_FLAG_EXTERNAL_TOKEN_NEEDED;
|
||||||
|
#endif
|
||||||
G_io_apdu_buffer[1] = LEDGER_MAJOR_VERSION;
|
G_io_apdu_buffer[1] = LEDGER_MAJOR_VERSION;
|
||||||
G_io_apdu_buffer[2] = LEDGER_MINOR_VERSION;
|
G_io_apdu_buffer[2] = LEDGER_MINOR_VERSION;
|
||||||
G_io_apdu_buffer[3] = LEDGER_PATCH_VERSION;
|
G_io_apdu_buffer[3] = LEDGER_PATCH_VERSION;
|
||||||
@@ -1936,15 +2011,11 @@ void handleSignPersonalMessage(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint
|
|||||||
workBuffer++;
|
workBuffer++;
|
||||||
dataLength--;
|
dataLength--;
|
||||||
for (i = 0; i < tmpCtx.messageSigningContext.pathLength; i++) {
|
for (i = 0; i < tmpCtx.messageSigningContext.pathLength; i++) {
|
||||||
tmpCtx.messageSigningContext.bip32Path[i] =
|
tmpCtx.messageSigningContext.bip32Path[i] = U4BE(workBuffer, 0);
|
||||||
(workBuffer[0] << 24) | (workBuffer[1] << 16) |
|
|
||||||
(workBuffer[2] << 8) | (workBuffer[3]);
|
|
||||||
workBuffer += 4;
|
workBuffer += 4;
|
||||||
dataLength -= 4;
|
dataLength -= 4;
|
||||||
}
|
}
|
||||||
tmpCtx.messageSigningContext.remainingLength =
|
tmpCtx.messageSigningContext.remainingLength = U4BE(workBuffer, 0);
|
||||||
(workBuffer[0] << 24) | (workBuffer[1] << 16) |
|
|
||||||
(workBuffer[2] << 8) | (workBuffer[3]);
|
|
||||||
workBuffer += 4;
|
workBuffer += 4;
|
||||||
dataLength -= 4;
|
dataLength -= 4;
|
||||||
// Initialize message header + length
|
// Initialize message header + length
|
||||||
@@ -2010,10 +2081,16 @@ void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx) {
|
|||||||
|
|
||||||
switch (G_io_apdu_buffer[OFFSET_INS]) {
|
switch (G_io_apdu_buffer[OFFSET_INS]) {
|
||||||
case INS_GET_PUBLIC_KEY:
|
case INS_GET_PUBLIC_KEY:
|
||||||
|
currentTokenSet = false;
|
||||||
handleGetPublicKey(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx);
|
handleGetPublicKey(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INS_SIGN:
|
case INS_PROVIDE_ERC20_TOKEN_INFORMATION:
|
||||||
|
currentTokenSet = false;
|
||||||
|
handleProvideErc20TokenInformation(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INS_SIGN:
|
||||||
handleSign(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx);
|
handleSign(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2022,6 +2099,7 @@ void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case INS_SIGN_PERSONAL_MESSAGE:
|
case INS_SIGN_PERSONAL_MESSAGE:
|
||||||
|
currentTokenSet = false;
|
||||||
handleSignPersonalMessage(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx);
|
handleSignPersonalMessage(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer[OFFSET_LC], flags, tx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
|
#ifdef HAVE_TOKENS_LIST
|
||||||
|
|
||||||
#include "tokens.h"
|
#include "tokens.h"
|
||||||
|
|
||||||
const tokenDefinition_t const TOKENS_AKROMA[NUM_TOKENS_AKROMA] = {};
|
const tokenDefinition_t const TOKENS_AKROMA[NUM_TOKENS_AKROMA] = {};
|
||||||
@@ -1175,3 +1177,5 @@ const tokenDefinition_t const TOKENS_MIX[NUM_TOKENS_MIX] = {};
|
|||||||
const tokenDefinition_t const TOKENS_REOSC[NUM_TOKENS_REOSC] = {};
|
const tokenDefinition_t const TOKENS_REOSC[NUM_TOKENS_REOSC] = {};
|
||||||
|
|
||||||
const tokenDefinition_t const TOKENS_HPB[NUM_TOKENS_HPB] = {};
|
const tokenDefinition_t const TOKENS_HPB[NUM_TOKENS_HPB] = {};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ typedef struct tokenDefinition_t {
|
|||||||
uint8_t decimals;
|
uint8_t decimals;
|
||||||
} tokenDefinition_t;
|
} tokenDefinition_t;
|
||||||
|
|
||||||
|
#ifdef HAVE_TOKENS_LIST
|
||||||
|
|
||||||
#define NUM_TOKENS_AKROMA 0
|
#define NUM_TOKENS_AKROMA 0
|
||||||
#define NUM_TOKENS_ELLAISM 1
|
#define NUM_TOKENS_ELLAISM 1
|
||||||
#define NUM_TOKENS_ETHEREUM 1102
|
#define NUM_TOKENS_ETHEREUM 1102
|
||||||
@@ -70,4 +72,6 @@ extern tokenDefinition_t const TOKENS_MIX[NUM_TOKENS_MIX];
|
|||||||
extern tokenDefinition_t const TOKENS_REOSC[NUM_TOKENS_REOSC];
|
extern tokenDefinition_t const TOKENS_REOSC[NUM_TOKENS_REOSC];
|
||||||
extern tokenDefinition_t const TOKENS_HPB[NUM_TOKENS_HPB];
|
extern tokenDefinition_t const TOKENS_HPB[NUM_TOKENS_HPB];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _TOKENS_H_ */
|
#endif /* _TOKENS_H_ */
|
||||||
|
|||||||
Reference in New Issue
Block a user