-
Notifications
You must be signed in to change notification settings - Fork 7
Checkout/transak primary sale #317
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
Open
caballoninja
wants to merge
15
commits into
master
Choose a base branch
from
checkout/transakPrimarySale
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+623
−32
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
da71fea
First commit
caballoninja e8447c0
erc1155
caballoninja 92d4faf
Encoder and compressor tests
caballoninja 97d6261
TransakCheckout fixes
caballoninja 3034f3d
Test Fix
caballoninja 85fc707
Fixed test contract data
caballoninja 7bbd346
more fixes
caballoninja f491914
more fixes to test
caballoninja eedba10
fixed test and adjusted methods for geting link with collectible order
caballoninja 2d7e7c0
removed some comments
caballoninja e40e8c7
Merge branch 'v2' into checkout/transakPrimarySale
caballoninja aa5a8a8
Fixed data parameter for erc1155
caballoninja 4185fac
Merge branch 'master' into checkout/transakPrimarySale
caballoninja 1e5b8c1
update erc1155 test to match new struct fields
caballoninja f426811
Merge branch 'master' into checkout/transakPrimarySale
caballoninja File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
4 changes: 2 additions & 2 deletions
4
Plugins/SequencePlugin/Content/Boilerplates/Pawn_SequenceDemo.uasset
Git LFS file not shown
35 changes: 35 additions & 0 deletions
35
Plugins/SequencePlugin/Source/SequencePlugin/Private/Checkout/Transak/CallDataCompressor.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| #include "Checkout/Transak/CallDataCompressor.h" | ||
| #include "Misc/Base64.h" | ||
| #include "Misc/Compression.h" | ||
| #include "GenericPlatform/GenericPlatformHttp.h" | ||
|
|
||
| FString FCallDataCompressor::Compress(const FString& In) | ||
| { | ||
| FTCHARToUTF8 Converter(*In); | ||
| const uint8* UncompressedData = reinterpret_cast<const uint8*>(Converter.Get()); | ||
| int32 UncompressedSize = Converter.Length(); | ||
|
|
||
| int32 CompressedBufferSize = FCompression::CompressMemoryBound(NAME_Zlib, UncompressedSize); | ||
| TArray<uint8> CompressedData; | ||
| CompressedData.SetNumUninitialized(CompressedBufferSize); | ||
|
|
||
| int32 CompressedSize = CompressedBufferSize; | ||
| bool bSuccess = FCompression::CompressMemory( | ||
| NAME_Zlib, | ||
| CompressedData.GetData(), | ||
| CompressedSize, | ||
| UncompressedData, | ||
| UncompressedSize, | ||
| COMPRESS_Default | ||
| ); | ||
|
|
||
| if (!bSuccess) | ||
| { | ||
| UE_LOG(LogTemp, Error, TEXT("CallData compression failed")); | ||
| return TEXT("CompressionError"); | ||
| } | ||
|
|
||
| CompressedData.SetNum(CompressedSize); | ||
| FString Base64 = FBase64::Encode(CompressedData); | ||
| return FGenericPlatformHttp::UrlEncode(Base64); | ||
| } |
225 changes: 225 additions & 0 deletions
225
Plugins/SequencePlugin/Source/SequencePlugin/Private/Checkout/Transak/TransakCheckout.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| #include "Checkout/Transak/TransakCheckout.h" | ||
| #include "Misc/DateTime.h" | ||
| #include "Misc/Guid.h" | ||
| #include "Misc/Base64.h" | ||
| #include "Containers/StringConv.h" | ||
| #include "Misc/DefaultValueHelper.h" | ||
| #include "Engine/Engine.h" | ||
| #include "Checkout/Transak/CallDataCompressor.h" | ||
| #include "Checkout/Transak/TransakNFTDataEncoder.h" | ||
| #include "Checkout/Structs/TransactionStep.h" | ||
| #include "Util/SequenceSupport.h" | ||
| void UTransakCheckout::Initialize(const FString& InWalletAddress, ENetwork InNetwork) | ||
| { | ||
| WalletAddress = InWalletAddress; | ||
| Network = InNetwork; | ||
| } | ||
| FString UTransakCheckout::GetNFTCheckoutLink(const FTransakNFTData& Item, const FString& CallData, const FTransakContractId& ContractId) | ||
| { | ||
| const FString NetworkString = UEnum::GetValueAsString(Network); | ||
|
|
||
| if (ContractId.Chain != NetworkString) | ||
| { | ||
| FString ChainMismatchError = FString::Printf(TEXT("Chain mismatch: ContractId.Chain = %s, Network = %s"), *ContractId.Chain, *NetworkString); | ||
| return ChainMismatchError; | ||
| } | ||
|
|
||
| const FString TransakCallData = FCallDataCompressor::Compress(CallData); | ||
|
|
||
| const FString TransakNftDataEncoded = FTransakNFTDataEncoder::Encode(Item); | ||
|
|
||
| const FString BaseURL = TEXT("https://global.transak.com/"); | ||
|
|
||
| const FString TContractId = ContractId.Id; | ||
| const FString TransakCryptocurrencyCode = ContractId.PriceTokenSymbol; | ||
| const int64 EstimatedGasLimit = 500000; | ||
|
|
||
| if (WalletAddress.IsEmpty()) | ||
| { | ||
| return TEXT("Error: Transak Checkout object wallet address is missing"); | ||
| } | ||
|
|
||
|
|
||
| const int64 Timestamp = FDateTime::UtcNow().ToUnixTimestamp(); | ||
|
|
||
| const FString PartnerOrderId = FString::Printf(TEXT("%s-%lld"), *WalletAddress, Timestamp); | ||
|
|
||
| FString address = *WalletAddress; | ||
|
|
||
| FString URL = | ||
| BaseURL + TEXT("?apiKey=5911d9ec-46b5-48fa-a755-d59a715ff0cf") | ||
| + TEXT("&isNFT=true") | ||
| + TEXT("&calldata=") + TransakCallData | ||
| + TEXT("&contractId=") + TContractId | ||
| + TEXT("&cryptoCurrencyCode=") + TransakCryptocurrencyCode | ||
| + TEXT("&estimatedGasLimit=") + FString::Printf(TEXT("%lld"), EstimatedGasLimit) | ||
| + TEXT("&nftData=") + TransakNftDataEncoded | ||
| + TEXT("&walletAddress=") + address | ||
| + TEXT("&disableWalletAddressForm=true") | ||
| + TEXT("&partnerOrderId=") + PartnerOrderId; | ||
|
|
||
| UE_LOG(LogTemp, Warning, TEXT("Final URL: %s"), *URL); | ||
| return URL; | ||
| } | ||
|
|
||
|
|
||
| FString UTransakCheckout::BuildNFTCheckoutLinkFromERC1155(UERC1155SaleContract* SaleContract, const FTransakNFTData TransakNFTData, const FTransakContractId& ContractId, const TArray<FString>& Proof, const FString& Data) | ||
| { | ||
|
|
||
| if (!SaleContract) | ||
| { | ||
| return TEXT("Error: Invalid SaleContract"); | ||
| } | ||
|
|
||
| if (TransakNFTData.Quantity <= 0) | ||
| { | ||
| return TEXT("Error: Invalid quantity"); | ||
| } | ||
|
|
||
| TArray<int32> TokenIds; | ||
| TArray<int32> Amounts; | ||
|
|
||
| for (const FString& TokenIDString : TransakNFTData.TokenID) | ||
| { | ||
| int32 ParsedTokenId = FCString::Atoi(*TokenIDString); | ||
| TokenIds.Add(ParsedTokenId); | ||
| Amounts.Add(TransakNFTData.Quantity); | ||
| } | ||
|
|
||
|
|
||
| FRawTransaction result = SaleContract->MakePurchaseTransaction( | ||
| TransakContractAddresses[Network], | ||
| TokenIds, | ||
| Amounts, | ||
| Proof, | ||
| Data | ||
| ); | ||
|
|
||
| FString callData = result.data; | ||
|
|
||
|
|
||
| return GetNFTCheckoutLink(TransakNFTData, callData, ContractId); | ||
| } | ||
| void UTransakCheckout::BuildNFTCheckoutLinkFromCollectibleOrder(FSeqCollectibleOrder order, int64 quantity, ENFTType type, FAdditionalFee AdditionalFee, FOnTransakCheckoutGenerated OnSuccessCallback, FOnTransakCheckoutFailure OnFailureCallback) | ||
| { | ||
| if (quantity <= 0) | ||
| { | ||
| UE_LOG(LogTemp, Error, TEXT("Invalid quantity")); | ||
| return; | ||
| } | ||
|
|
||
| double PriceAmount = FCString::Atod(*order.Order.PriceAmount); | ||
| double PriceDecimals = static_cast<double>(order.Order.PriceDecimals); | ||
| float NormalizedPrice = static_cast<float>(PriceAmount / FMath::Pow(10.0, PriceDecimals)); | ||
| TArray<float> Prices = { NormalizedPrice }; | ||
|
|
||
| TArray<FString> TokenIds = { FString::FromInt(order.TokenMetadata.tokenId) }; | ||
|
|
||
| FTransakNFTData NFTData( | ||
| order.TokenMetadata.image, | ||
| order.TokenMetadata.name, | ||
| order.TokenMetadata.contractAddress, | ||
| TokenIds, | ||
| Prices, | ||
| quantity, | ||
| ENFTType::ERC721 | ||
| ); | ||
|
|
||
|
|
||
| FTransakContractId TransakContractID = GetTransakContractIdFromCollectibleOrder(order); | ||
|
|
||
| if (TransakContractID.Id.IsEmpty()) | ||
| { | ||
| UE_LOG(LogTemp, Error, TEXT("TransakContractID is empty. Aborting.")); | ||
| return; | ||
| } | ||
|
|
||
| USequenceCheckout* Checkout = NewObject< USequenceCheckout>(); | ||
|
|
||
| Checkout->GenerateBuyTransaction( | ||
| order.Order.ChainId, | ||
| WalletAddress, | ||
| order.Order, | ||
| quantity, | ||
| AdditionalFee, | ||
| EWalletKind::Sequence, | ||
|
|
||
| [this, NFTData, TransakContractID, OnSuccessCallback](const FGenerateTransactionResponse& Response) | ||
| { | ||
| const FString URL = GetNFTCheckoutLink(NFTData, *Response.Steps[0].ExtractBuyStep(Response.Steps).Data, TransakContractID); | ||
| OnSuccessCallback.ExecuteIfBound(URL); | ||
BellringerQuinn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
|
|
||
| [OnFailureCallback](const FSequenceError& Error) | ||
| { | ||
| OnFailureCallback.ExecuteIfBound(TEXT("Error: Failed to generate buy transaction")); | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| FTransakContractId UTransakCheckout::GetTransakContractIdFromCollectibleOrder(FSeqCollectibleOrder order) | ||
| { | ||
| FTransakContractId TransakContractID; | ||
|
|
||
| if (order.Order.ChainId == 137) | ||
| { | ||
| switch (order.Order.Marketplace) | ||
| { | ||
| case EMarketplaceKind::SEQUENCE_MARKETPLACE_V2: | ||
|
|
||
| TransakContractID.Id = TEXT("67ac543448035690a20ac131"); | ||
| TransakContractID.ContractAddress = TEXT("0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712"); | ||
| TransakContractID.Chain = TransakContractID.Chain = UEnum::GetValueAsString(ENetwork::PolygonChain); | ||
| TransakContractID.PriceTokenSymbol = TEXT("POL"); | ||
|
|
||
| break; | ||
|
|
||
| default: | ||
|
|
||
| TransakContractID.Id = TEXT("6675a6d0f597abb8f3e2e9c2"); | ||
| TransakContractID.ContractAddress = TEXT("0xc2c862322e9c97d6244a3506655da95f05246fd8"); | ||
| TransakContractID.Chain = TransakContractID.Chain = UEnum::GetValueAsString(ENetwork::PolygonChain); | ||
| TransakContractID.PriceTokenSymbol = TEXT("MATIC"); | ||
|
|
||
| break; | ||
| } | ||
| } | ||
| else if (order.Order.ChainId == 42161) | ||
| { | ||
|
|
||
| if (order.Order.Marketplace == EMarketplaceKind::SEQUENCE_MARKETPLACE_V2) | ||
| { | ||
| TransakContractID.Id = TEXT("66c5a2cf2fb1688e11fcb167"); | ||
| TransakContractID.ContractAddress = TEXT("0xB537a160472183f2150d42EB1c3DD6684A55f74c"); | ||
| TransakContractID.Chain = UEnum::GetValueAsString(ENetwork::ArbitrumOne); | ||
| TransakContractID.PriceTokenSymbol = TEXT("USDC"); | ||
| } | ||
| else if (order.Order.Marketplace == EMarketplaceKind::SEQUENCE_MARKETPLACE_V1) | ||
| { | ||
| TransakContractID.Id = TEXT("66c5a2d8c00223b9cc6edfdc"); | ||
| TransakContractID.ContractAddress = TEXT("0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712"); | ||
| TransakContractID.Chain = UEnum::GetValueAsString(ENetwork::ArbitrumOne); | ||
| TransakContractID.PriceTokenSymbol = TEXT("USDC"); | ||
| } | ||
| } | ||
|
|
||
| return TransakContractID; | ||
| } | ||
|
|
||
|
|
||
| const TMap<ENetwork, FString> UTransakCheckout::TransakContractAddresses = { | ||
| {ENetwork::Ethereum, TEXT("0xab88cd272863b197b48762ea283f24a13f6586dd")}, | ||
| {ENetwork::Sepolia, TEXT("0xD84aC4716A082B1F7eCDe9301aA91A7c4B62ECd7")}, | ||
| {ENetwork::PolygonChain, TEXT("0x4A598B7eC77b1562AD0dF7dc64a162695cE4c78A")}, | ||
| {ENetwork::PolygonAmoy, TEXT("0xCB9bD5aCD627e8FcCf9EB8d4ba72AEb1Cd8Ff5EF")}, | ||
| {ENetwork::BNBSmartChain, TEXT("0x4A598B7eC77b1562AD0dF7dc64a162695cE4c78A")}, | ||
| {ENetwork::BNBSmartChainTestnet, TEXT("0x0E9539455944BE8a307bc43B0a046613a1aD6732")}, | ||
| {ENetwork::ArbitrumOne, TEXT("0x4A598B7eC77b1562AD0dF7dc64a162695cE4c78A")}, | ||
| {ENetwork::ArbitrumSepolia, TEXT("0x489F56e3144FF03A887305839bBCD20FF767d3d1")}, | ||
| {ENetwork::Optimism, TEXT("0x4A598B7eC77b1562AD0dF7dc64a162695cE4c78A")}, | ||
| {ENetwork::OptimismSepolia, TEXT("0xCB9bD5aCD627e8FcCf9EB8d4ba72AEb1Cd8Ff5EF")}, | ||
| {ENetwork::Immutable, TEXT("0x8b83dE7B20059864C479640CC33426935DC5F85b")}, | ||
| {ENetwork::ImmutableTestnet, TEXT("0x489F56e3144FF03A887305839bBCD20FF767d3d1")}, | ||
| {ENetwork::Base, TEXT("0x8b83dE7B20059864C479640CC33426935DC5F85b")}, | ||
| {ENetwork::BaseSepolia, TEXT("0xCB9bD5aCD627e8FcCf9EB8d4ba72AEb1Cd8Ff5EF")} | ||
| }; | ||
58 changes: 58 additions & 0 deletions
58
...s/SequencePlugin/Source/SequencePlugin/Private/Checkout/Transak/TransakNFTDataEncoder.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| #include "Checkout/Transak/TransakNFTDataEncoder.h" | ||
| #include "Serialization/JsonWriter.h" | ||
| #include "Policies/CondensedJsonPrintPolicy.h" | ||
| #include "Serialization/JsonSerializer.h" | ||
| #include "Dom/JsonObject.h" | ||
| #include "Dom/JsonValue.h" | ||
| #include "Misc/Base64.h" | ||
| #include "GenericPlatform/GenericPlatformHttp.h" | ||
|
|
||
| FString FTransakNFTDataEncoder::Encode(const FTransakNFTData& NftData) | ||
| { | ||
|
|
||
| TSharedRef<FJsonObject> JsonObject = MakeShared<FJsonObject>(); | ||
| JsonObject->SetStringField(TEXT("imageURL"), NftData.ImageURL); | ||
| JsonObject->SetStringField(TEXT("nftName"), NftData.Name); | ||
| JsonObject->SetStringField(TEXT("collectionAddress"), NftData.CollectionAddress); | ||
|
|
||
| TArray<TSharedPtr<FJsonValue>> TokenIDArray; | ||
| for (const FString& TokenID : NftData.TokenID) | ||
| { | ||
| TokenIDArray.Add(MakeShared<FJsonValueString>(TokenID)); | ||
| } | ||
| JsonObject->SetArrayField(TEXT("tokenID"), TokenIDArray); | ||
|
|
||
| TArray<TSharedPtr<FJsonValue>> PriceArray; | ||
| for (double Price : NftData.Price) | ||
| { | ||
| double RoundedPrice = FMath::RoundToDouble(Price * 100000.0) / 100000.0; | ||
| PriceArray.Add(MakeShared<FJsonValueNumber>(RoundedPrice)); | ||
| } | ||
| JsonObject->SetArrayField(TEXT("price"), PriceArray); | ||
|
|
||
| JsonObject->SetNumberField(TEXT("quantity"), static_cast<double>(NftData.Quantity)); | ||
|
|
||
| FString EnumStr = UEnum::GetValueAsString(NftData.Type).Replace(TEXT("ENFTType::"), TEXT("")); | ||
| JsonObject->SetStringField(TEXT("nftType"), EnumStr); | ||
|
|
||
| TArray<TSharedPtr<FJsonValue>> JsonArray; | ||
| JsonArray.Add(MakeShared<FJsonValueObject>(JsonObject)); | ||
|
|
||
| FString JsonString; | ||
| TSharedRef<TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>> Writer =TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&JsonString); | ||
|
|
||
| if (!FJsonSerializer::Serialize(JsonArray, Writer)) | ||
| { | ||
| return TEXT(""); | ||
| } | ||
|
|
||
| FString Base64Encoded = FBase64::Encode(JsonString); | ||
|
|
||
| UE_LOG(LogTemp, Log, TEXT("Base64 Encoded (with padding): %s"), *Base64Encoded); | ||
|
|
||
| FString UrlEncoded = FGenericPlatformHttp::UrlEncode(Base64Encoded); | ||
|
|
||
| UE_LOG(LogTemp, Log, TEXT("URL Encoded: %s"), *UrlEncoded); | ||
|
|
||
| return UrlEncoded; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
Plugins/SequencePlugin/Source/SequencePlugin/Public/Checkout/Transak/CallDataCompressor.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| #pragma once | ||
|
|
||
| #include "CoreMinimal.h" | ||
|
|
||
| /** | ||
| * Utility class for compressing call data using Deflate + Base64 + URI encoding. | ||
| */ | ||
| class SEQUENCEPLUGIN_API FCallDataCompressor | ||
| { | ||
| public: | ||
| static FString Compress(const FString& In); | ||
| }; |
22 changes: 22 additions & 0 deletions
22
.../SequencePlugin/Source/SequencePlugin/Public/Checkout/Transak/Structs/TransakContractId.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #pragma once | ||
|
|
||
| #include "CoreMinimal.h" | ||
| #include "TransakContractId.generated.h" | ||
|
|
||
| USTRUCT(BlueprintType) | ||
| struct SEQUENCEPLUGIN_API FTransakContractId | ||
| { | ||
| GENERATED_BODY() | ||
|
|
||
| UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Transak") | ||
| FString Id; | ||
|
|
||
| UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Transak") | ||
| FString ContractAddress; | ||
|
|
||
| UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Transak") | ||
| FString Chain; | ||
|
|
||
| UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Transak") | ||
| FString PriceTokenSymbol; | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a random thought - what do you think about making a wrapper function for converting strings to ints with a more readable name in a future PR? I know that this is a built-in function that should be rather well-known to C/C++ devs, but maybe would be worthwhile anyways as we wouldn't have to do as much mental gymnastics? Or is that just unnecessary overhead? I'm not really sure... What do you think?