Skip to content

Transaction builder for transfers: EGLD / ESDTs / NFTs. Sender & receiver usernames.#1845

Merged
optout21 merged 7 commits intotrustwallet:masterfrom
multiversx:elrond-esdt
Jan 14, 2022
Merged

Transaction builder for transfers: EGLD / ESDTs / NFTs. Sender & receiver usernames.#1845
optout21 merged 7 commits intotrustwallet:masterfrom
multiversx:elrond-esdt

Conversation

@andreibancioiu
Copy link
Copy Markdown
Contributor

@andreibancioiu andreibancioiu commented Dec 15, 2021

Description

  • Allow transactions to specify sender's and receiver's usernames (wrt. Elrond DNS).
  • Allow one to create EGLD / ESDT / NFT transfers via a TransactionFactory. This one depends on a NetworkConfig object, initialized with proper (Mainnet) defaults.
  • Adjusted existing tests to use the well-known devnet / testnet accounts.
  • Breaking change: the file Elrond.proto has been redesigned to accommodate the new features, and thus the construction of SigningInput objects has to be adjusted by clients of wallet-core, as well. Before the change - Kotlin, Swift. After the change - Kotlin, Swift.

Testing instructions

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change) - see description.

Checklist

  • Prefix PR title with [WIP] if necessary.
  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • I have read the guidelines for adding a new blockchain
  • If there is a related Issue, mention it in the description (e.g. Fixes #<issue_number> ).

…elds on transaction message: sender & receiver usernames.
@optout21
Copy link
Copy Markdown
Contributor

@andreibancioiu , nice extensive work across all layers.
However, I have a conceptual issue with this change:
we try to keep blockchain-specific C-exposed TWXxx methods to a minimum. The preferred approach is to extend .proto messages with the transaction types, details, incl. gas details, etc.
Did you consider this approach? Would it be possible to support these transactions through the Elrond .proto file?

@trustwallet trustwallet deleted a comment from costeazabica11213 Dec 17, 2021
@andreibancioiu
Copy link
Copy Markdown
Contributor Author

@catenocrypt, thanks for the input!

I will come back with the changes - define messages in the .proto file, drop the new TW* methods, and let the Signer component decide on the appropriate transaction logic (this would, in the end, delegate to the not-C-exported-anymore TransactionFactory).

@hewigovens hewigovens marked this pull request as draft December 27, 2021 01:25
Copy link
Copy Markdown
Contributor

@hewigovens hewigovens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I converted it to draft status, feel free to mark it ready to review when you make those changes

@andreibancioiu andreibancioiu marked this pull request as ready for review December 31, 2021 12:07
Copy link
Copy Markdown
Contributor

@hewigovens hewigovens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments, overall looks good

Comment thread src/Elrond/Codec.cpp Outdated

void normalizeHexStringTopLevel(std::string& value);

std::string Codec::encodeStringTopLevel(const std::string& value) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can remove suffix like TopLevel from all methods?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two distinct types of encoding (top level vs. nested) - however, indeed, the suffix was redundant in this case, I've removed it. In the future, if needed (most probably not), we might define two distinct classes: TopLevelCodec and NestedCodec.

Comment thread src/Elrond/Codec.cpp Outdated
Comment thread src/Elrond/Codec.h Outdated
Comment thread src/Elrond/Serialization.cpp Outdated
Comment thread src/Elrond/TransactionFactory.cpp Outdated
Comment thread src/proto/Elrond.proto Outdated

// A transaction, typical balance transfer
// Generic / general-purpose transaction. Using one of the more specific messages (below) is recommended.
// TODO: Ideally, we should rename this to "Transaction" or "GenericTransaction". The renaming is temporarily postponed (as of December 2021)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many common fields in EGLDTransfer, ESDTTransfer and ESDTNFTTransfer ( nonce/value/sender/receiver/gas/chain id), please take a look at Cosmos.proto, we can add a nested Transaction message to represent different transfer types

It's ok to do breaking changes as long as we have good test coverage (and mention it in the pr)

Copy link
Copy Markdown
Contributor Author

@andreibancioiu andreibancioiu Jan 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed.

Would the structure below be all right on your side (with breaking change on SigningInput)?

Also, do you think it would be OK if we postpone this breaking change to the next PR? If not, let me know, and I will include the change in this PR.

SigningInput:
        bytes private_key
	string chain_id
	Gas gas

	oneof:
		- GenericTransaction
		- EGLDTransfer
		- ESDTTransfer
		- ESDTNFTTransfer

Gas:
    uint64      gas_limit
    uint64      gas_price

		
GenericTransaction:
	Accounts accounts
	string      value
	string      data
	uint32      version
	uint32      options

EGLDTransfer:
	Accounts accounts
	string      amount

ESDTTransfer:
	Accounts accounts
	string      amount
	string      token_identifier

ESDTNFTTransfer:
	Accounts accounts
	string      amount
	string      token_collection
    	uint64      token_nonce

Accounts (nested in transfers / generic transaction, not nested in SigningInput):
    uint64    sender_nonce
    string      receiver
    string      sender
    string      sender_username
    string      receiver_username

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, no need to add Gas and just use gas_limit and gas_price

Copy link
Copy Markdown
Contributor Author

@andreibancioiu andreibancioiu Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the Elrond.proto file and made the necessary changes - mostly in TransactionFactory.cpp and in the test files. A new class has been defined, as well: Transaction.

Thanks for the .proto refactor idea!

Perhaps it would make sense to merge the file Serialization.cpp into the new Transaction.cpp, in a future PR. Another (unrelated) idea for a future PR would be to move Transaction construction logic (currently in TransactionFactory) and gas estimation logic (currently in GasEstimator) into subclasses of the (abstract) Transaction class, such as: GenericActionTransaction, EGLDTransferTransaction, ESDTTransferTransaction etc.

Copy link
Copy Markdown
Contributor

@hewigovens hewigovens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍,just to confirm senderUsername and receiverUsername are optional, correct?

@andreibancioiu
Copy link
Copy Markdown
Contributor Author

+1,just to confirm senderUsername and receiverUsername are optional, correct?

Yes, they are optional.

Comment thread src/Elrond/GasEstimator.h Outdated
Comment on lines +19 to +21
uint64_t forEGLDTransfer(size_t dataLength);
uint64_t forESDTTransfer(size_t dataLength);
uint64_t forESDTNFTTransfer(size_t dataLength);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider const for these methods.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added const qualifier.

Comment thread src/Elrond/NetworkConfig.h Outdated
public:
NetworkConfig();

const std::string& getChainId();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const std::string& getChainId();
const std::string& getChainId() const;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added const qualifier.

Comment thread src/Elrond/NetworkConfig.cpp Outdated
Comment on lines +84 to +88
// Example for ...
// if (timestamp > ...) {
// networkConfig.setPerDataByte(...)
// networkConfig.setMinGasPrice(...)
// }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this example needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the example.

Comment thread src/Elrond/Codec.cpp Outdated
auto valueAsData = Data();
encode256BE(valueAsData, value, 256);

std::string encoded = normalizeHex(hex(valueAsData));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using hex(store(value, 0)), that will have no leading 0s

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the suggestion - I've called store() without the second & optional parameter (the second parameter is newly introduced in master and would only be available after rebase - I've skipped the rebase, which could possibly break the diff of the PR on github's code review interface).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rigth, the parameter was just added, 0 is the default which is the same as the previous behaviour (no padding), so it should not cause issue in merge.

Comment thread src/Elrond/Codec.cpp Outdated
}

std::string Codec::encodeUint64(uint64_t value) {
std::string encoded = normalizeHex(hex(value));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using hex(store(uint256_t(value), 0)), that will have no leading 0s

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the suggestion - I've called store() without the second & optional parameter (the second parameter is newly introduced in master and would only be available after rebase - I've skipped the rebase, which could possibly break the diff of the PR on github's code review interface).

Comment thread src/HexCoding.h Outdated
return parse_hex(string.begin(), string.end());
}

/// "Normalizes" a hexadecimal string (redundant leading zeros are removed, even length is ensured).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • hex(Data) will always return even digits, and the same number of bytes as the input.
  • hex(uint64_t) will always return 8 bytes (16 hex digits), possibly with leading zeroes.
  • odd-length check is not needed
  • I prefer dropping this method, and using alternative, as suggested in the two places where it is used

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion of using store(). I've dropped this method (and its tests).

@optout21
Copy link
Copy Markdown
Contributor

Thanks for the rework.
I noted some missing const qualifiers on some methods, simplification of normalizeHex, otherwise all OK.

@noahNewton
Copy link
Copy Markdown

Signing is not working using the new strategy, even your tests are failing, fix please

@hewigovens
Copy link
Copy Markdown
Contributor

@noahNewton Can you elaborate the issue? your sample code, error messages and expected result

@noahNewton
Copy link
Copy Markdown

noahNewton commented Mar 31, 2022

@hewigovens

The issue is the signing is broken, I input all the required variables, receive a signature, and then when I try to POST the signature to the endpoint: https://devnet-api.elrond.com/transactions with the payload of {data: signature you gave me}, it responds with a 400 status code "Bad Request" error with the message "Sender must be provided", we have been checking very carefully and we are inputting a sender 100%, even when we take the "expectedSignature" variable from your test file (https://github.com/trustwallet/wallet-core/blob/master/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt) and send this as a payload the POSt request returns the exact same error, so I think either the devnet API is broken or you're not incorporating the "sender" on your end when constructing the transaction signature

@hewigovens
Copy link
Copy Markdown
Contributor

Will try it on our side

perpetua-engineering pushed a commit to perpetua-engineering/wallet-core that referenced this pull request Feb 25, 2026
…iver usernames. (trustwallet#1845)

* Add transaction builder for transfers: EGLD / ESDT / NFTs. Add new fields on transaction message: sender & receiver usernames.

* Fix after review: less C exports, added proto structures. Refactoring & better tests.

* Fix after review: remove not used files.

* Fix after review (renaming, refactoring, cleanup).

* Fix after review - redesign .proto structures.

* Fix Android / IOS tests.

* Fix after review: add missing "const" qualifiers, drop function normalizeHex().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants