Skip to content

Commit

Permalink
Savepoint
Browse files Browse the repository at this point in the history
  • Loading branch information
skibitsky committed Oct 2, 2024
1 parent c18d17f commit 3745db6
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 51 deletions.
70 changes: 28 additions & 42 deletions .idea/.idea.Reown/.idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion src/Reown.Sign/Runtime/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,16 @@ await Task.WhenAll(
OptionalNamespaces = proposal.OptionalNamespaces
});

await Client.Auth.PendingRequests.Set(authId, new AuthPendingRequest
{
Id = authId,
Requester = participant,
PairingTopic = pairingData.Topic,
PayloadParams = request.Payload,
Expiry = request.ExpiryTimestamp
});
Client.CoreClient.Expirer.Set(authId, request.ExpiryTimestamp);


return new AuthenticateData(pairingData.Uri, approvalTask.Task);

Expand Down Expand Up @@ -1195,7 +1205,7 @@ public async Task<SessionStruct> ApproveSessionAuthenticate(long requestId, Caca

if (ReCapUtils.TryGetRecapFromResources(cacao.Payload.Resources, out var recap))
{
var methodsFromRecap = ReCapUtils.GetMethodsFromRecap(recap);
var methodsFromRecap = ReCapUtils.GetActionsFromRecap(recap);
var chainsFromRecap = ReCapUtils.GetChainsFromRecap(recap);

approvedMethods.UnionWith(methodsFromRecap);
Expand Down
4 changes: 3 additions & 1 deletion src/Reown.Sign/Runtime/Internals/EngineHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ async Task IEnginePrivate.OnAuthenticateRequest(string topic, JsonRpcRequest<Ses
VerifyContext = verifyContext
};

@params.Payload.RequestId = id;

await PrivateThis.SetAuthRequest(id, request);

SessionAuthenticateRequest?.Invoke(this, @params);
Expand Down Expand Up @@ -396,7 +398,7 @@ await Task.WhenAll(

if (ReCapUtils.TryGetRecapFromResources(cacao.Payload.Resources, out var recapStr))
{
var methodsFromRecap = ReCapUtils.GetMethodsFromRecap(recapStr);
var methodsFromRecap = ReCapUtils.GetActionsFromRecap(recapStr);
var chainsFromRecap = ReCapUtils.GetChainsFromRecap(recapStr);
approvedMethods.AddRange(methodsFromRecap);
approvedChains.AddRange(chainsFromRecap);
Expand Down
39 changes: 37 additions & 2 deletions src/Reown.Sign/Runtime/Models/AuthPayloadParams.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Reown.Sign.Utils;

namespace Reown.Sign.Models
{
Expand Down Expand Up @@ -35,7 +38,7 @@ public class AuthPayloadParams
public string? Statement;

[JsonProperty("requestId", NullValueHandling = NullValueHandling.Ignore)]
public string? RequestId;
public long? RequestId;

[JsonProperty("resources", NullValueHandling = NullValueHandling.Ignore)]
public List<string>? Resources;
Expand All @@ -54,7 +57,7 @@ public AuthPayloadParams()
{
}

public AuthPayloadParams(string[] chains, string domain, string nonce, string? aud, string? type, string? nbf, string? exp, string? iat, string? statement, string? requestId, List<string>? resources, string? pairingTopic, string[]? methods, string? version)
public AuthPayloadParams(string[] chains, string domain, string nonce, string? aud, string? type, string? nbf, string? exp, string? iat, string? statement, long? requestId, List<string>? resources, string? pairingTopic, string[]? methods, string? version)
{
Chains = chains;
Domain = domain;
Expand All @@ -71,5 +74,37 @@ public AuthPayloadParams(string[] chains, string domain, string nonce, string? a
Methods = methods;
Version = version;
}

public void Populate(ICollection<string> supportedCains, ICollection<string> supportedMethods)
{
var approvedChains = supportedCains.Intersect(Chains);
if (!approvedChains.Any())
{
throw new InvalidOperationException("No approved chains found");
}

var statement = Statement ?? string.Empty;

if (!ReCapUtils.TryGetDecodedRecapFromResources(Resources, out var recap))
throw new InvalidOperationException("Recap not found in resources");

var actionsFromRecap = ReCapUtils.GetActionsFromRecap(recap);
var approvedActions = actionsFromRecap.Intersect(supportedMethods).ToArray();
if (approvedActions.Length == 0)
throw new InvalidOperationException($"Supported methods don't satisfy the requested: {string.Join(", ", actionsFromRecap)}. "
+ $"Supported methods: {string.Join(", ", supportedMethods)}");

var updatedResources = Resources ?? new List<string>();
var formattedActions = ReCapUtils.AssignAbilityToActions("request", approvedActions, new Dictionary<string, object>
{
{ "chains", approvedChains }
});

recap.AddResources("eip115", formattedActions);

updatedResources.RemoveAt(updatedResources.Count - 1);
updatedResources.Add(ReCapUtils.EncodeRecap(recap));
Resources = updatedResources;
}
}
}
49 changes: 45 additions & 4 deletions src/Reown.Sign/Runtime/Utils/ReCapUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,47 @@ public class ReCap
{
[JsonProperty("att")]
public Dictionary<string, AttValue> Att;

public void AddResources(string resource, Dictionary<string, Dictionary<string, object>[]> actions)
{
if (string.IsNullOrWhiteSpace(resource))
throw new ArgumentNullException(nameof(resource));

if (actions == null || actions.Count == 0)
throw new ArgumentException("Actions cannot be null or empty.", nameof(actions));

if (Att == null)
throw new InvalidOperationException("ReCap object is not initialized. Att is null.");

if (Att.TryGetValue(resource, out var attValue))
{
foreach (var action in actions)
{
var value = JToken.FromObject(action.Value);
if (attValue.Properties.TryAdd(action.Key, value))
continue;

if (attValue.Properties[action.Key] is JArray arr)
{
arr.Merge(value, new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
});
}
else throw new InvalidOperationException($"Unable to merge action '{action.Key}' into existing resource '{resource}'.");
}
}
else
{
Att[resource] = new AttValue
{
Properties = actions.ToDictionary(
kvp => kvp.Key,
kvp => JToken.FromObject(kvp.Value)
)
};
}
}
}

public class AttValue
Expand Down Expand Up @@ -49,7 +90,7 @@ public static ReCap CreateRecap(string resource, string ability, string[] action

public static Dictionary<string, Dictionary<string, object>[]> AssignAbilityToActions(
string ability,
string[] actions,
IEnumerable<string> actions,
Dictionary<string, object> limits = null)
{
if (string.IsNullOrWhiteSpace(ability))
Expand Down Expand Up @@ -102,13 +143,13 @@ public static bool IsReCap(string resource)
return !string.IsNullOrWhiteSpace(resource) && resource.StartsWith("urn:recap");
}

public static string[] GetMethodsFromRecap(string recapStr)
public static string[] GetActionsFromRecap(string recapStr)
{
var decodedRecap = DecodeRecap(recapStr);
return GetMethodsFromRecap(decodedRecap);
return GetActionsFromRecap(decodedRecap);
}

public static string[] GetMethodsFromRecap(ReCap recap)
public static string[] GetActionsFromRecap(ReCap recap)
{
try
{
Expand Down
26 changes: 26 additions & 0 deletions test/Reown.Sign.Test/AuthenticateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,32 @@ public async Task TestAuthenticate()
var tcs = new TaskCompletionSource<bool>();
wallet.SessionAuthenticateRequest += async (sender, args) =>
{
// Validate that the dapp has both `session_authenticate` & `session_proposal` stored
// And expirer configured
var pendingProposals = dapp.Proposal.Values;
Assert.Single(pendingProposals);
Assert.Contains($"id:{pendingProposals[0].Id}", dapp.CoreClient.Expirer.Keys);
Assert.NotNull(dapp.CoreClient.Expirer.Get(pendingProposals[0].Id));
Assert.True(dapp.CoreClient.Expirer.Get(pendingProposals[0].Id).Expiry > 0);

var pendingAuthRequests = dapp.Auth.PendingRequests.Values;
Assert.Single(pendingAuthRequests);
Assert.Contains($"id:{pendingAuthRequests[0].Id}", dapp.CoreClient.Expirer.Keys);
Assert.NotNull(dapp.CoreClient.Expirer.Get(pendingAuthRequests[0].Id));
Assert.True(dapp.CoreClient.Expirer.Get(pendingAuthRequests[0].Id).Expiry > 0);
Assert.Equal(args.Payload.RequestId, pendingAuthRequests[0].Id);

// Validate that the wallet doesn't have any pending proposals
var pendingProposalsWallet = wallet.Proposal.Values;
Assert.Empty(pendingProposalsWallet);

args.Payload.Populate([
"eip155:1"
], [
"personal_sign",
"eth_sendTransaction"
]);

_testOutputHelper.WriteLine("SessionAuthenticateRequest");
tcs.SetResult(true);
};
Expand Down
2 changes: 1 addition & 1 deletion test/Reown.Sign.Test/RecapTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ public void GetMethodsFromRecap_WithValidRecap_ReturnsExpectedMethods()
const string recapStr =
"urn:recap:eyJhdHQiOnsiZWlwMTU1Ijp7InJlcXVlc3QvZXRoX3NpZ25UeXBlZERhdGFfdjQiOlt7ImNoYWlucyI6WyJlaXAxNTU6MSIsImVpcDE1NToyIiwiZWlwMTU1OjMiXX1dLCJyZXF1ZXN0L3BlcnNvbmFsX3NpZ24iOlt7ImNoYWlucyI6WyJlaXAxNTU6MSIsImVpcDE1NToyIiwiZWlwMTU1OjMiXX1dfX19";

var methods = ReCapUtils.GetMethodsFromRecap(recapStr);
var methods = ReCapUtils.GetActionsFromRecap(recapStr);

var expectedMethods = new[]
{
Expand Down

0 comments on commit 3745db6

Please sign in to comment.