diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 8bfe4dd5268..18ab0a98320 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -83,5 +83,6 @@ class CoinAddressDerivationTests { CARDANO -> assertEquals("addr1snpa4z7ntyfszv7ckquprdw75w4qjqh0qmya9jtkpxxlzxghlqyvv7l0yjamh8fxraw06p3ua8sj2g2gv98v4849s43t9g2999kquuu5egnprk", address) NEO -> assertEquals("AT6w7PJvwPcSqHvtbNBY2aHPDv12eW5Uuf", address) FILECOIN -> assertEquals("f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori", address) + ELROND -> assertEquals("erd1jfcy8aeru6vlx4fe6h3pc3vlpe2cnnur5zetxdhp879yagq7vqvs8na4f8", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt new file mode 100644 index 00000000000..05c17837ddc --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt @@ -0,0 +1,49 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.elrond + +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestElrondAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + private val aliceBech32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" + private var aliceSeedHex = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf" + private var alicePubKeyHex = "0xfd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293" + + @Test + fun testAddressFromPrivateKey() { + val key = PrivateKey(aliceSeedHex.toHexByteArray()) + val pubKey = key.publicKeyEd25519 + val address = AnyAddress(pubKey, CoinType.ELROND) + + assertEquals(alicePubKeyHex, pubKey.data().toHex()) + assertEquals(aliceBech32, address.description()) + } + + @Test + fun testAddressFromPublicKey() { + val pubKey = PublicKey(alicePubKeyHex.toHexByteArray(), PublicKeyType.ED25519) + val address = AnyAddress(pubKey, CoinType.ELROND) + + assertEquals(aliceBech32, address.description()) + } + + @Test + fun testAddressFromString() { + val address = AnyAddress(aliceBech32, CoinType.ELROND) + + assertEquals(aliceBech32, address.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt new file mode 100644 index 00000000000..160dd9d7765 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt @@ -0,0 +1,57 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.elrond + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexByteArray +import junit.framework.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.CoinType +import wallet.core.jni.PrivateKey +import wallet.core.jni.proto.Elrond + +class TestElrondSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + val aliceBech32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" + var aliceSeedHex = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf" + var alicePubKeyHex = "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293" + + val bobBech32 = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" + var bobSeedHex = "e3a3a3d1ac40d42d8fd4c569a9749b65a1250dd3d10b6f4e438297662ea4850e" + var bobPubKeyHex = "c70cf50b238372fffaf7b7c5723b06b57859d424a2da621bcc1b2f317543aa36" + + @Test + fun signTransaction() { + val transaction = Elrond.TransactionMessage.newBuilder() + .setNonce(0) + .setValue("0") + .setSender(aliceBech32) + .setReceiver(bobBech32) + .setGasPrice(200000000000000) + .setGasLimit(500000000) + .setData("foo") + .build() + + val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) + + val signingInput = Elrond.SigningInput.newBuilder() + .setPrivateKey(privateKey) + .setTransaction(transaction) + .build() + + val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308" + + assertEquals(expectedSignature, output.signature) + assertEquals("""{"nonce":0,"value":"0","receiver":"$bobBech32","sender":"$aliceBech32","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"$expectedSignature"}""", output.encoded) + } +} diff --git a/coins.json b/coins.json index 18047f66535..175ef086de3 100644 --- a/coins.json +++ b/coins.json @@ -1330,5 +1330,27 @@ "clientPublic": "", "clientDocs": "https://docs.lotu.sh" } + }, + { + "id": "elrond", + "name": "Elrond", + "symbol": "ERD", + "decimals": 18, + "blockchain": "ElrondNetwork", + "derivationPath": "m/44'/508'/0'/0'/0'", + "curve": "ed25519", + "publicKeyType": "ed25519", + "hrp": "erd", + "explorer": { + "url": "https://explorer.elrond.com", + "txPath": "/transactions/", + "accountPath": "/address/" + }, + "info": { + "url": "https://elrond.com/", + "client": "https://github.com/ElrondNetwork/elrond-go", + "clientPublic": "https://api.elrond.com", + "clientDocs": "https://docs.elrond.com" + } } ] diff --git a/docs/coins.md b/docs/coins.md index 2fae8caf2e8..c99e9d92157 100644 --- a/docs/coins.md +++ b/docs/coins.md @@ -43,6 +43,7 @@ This list is generated from [./coins.json](../coins.json) | 461 | Filecoin | FIL | | | | 500 | Theta | THETA | | | | 501 | Solana | SOL | | | +| 508 | Elrond | ERD | | | | 714 | Binance | BNB | | | | 818 | VeChain | VET | | | | 820 | Callisto | CLO | | | diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index 8b1b82d6028..6a174d8af7c 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -45,6 +45,7 @@ enum TWBlockchain { TWBlockchainCardano = 30, TWBlockchainNEO = 31, TWBlockchainFilecoin = 32, + TWBlockchainElrondNetwork = 33, }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 0c2a152cec0..bf70c377ee9 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -79,6 +79,7 @@ enum TWCoinType { TWCoinTypeKusama = 434, TWCoinTypePolkadot = 354, TWCoinTypeFilecoin = 461, + TWCoinTypeElrond = 508, }; /// Returns the blockchain for a coin type. diff --git a/src/Coin.cpp b/src/Coin.cpp index c6135ddbec0..d0a0454d2ec 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -51,6 +51,7 @@ #include "Waves/Entry.h" #include "Zcash/Entry.h" #include "Zilliqa/Entry.h" +#include "Elrond/Entry.h" // end_of_coin_includes_marker_do_not_modify using namespace TW; @@ -100,6 +101,7 @@ void setupDispatchers() { new Waves::Entry(), new Zcash::Entry(), new Zilliqa::Entry(), + new Elrond::Entry(), }; // end_of_coin_entries_marker_do_not_modify dispatchMap.clear(); diff --git a/src/Elrond/Address.cpp b/src/Elrond/Address.cpp new file mode 100644 index 00000000000..1af84ace200 --- /dev/null +++ b/src/Elrond/Address.cpp @@ -0,0 +1,17 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "Address.h" + +using namespace TW::Elrond; + +const std::string Address::hrp = HRP_ELROND; + +bool Address::isValid(const std::string& string) { + return Bech32Address::isValid(string, hrp); +} diff --git a/src/Elrond/Address.h b/src/Elrond/Address.h new file mode 100644 index 00000000000..1f96edc6700 --- /dev/null +++ b/src/Elrond/Address.h @@ -0,0 +1,38 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../Data.h" +#include "../PublicKey.h" +#include "../Bech32Address.h" + +#include + +namespace TW::Elrond { + +class Address : public Bech32Address { + public: + // The human-readable part of the address, as defined in "coins.json" + static const std::string hrp; // HRP_ELROND + + /// Determines whether a string makes a valid address. + static bool isValid(const std::string& string); + + Address() : Bech32Address(hrp) {} + + /// Initializes an address with a key hash. + Address(Data keyHash) : Bech32Address(hrp, keyHash) {} + + /// Initializes an address with a public key. + Address(const PublicKey& publicKey) : Bech32Address(hrp, publicKey.bytes) {} + + static bool decode(const std::string& addr, Address& obj_out) { + return Bech32Address::decode(addr, obj_out, hrp); + } +}; + +} // namespace TW::Elrond diff --git a/src/Elrond/Entry.cpp b/src/Elrond/Entry.cpp new file mode 100644 index 00000000000..76b806ff128 --- /dev/null +++ b/src/Elrond/Entry.cpp @@ -0,0 +1,31 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Entry.h" + +#include "Address.h" +#include "Signer.h" + +using namespace TW::Elrond; +using namespace std; + +// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. + +bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { + return Address::isValid(address); +} + +string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { + return Address(publicKey).string(); +} + +void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { + return Signer::signJSON(json, key); +} diff --git a/src/Elrond/Entry.h b/src/Elrond/Entry.h new file mode 100644 index 00000000000..78a5f60a39f --- /dev/null +++ b/src/Elrond/Entry.h @@ -0,0 +1,25 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../CoinEntry.h" + +namespace TW::Elrond { + +/// Entry point for implementation of Elrond coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry: public CoinEntry { +public: + virtual std::vector coinTypes() const { return {TWCoinTypeElrond}; } + virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + virtual bool supportsJSONSigning() const { return true; } + virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; +}; + +} // namespace TW::Elrond diff --git a/src/Elrond/Serialization.cpp b/src/Elrond/Serialization.cpp new file mode 100644 index 00000000000..193daf5d210 --- /dev/null +++ b/src/Elrond/Serialization.cpp @@ -0,0 +1,67 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Serialization.h" + +#include "../Elrond/Address.h" +#include "../proto/Elrond.pb.h" +#include "Base64.h" +#include "PrivateKey.h" + +using namespace TW; + +std::map fields_order { + {"nonce", 1}, + {"value", 2}, + {"receiver", 3}, + {"sender", 4}, + {"gasPrice", 5}, + {"gasLimit", 6}, + {"data", 7}, + {"signature", 8} +}; + +struct FieldsSorter { + bool operator() (const string& lhs, const string& rhs) const { + return fields_order[lhs] < fields_order[rhs]; + } +}; + +template +using sorted_map = std::map; +using sorted_json = nlohmann::basic_json; + +string Elrond::serializeTransaction(const Proto::TransactionMessage& message) { + sorted_json payload { + {"nonce", json(message.nonce())}, + {"value", json(message.value())}, + {"receiver", json(message.receiver())}, + {"sender", json(message.sender())}, + {"gasPrice", json(message.gas_price())}, + {"gasLimit", json(message.gas_limit())}, + }; + + if (!message.data().empty()) { + payload["data"] = json(TW::Base64::encode(TW::data(message.data()))); + } + + return payload.dump(); +} + +string Elrond::serializeSignedTransaction(const Proto::TransactionMessage& message, string signature) { + sorted_json payload { + {"nonce", json(message.nonce())}, + {"value", json(message.value())}, + {"receiver", json(message.receiver())}, + {"sender", json(message.sender())}, + {"gasPrice", json(message.gas_price())}, + {"gasLimit", json(message.gas_limit())}, + {"data", json(message.data())}, + {"signature", json(signature)}, + }; + + return payload.dump(); +} diff --git a/src/Elrond/Serialization.h b/src/Elrond/Serialization.h new file mode 100644 index 00000000000..e56d8a5bf87 --- /dev/null +++ b/src/Elrond/Serialization.h @@ -0,0 +1,21 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../proto/Elrond.pb.h" +#include "Data.h" +#include + +using string = std::string; +using json = nlohmann::json; + +namespace TW::Elrond { + +string serializeTransaction(const Proto::TransactionMessage& message); +string serializeSignedTransaction(const Proto::TransactionMessage& message, string encodedSignature); + +} // namespace diff --git a/src/Elrond/Signer.cpp b/src/Elrond/Signer.cpp new file mode 100644 index 00000000000..20519c8b80a --- /dev/null +++ b/src/Elrond/Signer.cpp @@ -0,0 +1,38 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Signer.h" +#include "Address.h" +#include "Serialization.h" +#include "../PublicKey.h" +#include "HexCoding.h" + +#include + +using namespace TW; +using namespace TW::Elrond; + +Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { + auto privateKey = PrivateKey(input.private_key()); + auto signableAsString = serializeTransaction(input.transaction()); + auto signableAsData = TW::data(signableAsString); + auto signature = privateKey.sign(signableAsData, TWCurveED25519); + auto encodedSignature = hex(signature); + auto encoded = serializeSignedTransaction(input.transaction(), encodedSignature); + + auto protoOutput = Proto::SigningOutput(); + protoOutput.set_signature(encodedSignature); + protoOutput.set_encoded(encoded); + return protoOutput; +} + +std::string Signer::signJSON(const std::string& json, const Data& key) { + auto input = Proto::SigningInput(); + google::protobuf::util::JsonStringToMessage(json, &input); + input.set_private_key(key.data(), key.size()); + auto output = sign(input); + return output.encoded(); +} diff --git a/src/Elrond/Signer.h b/src/Elrond/Signer.h new file mode 100644 index 00000000000..046f5e52d67 --- /dev/null +++ b/src/Elrond/Signer.h @@ -0,0 +1,28 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../Data.h" +#include "../PrivateKey.h" +#include "../proto/Elrond.pb.h" + +namespace TW::Elrond { + +/// Helper class that performs Elrond transaction signing. +class Signer { +public: + /// Hide default constructor + Signer() = delete; + + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; + + /// Signs a json Proto::SigningInput with private key + static std::string signJSON(const std::string& json, const Data& key); +}; + +} // namespace TW::Elrond diff --git a/src/interface/TWAnyAddress.cpp b/src/interface/TWAnyAddress.cpp index fcd2587ddf2..8fab7633d30 100644 --- a/src/interface/TWAnyAddress.cpp +++ b/src/interface/TWAnyAddress.cpp @@ -20,6 +20,7 @@ #include "../Cardano/AddressV3.h" #include "../NEO/Address.h" #include "../Nano/Address.h" +#include "../Elrond/Address.h" #include "../Coin.h" #include "../HexCoding.h" @@ -184,6 +185,16 @@ TWData* _Nonnull TWAnyAddressData(struct TWAnyAddress* _Nonnull address) { data = Data(addr.bytes.begin(), addr.bytes.end()); break; } + + case TWCoinTypeElrond: { + Elrond::Address addr; + if (Elrond::Address::decode(string, addr)) { + data = addr.getKeyHash(); + } + + break; + } + default: break; } return TWDataCreateWithBytes(data.data(), data.size()); diff --git a/src/proto/Elrond.proto b/src/proto/Elrond.proto new file mode 100644 index 00000000000..41b4a0efc06 --- /dev/null +++ b/src/proto/Elrond.proto @@ -0,0 +1,36 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +syntax = "proto3"; + +package TW.Elrond.Proto; +option java_package = "wallet.core.jni.proto"; + +// A transaction, typical balance transfer +message TransactionMessage { + uint64 nonce = 1; + string value = 2; + string receiver = 3; + string sender = 4; + uint64 gas_price = 5; + uint64 gas_limit = 6; + string data = 7; +} + +// Input data necessary to create a signed transaction. +message SigningInput { + bytes private_key = 1; + + oneof message_oneof { + TransactionMessage transaction = 2; + } +} + +// Transaction signing output. +message SigningOutput { + string encoded = 1; + string signature = 2; +} diff --git a/swift/Tests/Blockchains/ElrondTests.swift b/swift/Tests/Blockchains/ElrondTests.swift new file mode 100644 index 00000000000..c3e3e5b4b8c --- /dev/null +++ b/swift/Tests/Blockchains/ElrondTests.swift @@ -0,0 +1,51 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import TrustWalletCore +import XCTest + +class ElrondTests: XCTestCase { + + let aliceBech32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" + let aliceSeedHex = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf" + let alicePubKeyHex = "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293" + let bobBech32 = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" + + func testAddress() { + let key = PrivateKey(data: Data(hexString: aliceSeedHex)!)! + let pubkey = key.getPublicKeyEd25519() + let address = AnyAddress(publicKey: pubkey, coin: .elrond) + let addressFromString = AnyAddress(string: aliceBech32, coin: .elrond)! + + XCTAssertEqual(pubkey.data.hexString, alicePubKeyHex) + XCTAssertEqual(address.description, addressFromString.description) + } + + func testSign() { + let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! + + let input = ElrondSigningInput.with { + $0.transaction = ElrondTransactionMessage.with { + $0.nonce = 0 + $0.value = "0" + $0.sender = aliceBech32 + $0.receiver = bobBech32 + $0.gasPrice = 200000000000000 + $0.gasLimit = 500000000 + $0.data = "foo" + } + + $0.privateKey = privateKey.data + } + + let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308" + let expectedEncoded = #"{"nonce":0,"value":"0","receiver":"\#(bobBech32)","sender":"\#(aliceBech32)","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"\#(expectedSignature)"}"# + + XCTAssertEqual(output.signature, expectedSignature) + XCTAssertEqual(output.encoded, expectedEncoded) + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 81b55b548ed..2a09ac50169 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -190,6 +190,9 @@ class CoinAddressDerivationTests: XCTestCase { case .filecoin: let expectedResult = "f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori" AssetCoinDerivation(coin, expectedResult, derivedAddress, address) + case .elrond: + let expectedResult = "erd1jfcy8aeru6vlx4fe6h3pc3vlpe2cnnur5zetxdhp879yagq7vqvs8na4f8" + AssetCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() } diff --git a/tests/Bech32AddressTests.cpp b/tests/Bech32AddressTests.cpp index ff425cb7c4f..4b6024ecd5e 100644 --- a/tests/Bech32AddressTests.cpp +++ b/tests/Bech32AddressTests.cpp @@ -27,6 +27,10 @@ TEST(Bech32Address, Valid) { ASSERT_TRUE(Bech32Address::isValid("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", "io")); ASSERT_TRUE(Bech32Address::isValid("zil1fwh4ltdguhde9s7nysnp33d5wye6uqpugufkz7", "zil")); + + ASSERT_TRUE(Bech32Address::isValid("erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz", "erd")); + ASSERT_TRUE(Bech32Address::isValid("erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r", "erd")); + ASSERT_TRUE(Bech32Address::isValid("erd19nu5t7hszckwah5nlcadmk5rlchtugzplznskffpwecygcu0520s9tnyy0", "erd")); } TEST(Bech32Address, Invalid) { @@ -48,6 +52,10 @@ TEST(Bech32Address, Invalid) { ASSERT_FALSE(Bech32Address::isValid("")); ASSERT_FALSE(Bech32Address::isValid("0x")); ASSERT_FALSE(Bech32Address::isValid("91cddcebe846ce4d47712287eee53cf17c2cfb7")); + + ASSERT_FALSE(Bech32Address::isValid("", "erd")); + ASSERT_FALSE(Bech32Address::isValid("erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35!", "erd")); + ASSERT_FALSE(Bech32Address::isValid("xerd19nu5t7hszckwah5nlcadmk5rlchtugzplznskffpwecygcu0520s9tnyy0", "erd")); } TEST(Bech32Address, InvalidWrongPrefix) { diff --git a/tests/CoinAddressDerivationTests.cpp b/tests/CoinAddressDerivationTests.cpp index 16ac09114df..1bf597c6d88 100644 --- a/tests/CoinAddressDerivationTests.cpp +++ b/tests/CoinAddressDerivationTests.cpp @@ -71,6 +71,7 @@ TEST(Coin, DeriveAddress) { EXPECT_EQ(TW::deriveAddress(TWCoinTypeFilecoin, privateKey), "f1qsx7qwiojh5duxbxhbqgnlyx5hmpcf7mcz5oxsy"); EXPECT_EQ(TW::deriveAddress(TWCoinTypeNEAR, privateKey), "NEAR2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5HYrdGj"); EXPECT_EQ(TW::deriveAddress(TWCoinTypeSolana, privateKey), "H4JcMPicKkHcxxDjkyyrLoQj7Kcibd9t815ak4UvTr9M"); + EXPECT_EQ(TW::deriveAddress(TWCoinTypeElrond, privateKey), "erd1a6f6fan035ttsxdmn04ellxdlnwpgyhg0lhx5vjv92v6rc8xw9yq83344f"); } } // namespace TW diff --git a/tests/CoinAddressValidationTests.cpp b/tests/CoinAddressValidationTests.cpp index 7b08cf9f61b..86a0d7f309a 100644 --- a/tests/CoinAddressValidationTests.cpp +++ b/tests/CoinAddressValidationTests.cpp @@ -373,4 +373,10 @@ TEST(Coin, ValidateAddressVeChain) { EXPECT_EQ(normalizeAddress(TWCoinTypeVeChain, "0x9d8a62f656a8d1615c1294fd71e9cfb3e4855a4f"), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); } +TEST(Coin, ValidateAddressElrond) { + EXPECT_TRUE(validateAddress(TWCoinTypeElrond, "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz")); + EXPECT_FALSE(validateAddress(TWCoinTypeElrond, "xerd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz")); +} + + } // namespace TW diff --git a/tests/Elrond/AddressTests.cpp b/tests/Elrond/AddressTests.cpp new file mode 100644 index 00000000000..3dd1fb6f166 --- /dev/null +++ b/tests/Elrond/AddressTests.cpp @@ -0,0 +1,79 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include + +#include "HexCoding.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include "Elrond/Address.h" +#include "TestAccounts.h" + +using namespace TW; +using namespace TW::Elrond; + + +TEST(ElrondAddress, Valid) { + ASSERT_TRUE(Address::isValid(ALICE_BECH32)); + ASSERT_TRUE(Address::isValid(BOB_BECH32)); + ASSERT_TRUE(Address::isValid(CAROL_BECH32)); +} + +TEST(ElrondAddress, Invalid) { + ASSERT_FALSE(Address::isValid("")); + ASSERT_FALSE(Address::isValid("foo")); + ASSERT_FALSE(Address::isValid("10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); + ASSERT_FALSE(Address::isValid("xerd10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); + ASSERT_FALSE(Address::isValid("foo10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); + ASSERT_FALSE(Address::isValid(ALICE_PUBKEY_HEX)); +} + +TEST(ElrondAddress, FromString) { + Address alice, bob, carol; + ASSERT_TRUE(Address::decode(ALICE_BECH32, alice)); + ASSERT_TRUE(Address::decode(BOB_BECH32, bob)); + ASSERT_TRUE(Address::decode(CAROL_BECH32, carol)); + + ASSERT_EQ(ALICE_PUBKEY_HEX, hex(alice.getKeyHash())); + ASSERT_EQ(BOB_PUBKEY_HEX, hex(bob.getKeyHash())); + ASSERT_EQ(CAROL_PUBKEY_HEX, hex(carol.getKeyHash())); +} + +TEST(ElrondAddress, FromData) { + const auto alice = Address(parse_hex(ALICE_PUBKEY_HEX)); + const auto bob = Address(parse_hex(BOB_PUBKEY_HEX)); + const auto carol = Address(parse_hex(CAROL_PUBKEY_HEX)); + + ASSERT_EQ(ALICE_BECH32, alice.string()); + ASSERT_EQ(BOB_BECH32, bob.string()); + ASSERT_EQ(CAROL_BECH32, carol.string()); +} + +TEST(ElrondAddress, FromPrivateKey) { + auto aliceKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + auto alice = Address(aliceKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(ALICE_BECH32, alice.string()); + + auto bobKey = PrivateKey(parse_hex(BOB_SEED_HEX)); + auto bob = Address(bobKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(BOB_BECH32, bob.string()); + + auto carolKey = PrivateKey(parse_hex(CAROL_SEED_HEX)); + auto carol = Address(carolKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(CAROL_BECH32, carol.string()); +} + +TEST(ElrondAddress, FromPublicKey) { + auto alice = PublicKey(parse_hex(ALICE_PUBKEY_HEX), TWPublicKeyTypeED25519); + ASSERT_EQ(ALICE_BECH32, Address(alice).string()); + + auto bob = PublicKey(parse_hex(BOB_PUBKEY_HEX), TWPublicKeyTypeED25519); + ASSERT_EQ(BOB_BECH32, Address(bob).string()); + + auto carol = PublicKey(parse_hex(CAROL_PUBKEY_HEX), TWPublicKeyTypeED25519); + ASSERT_EQ(CAROL_BECH32, Address(carol).string()); +} diff --git a/tests/Elrond/SerializationTests.cpp b/tests/Elrond/SerializationTests.cpp new file mode 100644 index 00000000000..80c048bb9da --- /dev/null +++ b/tests/Elrond/SerializationTests.cpp @@ -0,0 +1,54 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include "boost/format.hpp" + +#include "HexCoding.h" +#include "Elrond/Serialization.h" +#include "TestAccounts.h" + +using namespace TW; +using namespace TW::Elrond; + +TEST(ElrondSerialization, SignableString) { + Proto::TransactionMessage message; + message.set_nonce(42); + message.set_value("43"); + message.set_sender("alice"); + message.set_receiver("bob"); + message.set_data("foobar"); + + string jsonString = serializeTransaction(message); + ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"bob","sender":"alice","gasPrice":0,"gasLimit":0,"data":"Zm9vYmFy"})", jsonString); +} + +TEST(ElrondSerialization, SignableStringWithRealData) { + Proto::TransactionMessage message; + message.set_nonce(15); + message.set_value("100"); + message.set_sender(ALICE_BECH32); + message.set_receiver(BOB_BECH32); + message.set_gas_price(200000000000000); + message.set_gas_limit(500000000); + message.set_data("for dinner"); + + string expected = (boost::format(R"({"nonce":15,"value":"100","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"Zm9yIGRpbm5lcg=="})") % BOB_BECH32 % ALICE_BECH32).str(); + string actual = serializeTransaction(message); + ASSERT_EQ(expected, actual); +} + +TEST(ElrondSerialization, SignableStringWithoutData) { + Proto::TransactionMessage message; + message.set_nonce(42); + message.set_value("43"); + message.set_sender("feed"); + message.set_receiver("abba"); + + string jsonString = serializeTransaction(message); + ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"abba","sender":"feed","gasPrice":0,"gasLimit":0})", jsonString); +} diff --git a/tests/Elrond/SignerTests.cpp b/tests/Elrond/SignerTests.cpp new file mode 100644 index 00000000000..adda6c83a4d --- /dev/null +++ b/tests/Elrond/SignerTests.cpp @@ -0,0 +1,89 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "boost/format.hpp" + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "Elrond/Signer.h" +#include "Elrond/Address.h" +#include "TestAccounts.h" + +using namespace TW; +using namespace TW::Elrond; + + +TEST(ElrondSigner, Sign) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_transaction()->set_nonce(0); + input.mutable_transaction()->set_value("0"); + input.mutable_transaction()->set_sender(ALICE_BECH32); + input.mutable_transaction()->set_receiver(BOB_BECH32); + input.mutable_transaction()->set_gas_price(200000000000000); + input.mutable_transaction()->set_gas_limit(500000000); + input.mutable_transaction()->set_data("foo"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(ElrondSigner, SignJSON) { + // Shuffle some fields, assume arbitrary order in the input + auto input = (boost::format(R"({"transaction" : {"data":"foo","value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000}})") % BOB_BECH32 % ALICE_BECH32).str(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + + auto encoded = Signer::signJSON(input, privateKey.bytes); + auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(ElrondSigner, SignWithoutData) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_transaction()->set_nonce(0); + input.mutable_transaction()->set_value("0"); + input.mutable_transaction()->set_sender(ALICE_BECH32); + input.mutable_transaction()->set_receiver(BOB_BECH32); + input.mutable_transaction()->set_gas_price(200000000000000); + input.mutable_transaction()->set_gas_limit(500000000); + input.mutable_transaction()->set_data(""); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "39ab0e18bfce04bf53c9610faa3b9e7cecfca919510a7631e529e9086279b70a4832df32a5d1b8fdceb4e9082f2995da97f9195532c8d611ee749bc312cbf90c"; + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(ElrondSigner, SignJSONWithoutData) { + // Shuffle some fields, assume arbitrary order in the input + auto input = (boost::format(R"({"transaction" : {"value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000}})") % BOB_BECH32 % ALICE_BECH32).str(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + + auto encoded = Signer::signJSON(input, privateKey.bytes); + auto expectedSignature = "39ab0e18bfce04bf53c9610faa3b9e7cecfca919510a7631e529e9086279b70a4832df32a5d1b8fdceb4e9082f2995da97f9195532c8d611ee749bc312cbf90c"; + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedEncoded, encoded); +} diff --git a/tests/Elrond/TWAnySignerTests.cpp b/tests/Elrond/TWAnySignerTests.cpp new file mode 100644 index 00000000000..f9366ab52ba --- /dev/null +++ b/tests/Elrond/TWAnySignerTests.cpp @@ -0,0 +1,55 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "boost/format.hpp" + +#include +#include "HexCoding.h" +#include "../interface/TWTestUtilities.h" +#include "Elrond/Signer.h" +#include "TestAccounts.h" + +using namespace TW; +using namespace TW::Elrond; + + +TEST(TWAnySignerElrond, Sign) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_transaction()->set_nonce(0); + input.mutable_transaction()->set_value("0"); + input.mutable_transaction()->set_sender(ALICE_BECH32); + input.mutable_transaction()->set_receiver(BOB_BECH32); + input.mutable_transaction()->set_gas_price(200000000000000); + input.mutable_transaction()->set_gas_limit(500000000); + input.mutable_transaction()->set_data("foo"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeElrond); + + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(TWAnySignerElrond, SignJSON) { + // Shuffle some fields, assume arbitrary order in the input + auto input = STRING((boost::format(R"({"transaction" : {"data":"foo","value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000}})") % BOB_BECH32 % ALICE_BECH32).str().c_str()); + auto privateKey = DATA(ALICE_SEED_HEX); + auto encoded = WRAPS(TWAnySignerSignJSON(input.get(), privateKey.get(), TWCoinTypeElrond)); + auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeElrond)); + assertStringsEqual(encoded, expectedEncoded.c_str()); +} diff --git a/tests/Elrond/TWCoinTypeTests.cpp b/tests/Elrond/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..9e73e615721 --- /dev/null +++ b/tests/Elrond/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "../interface/TWTestUtilities.h" +#include +#include + + +TEST(TWElrondCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeElrond)); + auto txId = TWStringCreateWithUTF8Bytes("1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8"); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeElrond, txId)); + auto accId = TWStringCreateWithUTF8Bytes("erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj"); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeElrond, accId)); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeElrond)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeElrond)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeElrond), 18); + ASSERT_EQ(TWBlockchainElrondNetwork, TWCoinTypeBlockchain(TWCoinTypeElrond)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeElrond)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeElrond)); + assertStringsEqual(symbol, "ERD"); + assertStringsEqual(txUrl, "https://explorer.elrond.com/transactions/1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8"); + assertStringsEqual(accUrl, "https://explorer.elrond.com/address/erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj"); + assertStringsEqual(id, "elrond"); + assertStringsEqual(name, "Elrond"); +} diff --git a/tests/Elrond/TestAccounts.h b/tests/Elrond/TestAccounts.h new file mode 100644 index 00000000000..55060002abd --- /dev/null +++ b/tests/Elrond/TestAccounts.h @@ -0,0 +1,17 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +const auto ALICE_BECH32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"; +const auto ALICE_SEED_HEX = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf"; +const auto ALICE_PUBKEY_HEX = "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293"; +const auto BOB_BECH32 = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r"; +const auto BOB_SEED_HEX = "e3a3a3d1ac40d42d8fd4c569a9749b65a1250dd3d10b6f4e438297662ea4850e"; +const auto BOB_PUBKEY_HEX = "c70cf50b238372fffaf7b7c5723b06b57859d424a2da621bcc1b2f317543aa36"; +const auto CAROL_BECH32 = "erd19nu5t7hszckwah5nlcadmk5rlchtugzplznskffpwecygcu0520s9tnyy0"; +const auto CAROL_SEED_HEX = "a926316cc3daf8ff25ba3e417797e6dfd51f62ae735ab07148234732f7314052"; +const auto CAROL_PUBKEY_HEX = "2cf945faf0162ceede93fe3addda83fe2ebe2041f8a70b2521767044638fa29f"; diff --git a/tests/interface/TWAnyAddressTests.cpp b/tests/interface/TWAnyAddressTests.cpp index c35b6935a79..dbc77477d5d 100644 --- a/tests/interface/TWAnyAddressTests.cpp +++ b/tests/interface/TWAnyAddressTests.cpp @@ -119,4 +119,11 @@ TEST(AnyAddress, Data) { auto keyHash = WRAPD(TWAnyAddressData(addr.get())); assertHexEqual(keyHash, "172bdf43265c0adfe105a1a8c45b3f406a38362f24"); } + // elrond + { + auto string = STRING("erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeElrond)); + auto pubkey = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(pubkey, "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293"); + } } diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index 62f67695f0e..6b286eee3cb 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -231,6 +231,16 @@ TEST(HDWallet, DeriveAlgorand) { assertHexEqual(privateKeyData, "ce0b7ac644e2b7d9d14d3928b11643f43e48c33d3e328d059fef8add7f070e82"); } +TEST(HDWallet, DeriveElrond) { + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeElrond)); + auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); + auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeElrond, privateKey.get())); + + assertHexEqual(privateKeyData, "0eb593141de897d60a0883320793eb49e63d556ccdf783a87ec014f150d50453"); + assertStringsEqual(address, "erd1a6l7q9cfvrgr80xuzm37tapdr4zm3mwrtl6vt8f45df45x7eadfs8ds5vv"); +} + TEST(HDWallet, ExtendedKeys) { auto words = STRING("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"); auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), STRING("").get())); diff --git a/tests/interface/TWHRPTests.cpp b/tests/interface/TWHRPTests.cpp index 11af1a19e50..3b04ad4f8c3 100644 --- a/tests/interface/TWHRPTests.cpp +++ b/tests/interface/TWHRPTests.cpp @@ -28,6 +28,7 @@ TEST(TWHRP, StringForHRP) { ASSERT_STREQ(stringForHRP(TWHRPMonacoin), "mona"); ASSERT_STREQ(stringForHRP(TWHRPKava), "kava"); ASSERT_STREQ(stringForHRP(TWHRPCardano), "addr"); + ASSERT_STREQ(stringForHRP(TWHRPElrond), "erd"); } TEST(TWHRP, HRPForString) { @@ -47,6 +48,7 @@ TEST(TWHRP, HRPForString) { ASSERT_EQ(hrpForString("mona"), TWHRPMonacoin); ASSERT_EQ(hrpForString("kava"), TWHRPKava); ASSERT_EQ(hrpForString("addr"), TWHRPCardano); + ASSERT_EQ(hrpForString("erd"), TWHRPElrond); } TEST(TWHPR, HPRByCoinType) { @@ -65,6 +67,7 @@ TEST(TWHPR, HPRByCoinType) { ASSERT_EQ(TWHRPMonacoin, TWCoinTypeHRP(TWCoinTypeMonacoin)); ASSERT_EQ(TWHRPKava, TWCoinTypeHRP(TWCoinTypeKava)); ASSERT_EQ(TWHRPCardano, TWCoinTypeHRP(TWCoinTypeCardano)); + ASSERT_EQ(TWHRPElrond, TWCoinTypeHRP(TWCoinTypeElrond)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeAion)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeCallisto));