Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Nexus.Sdk.Token.Tests/Helpers/MockTokenServerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public Task<SignableResponse> ConnectAccountToTokensAsync(string accountCode, IE
throw new NotImplementedException();
}

public Task<SignableResponse> ConnectAccountToTokensAsync(string accountCode, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null)
{
throw new NotImplementedException();
}

public Task<AccountResponse> CreateAccountOnAlgorandAsync(string customerCode, string publicKey, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED")
{
throw new NotImplementedException();
Expand All @@ -37,11 +42,21 @@ public Task<SignableResponse> CreateAccountOnAlgorandAsync(string customerCode,
throw new NotImplementedException();
}

public Task<SignableResponse> CreateAccountOnAlgorandAsync(string customerCode, string publicKey, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED")
{
throw new NotImplementedException();
}

public Task<SignableResponse> CreateAccountOnStellarAsync(string customerCode, string publicKey, IEnumerable<string> tokenCodes, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED")
{
throw new NotImplementedException();
}

public Task<SignableResponse> CreateAccountOnStellarAsync(string customerCode, string publicKey, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED")
{
throw new NotImplementedException();
}

public Task<AccountResponse> CreateAccountOnStellarAsync(string customerCode, string publicKey, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED")
{
throw new NotImplementedException();
Expand Down Expand Up @@ -182,6 +197,13 @@ public Task<AccountBalancesResponse> GetAccountBalanceAsync(string accountCode)
throw new NotImplementedException();
}

public Task<PagedResponse<AccountTokenResponse>> GetAccountTokensAsync(IDictionary<string, string>? queryParameters)
{
var accountToken = new AccountTokenResponse("ACC001", "TOKEN001", "ACTIVE", new Dictionary<string, string> { { "VIBANNUMBER", "NL91ABNA0417164300" } });
var pagedResponse = new PagedResponse<AccountTokenResponse>(1, 1, 1, new Dictionary<string, string>(), new[] { accountToken });
return Task.FromResult(pagedResponse);
}

public Task<CustomerResponse> GetCustomer(string customerCode)
{
throw new NotImplementedException();
Expand Down Expand Up @@ -440,6 +462,11 @@ public Task<AccountResponse> CreateVirtualAccount(string customerCode, string ad
throw new NotImplementedException();
}

public Task<AccountResponse> CreateVirtualAccount(string customerCode, string address, bool generateReceiveAddress, string cryptoCode, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null, string? customName = null)
{
throw new NotImplementedException();
}

public Task<DocumentStoreSettingsResponse> GetDocumentStore(string customerIPAddress)
{
throw new NotImplementedException();
Expand Down
35 changes: 35 additions & 0 deletions Nexus.Sdk.Token.Tests/MockTokenServerProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,40 @@ public async Task MockTokenServerProviderTests_UpdateOperationStatusAsync()
Assert.That(tokenOperationResponse.Fees?.NetworkFees?.EstimatedCrypto, Is.EqualTo(100));
});
}

[Test]
public async Task MockTokenServerProviderTests_GetAccountTokensAsync()
{
_services = new ServiceCollection();

var mock = new MockTokenServerProvider();

_services.AddTokenServer(mock);

var provider = _services.BuildServiceProvider();

var tokenServerProvider = provider.GetRequiredService<ITokenServerProvider>();

var queryParameters = new Dictionary<string, string> { { "data_VIBANNUMBER", "NL91ABNA0417164300" } };
var pagedResponse = await tokenServerProvider.GetAccountTokensAsync(queryParameters);

Assert.Multiple(() =>
{
Assert.That(tokenServerProvider, Is.Not.Null);
Assert.That(pagedResponse, Is.Not.Null);
Assert.That(pagedResponse.Records, Is.Not.Null);
Assert.That(pagedResponse.Page, Is.EqualTo(1));
Assert.That(pagedResponse.Total, Is.EqualTo(1));
Assert.That(pagedResponse.TotalPages, Is.EqualTo(1));

var firstRecord = pagedResponse.Records.FirstOrDefault();
Assert.That(firstRecord, Is.Not.Null);
Assert.That(firstRecord!.AccountCode, Is.Not.Null.Or.Empty);
Assert.That(firstRecord.TokenCode, Is.Not.Null.Or.Empty);
Assert.That(firstRecord.Status, Is.Not.Null.Or.Empty);
Assert.That(firstRecord.Data, Is.Not.Null);
Assert.That(firstRecord.Data!["VIBANNUMBER"], Is.EqualTo("NL91ABNA0417164300"));
});
}
}
}
47 changes: 47 additions & 0 deletions Nexus.Sdk.Token/Facades/AccountsFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,33 @@ public async Task<AccountBalancesResponse> GetBalances(string accountCode)
return await _provider.GetAccountBalanceAsync(accountCode);
}

public async Task<PagedResponse<AccountTokenResponse>> GetAccountTokensAsync(string? accountCode = null, string? tokenCode = null, IDictionary<string, string>? dataFilters = null, int page = 1, int limit = 50)
{
var query = new Dictionary<string, string>
{
{ "page", page.ToString() },
{ "limit", limit.ToString() }
};

if (!string.IsNullOrWhiteSpace(accountCode))
{
query["accountCode"] = accountCode;
}

if (!string.IsNullOrWhiteSpace(tokenCode))
{
query["tokenCode"] = tokenCode;
}

if (dataFilters != null)
{
foreach (var (key, value) in dataFilters)
query[$"data_{key}"] = value;
}

return await _provider.GetAccountTokensAsync(query);
}

public async Task<SignableResponse> Update(string customerCode, string accountCode, UpdateTokenAccountRequest updateRequest, string? customerIPAddress = null)
{
return await _provider.UpdateAccount(customerCode, accountCode, updateRequest, customerIPAddress);
Expand All @@ -35,6 +62,11 @@ public async Task<AccountResponse> CreateVirtualAccount(string customerCode, str
return await _provider.CreateVirtualAccount(customerCode, address, generateReceiveAddress, cryptoCode, allowedTokens, customerIPAddress, customName);
}

public async Task<AccountResponse> CreateVirtualAccount(string customerCode, string address, bool generateReceiveAddress, string cryptoCode, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null, string? customName = null)
{
return await _provider.CreateVirtualAccount(customerCode, address, generateReceiveAddress, cryptoCode, tokensWithData, customerIPAddress, customName);
}

public async Task<AccountResponse> CreateOnStellarAsync(string customerCode, string publicKey, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED")
{
return await _provider.CreateAccountOnStellarAsync(customerCode, publicKey, customerIPAddress, customName, accountType);
Expand All @@ -45,6 +77,11 @@ public async Task<SignableResponse> CreateOnStellarAsync(string customerCode, st
return await _provider.CreateAccountOnStellarAsync(customerCode, publicKey, allowedTokens, customerIPAddress, customName, accountType);
}

public async Task<SignableResponse> CreateOnStellarAsync(string customerCode, string publicKey, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED")
{
return await _provider.CreateAccountOnStellarAsync(customerCode, publicKey, tokensWithData, customerIPAddress, customName, accountType);
}

public async Task<AccountResponse> CreateOnAlgorandAsync(string customerCode, string publicKey, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED")
{
return await _provider.CreateAccountOnAlgorandAsync(customerCode, publicKey, customerIPAddress, customName, accountType);
Expand All @@ -55,6 +92,11 @@ public async Task<SignableResponse> CreateOnAlgorandAsync(string customerCode, s
return await _provider.CreateAccountOnAlgorandAsync(customerCode, publicKey, allowedTokens, customerIPAddress, customName, accountType);
}

public async Task<SignableResponse> CreateOnAlgorandAsync(string customerCode, string publicKey, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED")
{
return await _provider.CreateAccountOnAlgorandAsync(customerCode, publicKey, tokensWithData, customerIPAddress, customName, accountType);
}

public async Task<SignableResponse> ConnectToTokenAsync(string accountCode, string tokenCode, string? customerIPAddress = null)
{
return await _provider.ConnectAccountToTokenAsync(accountCode, tokenCode, customerIPAddress);
Expand All @@ -65,6 +107,11 @@ public async Task<SignableResponse> ConnectToTokensAsync(string accountCode, IEn
return await _provider.ConnectAccountToTokensAsync(accountCode, tokenCodes, customerIPAddress);
}

public async Task<SignableResponse> ConnectToTokensAsync(string accountCode, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null)
{
return await _provider.ConnectAccountToTokensAsync(accountCode, tokensWithData, customerIPAddress);
}

public async Task<NexusResponse> DeleteAccount(string accountCode)
{
return await _provider.DeleteAccount(accountCode);
Expand Down
58 changes: 58 additions & 0 deletions Nexus.Sdk.Token/Facades/Interfaces/IAccountsFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ public interface IAccountsFacade
/// <returns>List of token balances for the account matching the provided code</returns>
public Task<AccountBalancesResponse> GetBalances(string accountCode);

/// <summary>
/// Get a paginated list of account tokens matching the provided search criteria.
/// At least one of accountCode, tokenCode, or dataFilters must be provided.
/// </summary>
/// <param name="accountCode">Optional account code to filter on.</param>
/// <param name="tokenCode">Optional token code to filter on.</param>
/// <param name="dataFilters">Optional data property filters (key=property_name, value=property_value). Keys will be prefixed with data_ when sent to the API.</param>
/// <param name="page">Page number (default 1).</param>
/// <param name="limit">Page size (default 50).</param>
/// <returns>A paged list of account tokens.</returns>
public Task<PagedResponse<AccountTokenResponse>> GetAccountTokensAsync(string? accountCode = null, string? tokenCode = null, IDictionary<string, string>? dataFilters = null, int page = 1, int limit = 50);

/// <summary>
/// Create a virtual account
/// </summary>
Expand All @@ -35,6 +47,19 @@ public interface IAccountsFacade
/// <returns></returns>
Task<AccountResponse> CreateVirtualAccount(string customerCode, string address, bool generateReceiveAddress, string cryptoCode, IEnumerable<string> allowedTokens, string? customerIPAddress = null, string? customName = null);

/// <summary>
/// Create a virtual account with optional per-token metadata
/// </summary>
/// <param name="customerCode">Unique Nexus identifier of the customer.</param>
/// <param name="address">Optionally supply a receive address for the virtual account to receive from blockchain accounts.</param>
/// <param name="generateReceiveAddress">Generate a receive address for the virtual account to receive from blockchain accounts.</param>
/// <param name="cryptoCode">Blockchain to connect this account to.</param>
/// <param name="tokensWithData">Token codes with optional metadata the account will be connected to upon creation</param>
/// <param name="customerIPAddress">Optional IP address of the customer used for tracing their actions</param>
/// <param name="customName">Optional custom name for account</param>
/// <returns></returns>
Task<AccountResponse> CreateVirtualAccount(string customerCode, string address, bool generateReceiveAddress, string cryptoCode, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null, string? customName = null);

/// <summary>
/// Create a new account on the Stellar blockchain
/// </summary>
Expand All @@ -58,6 +83,18 @@ public interface IAccountsFacade
/// <returns>A transaction that needs to be signed using the private key that matches the provided public key</returns>
public Task<SignableResponse> CreateOnStellarAsync(string customerCode, string publicKey, IEnumerable<string> allowedTokens, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED");

/// <summary>
/// Create a new account on the Stellar blockchain, connecting it with tokens and optional per-token metadata
/// </summary>
/// <param name="customerCode">Unique Nexus identifier of the customer.</param>
/// <param name="publicKey">The public key the new Stellar account</param>
/// <param name="tokensWithData">Token codes with optional metadata the account will be connected to upon creation</param>
/// <param name="customerIPAddress">Optional IP address of the customer used for tracing their actions</param>
/// <param name="customName">Optional custom name for account</param>
/// <param name="accountType">Optional type for account (Defaults to a managed account)</param>
/// <returns>A transaction that needs to be signed using the private key that matches the provided public key</returns>
public Task<SignableResponse> CreateOnStellarAsync(string customerCode, string publicKey, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED");

/// <summary>
/// Create a new account on the Algorand blockchain
/// </summary>
Expand All @@ -81,6 +118,18 @@ public interface IAccountsFacade
/// <returns>A transaction that needs to be signed using the private key that matches the provided public key</returns>
public Task<SignableResponse> CreateOnAlgorandAsync(string customerCode, string publicKey, IEnumerable<string> allowedTokens, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED");

/// <summary>
/// Create a new account on the Algorand blockchain, connecting it with tokens and optional per-token metadata
/// </summary>
/// <param name="customerCode">The code of the customer this account is created for</param>
/// <param name="publicKey">The public key the new Algorand account</param>
/// <param name="tokensWithData">Token codes with optional metadata the account will be connected to upon creation</param>
/// <param name="customerIPAddress">Optional IP address of the customer used for tracing their actions</param>
/// <param name="customName">Optional custom name for account</param>
/// <param name="accountType">Optional type for account (Defaults to a managed account)</param>
/// <returns>A transaction that needs to be signed using the private key that matches the provided public key</returns>
public Task<SignableResponse> CreateOnAlgorandAsync(string customerCode, string publicKey, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null, string? customName = null, string? accountType = "MANAGED");

/// <summary>
/// Connect a token to an account
/// </summary>
Expand All @@ -99,6 +148,15 @@ public interface IAccountsFacade
/// <returns>A transaction that needs to be signed using the private key of the provided account</returns>
public Task<SignableResponse> ConnectToTokensAsync(string accountCode, IEnumerable<string> tokenCodes, string? customerIPAddress = null);

/// <summary>
/// Connect tokens with optional per-token metadata to an account
/// </summary>
/// <param name="accountCode">{crypto}-{publickey} combination of the account. E.g. XLM-GAW6GBLA5U4KCXV4E5SZTVERBF3AUASEPNTN4ZXSXLCROOTJ7KQQW4S7</param>
/// <param name="tokensWithData">Token codes with optional metadata to associate with each token on this account</param>
/// <param name="customerIPAddress">Optional IP address of the customer used for tracing their actions</param>
/// <returns>A transaction that needs to be signed using the private key of the provided account</returns>
public Task<SignableResponse> ConnectToTokensAsync(string accountCode, IEnumerable<TokenCodeWithData> tokensWithData, string? customerIPAddress = null);

/// <summary>
/// Updates account properties
/// </summary>
Expand Down
Loading
Loading