-
Notifications
You must be signed in to change notification settings - Fork 8
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
base: master
Are you sure you want to change the base?
Changes from 11 commits
da71fea
e8447c0
92d4faf
97d6261
3034f3d
85fc707
7bbd346
f491914
eedba10
2d7e7c0
e40e8c7
aa5a8a8
4185fac
1e5b8c1
f426811
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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); | ||
} |
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) | ||
{ | ||
UE_LOG(LogTemp, Error, TEXT("Chain mismatch: ContractId.Chain = %s, Network = %s"), *ContractId.Chain, *NetworkString); | ||
return TEXT("Error: Chain mismatch. Transak Checkout object network must match the TransakContractId chain."); | ||
} | ||
|
||
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<uint8>& Data, const TArray<FString>& Proof) | ||
|
||
{ | ||
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? |
||
TokenIds.Add(ParsedTokenId); | ||
Amounts.Add(TransakNFTData.Quantity); | ||
} | ||
|
||
|
||
FRawTransaction result = SaleContract->MakePurchaseTransaction( | ||
TransakContractAddresses[Network], | ||
TokenIds, | ||
Amounts, | ||
Proof | ||
); | ||
|
||
FString callData = result.data; | ||
|
||
|
||
return GetNFTCheckoutLink(TransakNFTData, callData, ContractId); | ||
} | ||
void UTransakCheckout::BuildNFTCheckoutLinkFromCollectibleOrder(FSeqCollectibleOrder order, int64 quantity, ENFTType type, FAdditionalFee AdditionalFee, FOnTransakCheckoutGenerated OnSuccessCallback) | ||
|
||
{ | ||
if (quantity <= 0) | ||
{ | ||
UE_LOG(LogTemp, Error, TEXT("Invalid quantity")); | ||
return; | ||
} | ||
|
||
TArray<FString> TokenIds = { FString::FromInt(order.TokenMetadata.tokenId) }; | ||
TArray<float> Prices = { static_cast<float>(order.Order.PriceUSD) }; | ||
|
||
|
||
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
|
||
}, | ||
|
||
[OnSuccessCallback](const FSequenceError& Error) | ||
{ | ||
OnSuccessCallback.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"); | ||
} | ||
} | ||
|
||
*TransakContractID.Id, | ||
*TransakContractID.ContractAddress, | ||
*TransakContractID.Chain, | ||
*TransakContractID.PriceTokenSymbol); | ||
BellringerQuinn marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
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")} | ||
}; |
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; | ||
} |
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); | ||
}; |
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; | ||
}; |
Uh oh!
There was an error while loading. Please reload this page.