Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Chains/BNBGreenfield]: Add BNB Greenfield #3394

Merged
merged 13 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class CoinAddressDerivationTests {
ETHEREUM, SMARTCHAIN, POLYGON, OPTIMISM, ZKSYNC, ARBITRUM, ARBITRUMNOVA, ECOCHAIN, AVALANCHECCHAIN, XDAI,
FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS,
AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN, POLYGONZKEVM, SCROLL,
CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address)
CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address)
RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address)
ETHEREUMCLASSIC -> assertEquals("0x078bA3228F3E6C08bEEac9A005de0b7e7089aD1c", address)
GOCHAIN -> assertEquals("0x5940ce4A14210d4Ccd0ac206CE92F21828016aC2", address)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright © 2017-2023 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.greenfield

import com.google.protobuf.ByteString
import com.trustwallet.core.app.utils.toHexByteArray
import org.junit.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.Greenfield

class TestGreenfieldSigner {

init {
System.loadLibrary("TrustWalletCore")
}

@Test
fun GreenfieldTransactionSigning() {
// Successfully broadcasted: https://greenfieldscan.com/tx/ED8508F3C174C4430B8EE718A6D6F0B02A8C516357BE72B1336CF74356529D19

val key =
PrivateKey("825d2bb32965764a98338139412c7591ed54c951dd65504cd8ddaeaa0fea7b2a".toHexByteArray())

val msgSend = Greenfield.Message.Send.newBuilder().apply {
fromAddress = "0xA815ae0b06dC80318121745BE40e7F8c6654e9f3"
toAddress = "0x8dbD6c7Ede90646a61Bbc649831b7c298BFd37A0"
addAmounts(Greenfield.Amount.newBuilder().apply {
amount = "1234500000000000"
denom = "BNB"
})
}.build()

val greenfieldFee = Greenfield.Fee.newBuilder().apply {
gas = 1200
addAmounts(Greenfield.Amount.newBuilder().apply {
amount = "6000000000000"
denom = "BNB"
})
}.build()

val signingInput = Greenfield.SigningInput.newBuilder().apply {
signingMode = Greenfield.SigningMode.Eip712
encodingMode = Greenfield.EncodingMode.Protobuf
accountNumber = 15952
ethChainId = "5600"
cosmosChainId = "greenfield_5600-1"
memo = "Trust Wallet test memo"
sequence = 0
fee = greenfieldFee
mode = Greenfield.BroadcastMode.SYNC
privateKey = ByteString.copyFrom(key.data())
addMessages(Greenfield.Message.newBuilder().apply {
sendCoinsMessage = msgSend
})
}.build()

val output = AnySigner.sign(signingInput, CoinType.GREENFIELD, Greenfield.SigningOutput.parser())

assertEquals(
output.serialized,
"{\"mode\":\"BROADCAST_MODE_SYNC\",\"tx_bytes\":\"CqwBCpEBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnEKKjB4QTgxNWFlMGIwNmRDODAzMTgxMjE3NDVCRTQwZTdGOGM2NjU0ZTlmMxIqMHg4ZGJENmM3RWRlOTA2NDZhNjFCYmM2NDk4MzFiN2MyOThCRmQzN0EwGhcKA0JOQhIQMTIzNDUwMDAwMDAwMDAwMBIWVHJ1c3QgV2FsbGV0IHRlc3QgbWVtbxJzClYKTQomL2Nvc21vcy5jcnlwdG8uZXRoLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohAhm/mQgs8vzaqBLW66HrqQNv86PYTBgXyElU1OiuKD/sEgUKAwjIBRIZChQKA0JOQhINNjAwMDAwMDAwMDAwMBCwCRpBwbRb1qEAWwaqVfmp1Mn7iMi7wwV/oPi2J2eW9NBIdNoky+ZL+uegS/kY+funCOrqVZ+Kbol9/djAV+bQaNUB0xw=\"}"
)
assertEquals(output.errorMessage, "")
}
}
1 change: 1 addition & 0 deletions docs/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ This list is generated from [./registry.json](../registry.json)
| 2301 | Qtum | QTUM | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/qtum/info/logo.png" width="32" /> | <https://qtum.org> |
| 2718 | Nebulas | NAS | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/nebulas/info/logo.png" width="32" /> | <https://nebulas.io> |
| 3030 | Hedera | HBAR | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/hedera/info/logo.png" width="32" /> | <https://hedera.com/> |
| 5600 | BNB Greenfield | BNB | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/greenfield/info/logo.png" width="32" /> | <https://greenfield.bnbchain.org> |
| 6060 | GoChain | GO | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/gochain/info/logo.png" width="32" /> | <https://gochain.io> |
| 8453 | Base | ETH | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/info/logo.png" width="32" /> | <https://base.mirror.xyz/> |
| 8964 | NULS | NULS | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/nuls/info/logo.png" width="32" /> | <https://nuls.io> |
Expand Down
1 change: 1 addition & 0 deletions include/TrustWalletCore/TWBlockchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ enum TWBlockchain {
TWBlockchainHedera = 48, // Hedera
TWBlockchainTheOpenNetwork = 49,
TWBlockchainSui = 50,
TWBlockchainGreenfield = 51,
};

TW_EXTERN_C_END
1 change: 1 addition & 0 deletions include/TrustWalletCore/TWCoinType.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ enum TWCoinType {
TWCoinTypeSei = 19000118,
TWCoinTypeArbitrumNova = 10042170,
TWCoinTypeLinea = 59144,
TWCoinTypeGreenfield = 5600,
};

/// Returns the blockchain for a coin type.
Expand Down
31 changes: 31 additions & 0 deletions registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -4081,6 +4081,37 @@
"documentation": "https://doc.confluxnetwork.org/docs/espace"
}
},
{
"id": "greenfield",
"name": "Greenfield",
"displayName": "BNB Greenfield",
"coinId": 5600,
"symbol": "BNB",
"decimals": 18,
"chainId": "5600",
"blockchain": "Greenfield",
"derivation": [
{
"path": "m/44'/60'/0'/0/0"
}
],
"curve": "secp256k1",
"publicKeyType": "secp256k1",
"addressHasher": "keccak256",
"explorer": {
"url": "https://greenfieldscan.com",
"txPath": "/tx/",
"accountPath": "/account/",
"sampleTx": "9F895CF2DD64FB1F428CEFCF2A6585A813C3540FC9FE1EF42DB1DA2CB1DF55AB",
"sampleAccount": "0x9d1d97adfcd324bbd603d3872bd78e04098510b1"
},
"info": {
"url": "https://greenfield.bnbchain.org",
"source": "https://github.com/bnb-chain/greenfield",
"rpc": "https://gnfd-testnet-fullnode-tendermint-us.bnbchain.org",
"documentation": "https://docs.bnbchain.org/greenfield-docs"
}
},
{
"id": "opbnb",
"name": "OpBNB",
Expand Down
2 changes: 1 addition & 1 deletion samples/kmp/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("com.trustwallet:wallet-core-kotlin:3.2.12")
implementation("com.trustwallet:wallet-core-kotlin:3.2.13")
}
}
val commonTest by getting {
Expand Down
3 changes: 3 additions & 0 deletions src/Coin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#include "Hedera/Entry.h"
#include "TheOpenNetwork/Entry.h"
#include "Sui/Entry.h"
#include "Greenfield/Entry.h"
// end_of_coin_includes_marker_do_not_modify

using namespace TW;
Expand Down Expand Up @@ -119,6 +120,7 @@ Everscale::Entry EverscaleDP;
Hedera::Entry HederaDP;
TheOpenNetwork::Entry tonDP;
Sui::Entry SuiDP;
Greenfield::Entry GreenfieldDP;
// end_of_coin_dipatcher_declarations_marker_do_not_modify

CoinEntry* coinDispatcher(TWCoinType coinType) {
Expand Down Expand Up @@ -176,6 +178,7 @@ CoinEntry* coinDispatcher(TWCoinType coinType) {
case TWBlockchainHedera: entry = &HederaDP; break;
case TWBlockchainTheOpenNetwork: entry = &tonDP; break;
case TWBlockchainSui: entry = &SuiDP; break;
case TWBlockchainGreenfield: entry = &GreenfieldDP; break;
// end_of_coin_dipatcher_switch_marker_do_not_modify

default: entry = nullptr; break;
Expand Down
5 changes: 5 additions & 0 deletions src/Cosmos/Protobuf/tx_signing.proto
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ enum SignMode {
// SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses
// Amino JSON and will be removed in the future.
SIGN_MODE_LEGACY_AMINO_JSON = 127;

// Greenfield specific signing mode.
// SIGN_MODE_EIP_712 specifies the sign mode for EIP 712 signing on the Cosmos
// SDK. Ref: https://eips.ethereum.org/EIPS/eip-712
SIGN_MODE_EIP_712 = 712;
}

// SignatureDescriptors wraps multiple SignatureDescriptor's.
Expand Down
7 changes: 4 additions & 3 deletions src/Ethereum/ABI/Array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,10 @@ Data ParamArray::hashStruct() const {
return hash;
}

std::string ParamArray::getExtraTypes(std::vector<std::string>& ignoreList) const {
const auto& proto = getProtoElem();
return (proto != nullptr) ? proto->getExtraTypes(ignoreList) : "";
void ParamArray::fillExtraTypesMap(ExtraTypesMap& extraTypes) const {
if (const auto& proto = getProtoElem(); proto != nullptr) {
proto->fillExtraTypesMap(extraTypes);
}
}

std::shared_ptr<ParamBase> ParamArray::clone() const {
Expand Down
2 changes: 1 addition & 1 deletion src/Ethereum/ABI/Array.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ParamArray final : public ParamCollection {
bool decode(const Data& encoded, size_t& offset_inout) override;
bool setValueJson(const std::string& value) override;
Data hashStruct() const override;
std::string getExtraTypes(std::vector<std::string>& ignoreList) const override;
void fillExtraTypesMap(ExtraTypesMap& extraTypes) const override;
std::shared_ptr<ParamBase> clone() const override;
};

Expand Down
11 changes: 8 additions & 3 deletions src/Ethereum/ABI/ParamBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@

#include "Data.h"

#include <string>
#include <map>
#include <memory>
#include <string>

namespace TW::Ethereum::ABI {

// A map of `StructName -> StructType` key-values, where `StructType` is a full type including the structure name.
// Referenced struct type should be sorted by name see: https://eips.ethereum.org/EIPS/eip-712#definition-of-encodetype
using ExtraTypesMap = std::map<std::string, std::string, std::less<>>;

/// Abstract base class for parameters.
class ParamBase
{
Expand All @@ -26,8 +31,8 @@ class ParamBase
virtual bool setValueJson(const std::string& value) = 0;
// EIP712-style hash of the value (used for signing); default implementation
virtual Data hashStruct() const;
// Helper for EIP712 encoding; provide full type of all used types (recursively). Default is empty implementation.
virtual std::string getExtraTypes([[maybe_unused]] std::vector<std::string>& ignoreList) const { return ""; }
// Helper for EIP712 encoding; fill the given `extraTypes` (recursively).
virtual void fillExtraTypesMap([[maybe_unused]] ExtraTypesMap& extraTypes) const {}
// Creates a copy of this element.
// This method **must** be implemented in a `final` class only.
virtual std::shared_ptr<ParamBase> clone() const = 0;
Expand Down
51 changes: 20 additions & 31 deletions src/Ethereum/ABI/ParamStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,31 +72,10 @@ Data ParamSetNamed::encodeHashes() const {
return hashes;
}

std::string ParamSetNamed::getExtraTypes(std::vector<std::string>& ignoreList) const {
std::string types;

auto params = _params;
/// referenced struct type should be sorted by name see: https://eips.ethereum.org/EIPS/eip-712#definition-of-encodetype
std::stable_sort(params.begin(), params.end(), [](auto& a, auto& b) {
auto lhs = a->getType();
auto rhs = b->getType();
if (ParamFactory::isPrimitive(lhs)) {
return true;
}
if (ParamFactory::isPrimitive(rhs)) {
return true;
}
return lhs.compare(rhs) < 0;
});

for (auto& p : params) {
auto pType = p->_param->getType();
if (std::find(ignoreList.begin(), ignoreList.end(), pType) == ignoreList.end()) {
types += p->getExtraTypes(ignoreList);
ignoreList.push_back(pType);
}
void ParamSetNamed::fillExtraTypesMap(ExtraTypesMap& extraTypes) const {
for (auto& p : _params) {
p->fillExtraTypesMap(extraTypes);
}
return types;
}

std::shared_ptr<ParamNamed> ParamSetNamed::findParamByName(const std::string& name) const {
Expand Down Expand Up @@ -140,14 +119,24 @@ Data ParamStruct::hashStruct() const {
return hash;
}

std::string ParamStruct::getExtraTypes(std::vector<std::string>& ignoreList) const {
std::string types;
if (std::find(ignoreList.begin(), ignoreList.end(), _name) == ignoreList.end()) {
types += _name + _params.getType();
ignoreList.push_back(_name);
std::string ParamStruct::encodeType() const {
ExtraTypesMap extraTypes;
fillExtraTypesMap(extraTypes);
auto structFullType = extraTypes[_name];
extraTypes.erase(_name);

for (const auto& [paramName, paramType] : extraTypes) {
structFullType += paramType;
}
return structFullType;
}

void ParamStruct::fillExtraTypesMap(ExtraTypesMap& extraTypes) const {
if (extraTypes.contains(_name)) {
return;
}
types += _params.getExtraTypes(ignoreList);
return types;
extraTypes[_name] = _name + _params.getType();
_params.fillExtraTypesMap(extraTypes);
}

std::shared_ptr<ParamBase> ParamStruct::clone() const {
Expand Down
11 changes: 4 additions & 7 deletions src/Ethereum/ABI/ParamStruct.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ParamNamed final : public ParamBase
bool decode(const Data& encoded, size_t& offset_inout) override { return _param->decode(encoded, offset_inout); }
bool setValueJson(const std::string& value) override { return _param->setValueJson(value); }
Data hashStruct() const override { return _param->hashStruct(); }
std::string getExtraTypes(std::vector<std::string>& ignoreList) const override { return _param->getExtraTypes(ignoreList); }
void fillExtraTypesMap(ExtraTypesMap& extraTypes) const override { _param->fillExtraTypesMap(extraTypes); }
std::shared_ptr<ParamBase> clone() const override { return cloneNamed(); }
std::shared_ptr<ParamNamed> cloneNamed() const;
};
Expand All @@ -56,7 +56,7 @@ class ParamSetNamed {
std::shared_ptr<ParamNamed> getParam(int idx) const { return _params[idx]; }
std::string getType() const;
Data encodeHashes() const;
std::string getExtraTypes(std::vector<std::string>& ignoreList) const;
void fillExtraTypesMap(ExtraTypesMap& extraTypes) const;
std::shared_ptr<ParamNamed> findParamByName(const std::string& name) const;
ParamSetNamed clone() const;
};
Expand All @@ -79,10 +79,7 @@ class ParamStruct final : public ParamCollection
/// Compute the hash of a struct, used for signing, according to EIP712
Data hashStruct() const override;
/// Get full type, extended by used sub-types, of the form 'Mail(Person from,Person to,string contents)Person(string name,address wallet)'
std::string encodeType() const {
std::vector<std::string> ignoreList;
return getExtraTypes(ignoreList);
}
std::string encodeType() const;
/// Get the hash of the full type.
Data hashType() const;

Expand All @@ -93,7 +90,7 @@ class ParamStruct final : public ParamCollection
bool decode([[maybe_unused]] const Data& encoded, [[maybe_unused]] size_t& offset_inout) override { return true; }
bool setValueJson([[maybe_unused]] const std::string& value) override { return false; } // see makeStruct
Data encodeHashes() const;
std::string getExtraTypes(std::vector<std::string>& ignoreList) const override;
void fillExtraTypesMap(ExtraTypesMap& extraTypes) const override;
std::shared_ptr<ParamBase> clone() const override;
std::shared_ptr<ParamNamed> findParamByName(const std::string& name) const { return _params.findParamByName(name); }

Expand Down
Loading