Skip to content

Conversation

@Tomas-Silva2187
Copy link
Contributor

@Tomas-Silva2187 Tomas-Silva2187 commented Aug 6, 2025

feat(satp): add support for NFTs

Implements support for cross-chain transfers of NFTs with Ethereum and Besu Leafs.

CLOSES #3869
Pull Request Requirements

  • Rebased onto upstream/main branch and squashed into single commit to help maintainers review it more efficient and to avoid spaghetti git commit graphs that obfuscate which commit did exactly what change, when and, why.
  • Have git sign off at the end of commit message to avoid being marked red. You can add -s flag when using git commit command. You may refer to this link for more information.
  • Follow the Commit Linting specification. You may refer to this link for more information.

Character Limit

  • Pull Request Title and Commit Subject must not exceed 72 characters (including spaces and special characters).
  • Commit Message per line must not exceed 80 characters (including spaces and special characters).

A Must Read for Beginners
For rebasing and squashing, here's a must read guide for beginners.

@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch 2 times, most recently from 2ba814c to 52ac5ad Compare August 7, 2025 09:09
Copy link
Contributor

@LordKubaya LordKubaya left a comment

Choose a reason for hiding this comment

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

Please take a look at the comments.


if (token.amount == undefined) {
throw new AmountMissingError(fnTag);
let token: FungibleAsset | NonFungibleAsset;
Copy link
Contributor

Choose a reason for hiding this comment

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

why don't you abstract this with a class name asset, and the fungible and non fungible are extensions of the same?

Copy link
Contributor

Choose a reason for hiding this comment

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

That would be probably the right thing to do, because in the future it will be easier to add new features. @Tomas-Silva2187, @LordKubaya, I propose a middle term, which is leaving this solution (provided it works), and @Tomas-Silva2187 creates a detailed issue with a proposal to implement what @LordKubaya proposed.

Copy link
Contributor Author

@Tomas-Silva2187 Tomas-Silva2187 Sep 11, 2025

Choose a reason for hiding this comment

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

Done as @LordKubaya suggested

return this.wrapperContractName;
case "NONFUNGIBLE":
//TODO implement
//TODO implement: can be the same wrapper of fungible assets
Copy link
Contributor

Choose a reason for hiding this comment

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

Why leave this comment? Isn't this PR the implementation of that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This PR only implements NFT support for Ethereum and Besu. A new issue was created for Fabric support to be later implemented.

@RafaelAPB RafaelAPB self-assigned this Aug 7, 2025
error InsuficientAmountLocked(string tokenId, uint256 amount);

error TokenTypeNotSupported(string tokenId);

Copy link
Contributor

@RafaelAPB RafaelAPB Aug 7, 2025

Choose a reason for hiding this comment

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

Possibly a better name is TokenNotSupported, because we only know what types we support.

Suggested change
error TokenNotSupported(string tokenId);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

if(tokens[tokenId].amount > 0) {
revert TokenLocked(tokenId);
TokenType tt = tokens[tokenId].tokenType;
if(tt == TokenType.NONSTANDARD_FUNGIBLE || tt == TokenType.ERC20) {
Copy link
Contributor

Choose a reason for hiding this comment

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

what do you mean by non standard fungible?

Copy link
Contributor

Choose a reason for hiding this comment

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

We support erc721 (non fungible) tokens and erc20 (fungible). we should probably refer to the specific standards that we support

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe in this situation, tokens that do not follow the ERC APIs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TokenTypes were reduced to Nonstandard Fungible and Nonstandard NonFungible. They kept this nomenclature due to an error in CBDC that occurs when these names are changed. As for the token standards, they were defined as new attribute of assets, independent of the TokenType.

Copy link
Contributor

@RafaelAPB RafaelAPB left a comment

Choose a reason for hiding this comment

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

Thank you very much for your contribution. Overall, this is an impressive contribution. Code is clean, tests are detailed and address the implemented functionality. Please do add documentation on your added feature - namely the "assetAttribute". It is understandable that this parameter is a generalization of the old "amount", but the different supported values should be well-documented.

Prior to merge, please address my and Carlo's comments - we can use the PR threads to discuss.

Great work!

if(tokens[tokenId].amount > 0) {
revert TokenLocked(tokenId);
TokenType tt = tokens[tokenId].tokenType;
if(tt == TokenType.NONSTANDARD_FUNGIBLE || tt == TokenType.ERC20) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We support erc721 (non fungible) tokens and erc20 (fungible). we should probably refer to the specific standards that we support

// upon minting, the minted attribute is set as the value that was minted
// (may it be amount for fungibles, or uniqueDescriptor for non-fungibles)
tokens[tokenId].amount = assetAttribute;
emit Mint(tokenId, assetAttribute);
Copy link
Contributor

Choose a reason for hiding this comment

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

how does this asset attribute work, in particular? Please add documentation on what is the goal of this attribute and how we use it for different standards

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

* @param assetAttribute The asset attribute of tokens to be encoded.
*/
function encodeParams(VarType[] memory variables, string memory tokenId, address receiver, uint256 amount) internal view returns (bytes[] memory){
function encodeParams(VarType[] memory variables, string memory tokenId, address receiver, uint256 assetAttribute) internal view returns (bytes[] memory){
Copy link
Contributor

Choose a reason for hiding this comment

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

this function should have a more specific name since it is not general-purpose encoding

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. It was renamed as AssetParameterIdentifierEncoder, as it encodes custom parameters defined for the assets, to use them in contract calls.


if (token.amount == undefined) {
throw new AmountMissingError(fnTag);
let token: FungibleAsset | NonFungibleAsset;
Copy link
Contributor

Choose a reason for hiding this comment

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

That would be probably the right thing to do, because in the future it will be easier to add new features. @Tomas-Silva2187, @LordKubaya, I propose a middle term, which is leaving this solution (provided it works), and @Tomas-Silva2187 creates a detailed issue with a proposal to implement what @LordKubaya proposed.

throw new WrapperContractError(
`${fnTag}, Wrapper Contract not deployed`,
);
switch (asset.type) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same comments as in besu-leaf

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has been addressed

network: NetworkId;
}

export type Brand<K, T> = K & { __brand: T };
Copy link
Contributor

Choose a reason for hiding this comment

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

nice solution

export interface EvmFungibleAsset extends EvmAsset, FungibleAsset {}
export interface EvmNonFungibleAsset extends EvmAsset, NonFungibleAsset {}

export enum VarType {
Copy link
Contributor

Choose a reason for hiding this comment

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

perhaps assetParameterIdentifier would be a more accurate name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


const proof = await this.bridgeEndPoint.getProof(
asset,
this.claimType,
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks great. However there is quite a lot of code duplication. Could you extract the functionality into a function that can be reused in these different related functionalities?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

/**
* @title SATPTokenContract
* The SATPTokenContract is an example of a custom ERC721 token contract.
* This is a simple contract, meaning it uses functions that assume everything is correct.
Copy link
Contributor

Choose a reason for hiding this comment

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

what do you mean that functions "assume everything is correct"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Initially, the NFT contract was an unsafe OpenZeppelin version, which used functions that did not pre-check if the function caller had authorization over the asset it was interacting with. The currently implemented version uses safe functions, and the approve action, which is required to deal with these aspects of authorization, is implemented.

@RafaelAPB RafaelAPB force-pushed the satp-stg branch 2 times, most recently from 4cacbaf to 44f3bb4 Compare August 20, 2025 15:52
@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch from c548d20 to a03c7b0 Compare August 25, 2025 16:40
@RafaelAPB RafaelAPB force-pushed the satp-stg branch 7 times, most recently from 6617318 to 83f9482 Compare August 27, 2025 13:31
@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch from 1cb5caf to 392aa6e Compare August 28, 2025 22:09
@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch 2 times, most recently from f14db8d to 45e1e33 Compare August 31, 2025 17:02
@RafaelAPB RafaelAPB force-pushed the satp-stg branch 3 times, most recently from b3bb285 to a14f896 Compare September 29, 2025 04:43
@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch from 78bc4c6 to 26c13ed Compare September 29, 2025 10:44
@RafaelAPB
Copy link
Contributor

@Tomas-Silva2187 most items seem to have been addressed. Could you please confirm that you addressed all comments (I see some items without a response). Thank you!

@RafaelAPB RafaelAPB force-pushed the satp-stg branch 2 times, most recently from 9bee4bd to 1ae6a34 Compare October 1, 2025 13:03
@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch 4 times, most recently from 242fcd7 to d5beace Compare October 9, 2025 16:41
@Tomas-Silva2187 Tomas-Silva2187 changed the base branch from satp-stg to main October 9, 2025 16:41
@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch from d5beace to 98d913a Compare October 16, 2025 16:24
@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch from 98d913a to 9b04d5e Compare October 16, 2025 16:53
@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch from a5f30e1 to 8bcb1cb Compare October 17, 2025 18:35
@RafaelAPB RafaelAPB added this to the SATP-Hermes v0.0.4-beta milestone Oct 20, 2025
@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch from 8bcb1cb to 76d7408 Compare October 28, 2025 13:18
@LordKubaya
Copy link
Contributor

The yarn lint job is failling

@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch 4 times, most recently from 4451e47 to e73fa43 Compare October 30, 2025 17:27
@RafaelAPB RafaelAPB enabled auto-merge (rebase) October 30, 2025 17:34
auto-merge was automatically disabled October 31, 2025 21:47

Head branch was pushed to by a user without write access

@Tomas-Silva2187 Tomas-Silva2187 force-pushed the REDOnftSupportDev25-08-04 branch from e73fa43 to 88e5d1b Compare October 31, 2025 21:47
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.

feat(satp): add support for NFTs

3 participants