-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
338 additions
and
8 deletions.
There are no files selected for viewing
This file contains 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,71 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using Cysharp.Threading.Tasks; | ||
using Immutable.Orderbook.Api; | ||
using Immutable.Orderbook.Client; | ||
using Immutable.Orderbook.Model; | ||
using Immutable.Passport; | ||
using Immutable.Passport.Model; | ||
using Newtonsoft.Json; | ||
using UnityEngine; | ||
|
||
namespace HyperCasual.Runner | ||
{ | ||
public class CancelListingUseCase | ||
{ | ||
private static readonly Lazy<CancelListingUseCase> s_Instance = new(() => new CancelListingUseCase()); | ||
|
||
private readonly OrderbookApi m_OrderbookApi = new(new Configuration { BasePath = Config.BASE_URL }); | ||
|
||
private CancelListingUseCase() { } | ||
|
||
public static CancelListingUseCase Instance => s_Instance.Value; | ||
|
||
/// <summary> | ||
/// Cancels the specified listing. | ||
/// </summary> | ||
/// <param name="listingId">The unique identifier of the listing to cancel.</param> | ||
public async UniTask CancelListing(string listingId) | ||
{ | ||
try | ||
{ | ||
var request = new CancelOrdersOnChainRequest( | ||
accountAddress: SaveManager.Instance.WalletAddress, orderIds: new List<string> { listingId }); | ||
|
||
var response = await m_OrderbookApi.CancelOrdersOnChainAsync(request); | ||
var transactionAction = response?.CancellationAction.PopulatedTransactions; | ||
|
||
if (transactionAction?.To == null) | ||
throw new Exception("Failed to cancel listing."); | ||
|
||
var txResponse = await Passport.Instance.ZkEvmSendTransactionWithConfirmation( | ||
new TransactionRequest | ||
{ | ||
to = transactionAction.To, | ||
data = transactionAction.Data, | ||
value = "0" | ||
}); | ||
|
||
if (txResponse.status != "1") | ||
throw new Exception("Failed to cancel listing."); | ||
} | ||
catch (ApiException e) | ||
{ | ||
HandleApiException(e); | ||
throw; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Handles API exceptions by logging relevant details. | ||
/// </summary> | ||
private static void HandleApiException(ApiException e) | ||
{ | ||
Debug.LogError($"API Error: {e.Message} (Status: {e.ErrorCode})"); | ||
Debug.LogError(e.ErrorContent); | ||
Debug.LogError(e.StackTrace); | ||
var errorModel = JsonConvert.DeserializeObject<ErrorModel>($"{e.ErrorContent}"); | ||
if (errorModel != null) throw new Exception(errorModel.message); | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains 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,170 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Cysharp.Threading.Tasks; | ||
using Immutable.Orderbook.Api; | ||
using Immutable.Orderbook.Client; | ||
using Immutable.Orderbook.Model; | ||
using Immutable.Passport; | ||
using Immutable.Passport.Model; | ||
using Newtonsoft.Json; | ||
using UnityEngine; | ||
using ERC1155Item = Immutable.Orderbook.Model.ERC1155Item; | ||
using ERC20Item = Immutable.Orderbook.Model.ERC20Item; | ||
using ERC721Item = Immutable.Orderbook.Model.ERC721Item; | ||
|
||
namespace HyperCasual.Runner | ||
{ | ||
public class CreateOrderUseCase | ||
{ | ||
private static readonly Lazy<CreateOrderUseCase> s_Instance = new(() => new CreateOrderUseCase()); | ||
|
||
private readonly OrderbookApi m_OrderbookApi = new(new Configuration { BasePath = Config.BASE_URL }); | ||
|
||
private CreateOrderUseCase() { } | ||
|
||
public static CreateOrderUseCase Instance => s_Instance.Value; | ||
|
||
/// <summary> | ||
/// Creates a new listing for the specified NFT. | ||
/// </summary> | ||
/// <param name="contractAddress">The address of the NFT's contract.</param> | ||
/// <param name="contractType">The type of the contract (e.g., "ERC721" or "ERC1155").</param> | ||
/// <param name="tokenId">The ID of the NFT.</param> | ||
/// <param name="price"> | ||
/// The sale price of the NFT, represented as a string amount in IMR (scaled by 10^18). | ||
/// </param> | ||
/// <param name="amountToSell"> | ||
/// The quantity of the NFT to sell. "1" for ERC721 tokens and a higher number for ERC1155 tokens. | ||
/// </param> | ||
/// <param name="buyContractAddress"> | ||
/// The contract address of the token used to purchase the NFT. | ||
/// </param> | ||
/// <returns> | ||
/// A <see cref="UniTask{String}"/> that returns the listing ID if the sale is successfully created. | ||
/// </returns> | ||
public async UniTask<string> CreateListing( | ||
string contractAddress, string contractType, string tokenId, | ||
string price, string amountToSell, string buyContractAddress) | ||
{ | ||
try | ||
{ | ||
if (contractType == "ERC721" && amountToSell != "1") | ||
{ | ||
throw new ArgumentException("Invalid arguments: 'amountToSell' must be '1' when listing an ERC721."); | ||
} | ||
|
||
var listingData = await PrepareListing(contractAddress, contractType, tokenId, price, amountToSell, buyContractAddress); | ||
|
||
await SignAndSubmitApproval(listingData); | ||
|
||
var signature = await SignListing(listingData); | ||
|
||
var listingId = await ListAsset(signature, listingData); | ||
|
||
return listingId; | ||
} | ||
catch (ApiException e) | ||
{ | ||
HandleApiException(e); | ||
throw; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Prepares a listing for the specified NFT and purchase details. | ||
/// </summary> | ||
private async UniTask<PrepareListing200Response> PrepareListing( | ||
string contractAddress, string contractType, string tokenId, | ||
string price, string amountToSell, string buyContractAddress) | ||
{ | ||
var sellRequest = CreateSellRequest(contractType, contractAddress, tokenId, amountToSell); | ||
var buyRequest = new ERC20Item(price, buyContractAddress); | ||
|
||
return await m_OrderbookApi.PrepareListingAsync(new PrepareListingRequest( | ||
makerAddress: SaveManager.Instance.WalletAddress, | ||
sell: sellRequest, | ||
buy: new PrepareListingRequestBuy(buyRequest) | ||
)); | ||
} | ||
|
||
/// <summary> | ||
/// Creates the appropriate sell request based on the contract type. | ||
/// </summary> | ||
private static PrepareListingRequestSell CreateSellRequest( | ||
string contractType, string contractAddress, string tokenId, string amountToSell) | ||
{ | ||
return contractType.ToUpper() switch | ||
{ | ||
"ERC1155" => new PrepareListingRequestSell(new ERC1155Item(amountToSell, contractAddress, tokenId)), | ||
"ERC721" => new PrepareListingRequestSell(new ERC721Item(contractAddress, tokenId)), | ||
_ => throw new Exception($"Unsupported contract type: {contractType}") | ||
}; | ||
} | ||
|
||
/// <summary> | ||
/// Signs and submits approval if required by the listing. | ||
/// </summary> | ||
private async UniTask SignAndSubmitApproval(PrepareListing200Response listingData) | ||
{ | ||
var transactionAction = listingData.Actions | ||
.FirstOrDefault(action => action.ActualInstance is TransactionAction)? | ||
.GetTransactionAction(); | ||
|
||
if (transactionAction == null) return; | ||
|
||
var response = await Passport.Instance.ZkEvmSendTransactionWithConfirmation( | ||
new TransactionRequest | ||
{ | ||
to = transactionAction.PopulatedTransactions.To, | ||
data = transactionAction.PopulatedTransactions.Data, | ||
value = "0" | ||
}); | ||
|
||
if (response.status != "1") | ||
throw new Exception("Failed to sign and submit approval."); | ||
} | ||
|
||
/// <summary> | ||
/// Signs the listing with the user's wallet. | ||
/// </summary> | ||
private async UniTask<string> SignListing(PrepareListing200Response listingData) | ||
{ | ||
var signableAction = listingData.Actions | ||
.FirstOrDefault(action => action.ActualInstance is SignableAction)? | ||
.GetSignableAction(); | ||
|
||
if (signableAction == null) | ||
throw new Exception("No valid listing to sign."); | ||
|
||
var messageJson = JsonConvert.SerializeObject(signableAction.Message, Formatting.Indented); | ||
return await Passport.Instance.ZkEvmSignTypedDataV4(messageJson); | ||
} | ||
|
||
/// <summary> | ||
/// Finalises the listing and returns the listing ID. | ||
/// </summary> | ||
private async UniTask<string> ListAsset(string signature, PrepareListing200Response listingData) | ||
{ | ||
var response = await m_OrderbookApi.CreateListingAsync(new CreateListingRequest( | ||
new List<FeeValue>(), | ||
listingData.OrderComponents, | ||
listingData.OrderHash, | ||
signature | ||
)); | ||
return response.Result.Id; | ||
} | ||
|
||
/// <summary> | ||
/// Handles API exceptions by logging relevant details. | ||
/// </summary> | ||
private static void HandleApiException(ApiException e) | ||
{ | ||
Debug.LogError($"API Error: {e.Message} (Status: {e.ErrorCode})"); | ||
Debug.LogError(e.ErrorContent); | ||
Debug.LogError(e.StackTrace); | ||
var errorModel = JsonConvert.DeserializeObject<ErrorModel>($"{e.ErrorContent}"); | ||
if (errorModel != null) throw new Exception(errorModel.message); | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains 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,73 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using Cysharp.Threading.Tasks; | ||
using Immutable.Orderbook.Api; | ||
using Immutable.Orderbook.Client; | ||
using Immutable.Orderbook.Model; | ||
using Immutable.Passport; | ||
using Immutable.Passport.Model; | ||
using Newtonsoft.Json; | ||
using UnityEngine; | ||
|
||
namespace HyperCasual.Runner | ||
{ | ||
public class FulfillOrderUseCase | ||
{ | ||
private static readonly Lazy<FulfillOrderUseCase> s_Instance = new(() => new FulfillOrderUseCase()); | ||
|
||
private readonly OrderbookApi m_OrderbookApi = new(new Configuration { BasePath = Config.BASE_URL }); | ||
|
||
private FulfillOrderUseCase() { } | ||
|
||
public static FulfillOrderUseCase Instance => s_Instance.Value; | ||
|
||
/// <summary> | ||
/// Executes an order by fulfilling a listing and optionally confirming its status. | ||
/// </summary> | ||
/// <param name="listingId">The unique identifier of the listing to fulfill.</param> | ||
/// <param name="fees">The taker fees</param> | ||
public async UniTask ExecuteOrder(string listingId, List<FulfillOrderRequestTakerFeesInner> fees) | ||
{ | ||
try | ||
{ | ||
var request = new FulfillOrderRequest( | ||
takerAddress: SaveManager.Instance.WalletAddress, | ||
listingId: listingId, | ||
takerFees: fees); | ||
|
||
var createListingResponse = await m_OrderbookApi.FulfillOrderAsync(request); | ||
|
||
if (createListingResponse.Actions.Count > 0) | ||
{ | ||
foreach (var transaction in createListingResponse.Actions) | ||
{ | ||
var transactionHash = await Passport.Instance.ZkEvmSendTransaction(new TransactionRequest | ||
{ | ||
to = transaction.PopulatedTransactions.To, | ||
data = transaction.PopulatedTransactions.Data, | ||
value = "0" | ||
}); | ||
Debug.Log($"Transaction hash: {transactionHash}"); | ||
} | ||
} | ||
} | ||
catch (ApiException e) | ||
{ | ||
HandleApiException(e); | ||
throw; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Handles API exceptions by logging relevant details. | ||
/// </summary> | ||
private static void HandleApiException(ApiException e) | ||
{ | ||
Debug.LogError($"API Error: {e.Message} (Status: {e.ErrorCode})"); | ||
Debug.LogError(e.ErrorContent); | ||
Debug.LogError(e.StackTrace); | ||
var errorModel = JsonConvert.DeserializeObject<ErrorModel>($"{e.ErrorContent}"); | ||
if (errorModel != null) throw new Exception(errorModel.message); | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains 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
This file contains 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
Oops, something went wrong.