diff --git a/Assets/ComethSDK/Examples/ContractDefinition.meta b/Assets/ComethSDK/Examples/ContractDefinition.meta new file mode 100644 index 0000000..2774ec6 --- /dev/null +++ b/Assets/ComethSDK/Examples/ContractDefinition.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d03f1a71077a48df9ab3c65adbd3950a +timeCreated: 1699363371 \ No newline at end of file diff --git a/Assets/ComethSDK/Examples/ContractDefinition/ContractDefinition.asmdef b/Assets/ComethSDK/Examples/ContractDefinition/ContractDefinition.asmdef new file mode 100644 index 0000000..e1fc886 --- /dev/null +++ b/Assets/ComethSDK/Examples/ContractDefinition/ContractDefinition.asmdef @@ -0,0 +1,3 @@ +{ + "name": "ContractDefinition" +} \ No newline at end of file diff --git a/Assets/ComethSDK/Examples/ContractDefinition/ContractDefinition.asmdef.meta b/Assets/ComethSDK/Examples/ContractDefinition/ContractDefinition.asmdef.meta new file mode 100644 index 0000000..e764083 --- /dev/null +++ b/Assets/ComethSDK/Examples/ContractDefinition/ContractDefinition.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d20ebb163def4678b3d544ec12ac7c87 +timeCreated: 1699363842 \ No newline at end of file diff --git a/Assets/ComethSDK/Examples/ContractDefinition/CountFunction.cs b/Assets/ComethSDK/Examples/ContractDefinition/CountFunction.cs new file mode 100644 index 0000000..f8a1ea1 --- /dev/null +++ b/Assets/ComethSDK/Examples/ContractDefinition/CountFunction.cs @@ -0,0 +1,7 @@ +namespace ComethSDK.Examples.ContractDefinition +{ + public class CountFunction : CountFunctionBase + { + + } +} \ No newline at end of file diff --git a/Assets/ComethSDK/Examples/ContractDefinition/CountFunction.cs.meta b/Assets/ComethSDK/Examples/ContractDefinition/CountFunction.cs.meta new file mode 100644 index 0000000..9c175d2 --- /dev/null +++ b/Assets/ComethSDK/Examples/ContractDefinition/CountFunction.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 163d72c5908e4ca6b66d6a7b1baf39ed +timeCreated: 1699363511 \ No newline at end of file diff --git a/Assets/ComethSDK/Examples/ContractDefinition/CountFunctionBase.cs b/Assets/ComethSDK/Examples/ContractDefinition/CountFunctionBase.cs new file mode 100644 index 0000000..c6e9140 --- /dev/null +++ b/Assets/ComethSDK/Examples/ContractDefinition/CountFunctionBase.cs @@ -0,0 +1,11 @@ +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace ComethSDK.Examples.ContractDefinition +{ + [Function("count")] + public class CountFunctionBase : FunctionMessage + { + + } +} \ No newline at end of file diff --git a/Assets/ComethSDK/Examples/ContractDefinition/CountFunctionBase.cs.meta b/Assets/ComethSDK/Examples/ContractDefinition/CountFunctionBase.cs.meta new file mode 100644 index 0000000..aff78e6 --- /dev/null +++ b/Assets/ComethSDK/Examples/ContractDefinition/CountFunctionBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b4ff288b8cec4ad1af1eddec1362e4c3 +timeCreated: 1699363390 \ No newline at end of file diff --git a/Assets/ComethSDK/Examples/Scenes/Connect-Example.unity b/Assets/ComethSDK/Examples/Scenes/Connect-Example.unity index fecc368..935bfac 100644 --- a/Assets/ComethSDK/Examples/Scenes/Connect-Example.unity +++ b/Assets/ComethSDK/Examples/Scenes/Connect-Example.unity @@ -643,7 +643,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -270.7737, y: -243.40814} + m_AnchoredPosition: {x: -270.7737, y: -342.34204} m_SizeDelta: {x: 238.8818, y: 59.6312} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &270538141 @@ -1537,7 +1537,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -605.4474, y: -246.79013} + m_AnchoredPosition: {x: -605.4474, y: -345.72403} m_SizeDelta: {x: 238.8818, y: 59.6312} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &491760304 @@ -2526,8 +2526,8 @@ MonoBehaviour: m_TargetGraphic: {fileID: 141378945} m_HandleRect: {fileID: 141378944} m_Direction: 0 - m_Value: 0.49999994 - m_Size: 0.4630167 + m_Value: 0.50000006 + m_Size: 0.46301672 m_NumberOfSteps: 0 m_OnValueChanged: m_PersistentCalls: @@ -2570,6 +2570,140 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 853382815} m_CullTransparentMesh: 1 +--- !u!1 &855925796 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 855925797} + - component: {fileID: 855925799} + - component: {fileID: 855925798} + m_Layer: 5 + m_Name: Text (TMP) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &855925797 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 855925796} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1460381554} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &855925798 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 855925796} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: 'Send Sponsored Tx: Batch Count' + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4281479730 + m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 24 + m_fontSizeBase: 24 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &855925799 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 855925796} + m_CullTransparentMesh: 1 --- !u!1 &900214159 GameObject: m_ObjectHideFlags: 0 @@ -3253,7 +3387,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -599.75, y: -60.25} + m_AnchoredPosition: {x: -599.75, y: -238.45798} m_SizeDelta: {x: 238.8818, y: 59.6312} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1305436149 @@ -3461,6 +3595,7 @@ RectTransform: - {fileID: 1935419022} - {fileID: 1305436148} - {fileID: 216291508} + - {fileID: 1460381554} - {fileID: 2098011956} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -3909,6 +4044,139 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1444845274} m_CullTransparentMesh: 1 +--- !u!1 &1460381553 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1460381554} + - component: {fileID: 1460381557} + - component: {fileID: 1460381556} + - component: {fileID: 1460381555} + m_Layer: 5 + m_Name: 'Sponsored: Batch Count' + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1460381554 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1460381553} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 855925797} + m_Father: {fileID: 1379782326} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -598.25, y: -55.974243} + m_SizeDelta: {x: 258.7886, y: 59.6312} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1460381555 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1460381553} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1460381556} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1442284380} + m_TargetAssemblyTypeName: ComethSDK.Examples.Scripts.TestConnectWallet, ExampleScripts + m_MethodName: TestCallToCountBatch + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &1460381556 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1460381553} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.5647059, g: 0.6156863, b: 0.62352943, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1460381557 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1460381553} + m_CullTransparentMesh: 1 --- !u!1 &1499967064 GameObject: m_ObjectHideFlags: 0 @@ -4922,7 +5190,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &1935419022 RectTransform: m_ObjectHideFlags: 0 @@ -4941,7 +5209,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -276, y: -92.950195} + m_AnchoredPosition: {x: -276, y: -144.8052} m_SizeDelta: {x: 250, y: 60} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1935419023 diff --git a/Assets/ComethSDK/Examples/Scripts/ExampleScripts.asmdef b/Assets/ComethSDK/Examples/Scripts/ExampleScripts.asmdef index 8e56887..7bba4aa 100644 --- a/Assets/ComethSDK/Examples/Scripts/ExampleScripts.asmdef +++ b/Assets/ComethSDK/Examples/Scripts/ExampleScripts.asmdef @@ -13,7 +13,9 @@ "GUID:0022c094e0c7f481288196a6d3e9ce33", "GUID:7dd4f8caf82c243428d2e7006f515400", "GUID:a624b62300d14c788d1e3cedda57dc69", - "GUID:66ce3b4eab902fe469aa1f5b8634d609" + "GUID:66ce3b4eab902fe469aa1f5b8634d609", + "GUID:899b09f289b75794fb3f05e12d822ffd", + "GUID:d20ebb163def4678b3d544ec12ac7c87" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Assets/ComethSDK/Examples/Scripts/TestConnectWallet.cs b/Assets/ComethSDK/Examples/Scripts/TestConnectWallet.cs index 9805ba2..7270e86 100644 --- a/Assets/ComethSDK/Examples/Scripts/TestConnectWallet.cs +++ b/Assets/ComethSDK/Examples/Scripts/TestConnectWallet.cs @@ -1,7 +1,10 @@ using ComethSDK.Scripts.Adapters; using ComethSDK.Scripts.Core; +using ComethSDK.Scripts.Interfaces; using ComethSDK.Scripts.Services; using ComethSDK.Scripts.Tools; +using ComethSDK.Scripts.Types; +using ComethSDK.Scripts.Types.MessageTypes; using Nethereum.Web3; using TMPro; using UnityEngine; @@ -10,46 +13,50 @@ namespace ComethSDK.Examples.Scripts { public class TestConnectWallet : TestWallet { - [SerializeField] public ConnectAdaptor authAdaptor; + [SerializeField] public ConnectAdaptor connectAuthAdaptor; [SerializeField] private TMP_Text console; [SerializeField] private string walletAddress; [SerializeField] private string apiKey; + [SerializeField] private string baseUrl; private ComethWallet _wallet; private void Start() { - if (authAdaptor && !string.IsNullOrEmpty(apiKey)) - _wallet = new ComethWallet(authAdaptor, apiKey); - else - Debug.LogError("Please set the apiKey & authAdaptor serialised variables"); + if (!connectAuthAdaptor && string.IsNullOrEmpty(apiKey)) + { + Debug.LogError("Please set the apiKey & connectAuthAdaptor serialised variables"); + return; + } + + _wallet = string.IsNullOrEmpty(baseUrl) ? new ComethWallet(connectAuthAdaptor, apiKey) : new ComethWallet(connectAuthAdaptor, apiKey, baseUrl); } - public async void Connect() + public override async void Connect() { PrintInConsole("Connecting..."); - if(string.IsNullOrEmpty(walletAddress)) + if (string.IsNullOrEmpty(walletAddress)) await _wallet.Connect(); else await _wallet.Connect(walletAddress); PrintInConsole("Connected"); } - public async void Disconnect() + public override async void Disconnect() { PrintInConsole("Disconnecting..."); await _wallet.Logout(); PrintInConsole("Disconnected"); } - public async void SignMessage() + public override async void SignMessage() { PrintInConsole("Signing message..."); var messageSigned = await _wallet.SignMessage("Hello World!"); PrintInConsole("Message signed: " + messageSigned); } - public void CancelWait() + public override void CancelWait() { _wallet.CancelWaitingForEvent(); } @@ -79,7 +86,41 @@ public override async void SendTestTransaction(string to) var transactionReceipt = await _wallet.Wait(safeTxHash); PrintInConsole("Transaction confirmed, see it on the block explorer: " + transactionReceipt.TransactionHash); - SeeTransactionReceiptOnBlockExplorer(transactionReceipt.TransactionHash, authAdaptor.ChainId); + SeeTransactionReceiptOnBlockExplorer(transactionReceipt.TransactionHash, connectAuthAdaptor.ChainId); + } + + public async void SendBatchTestTransaction(string to) + { + if (to is "" or Constants.ZERO_ADDRESS) + { + Debug.LogError("Please enter a valid address"); + return; + } + + var dataArr = new[] + { + new MetaTransactionData + { + to = to, + value = "0", + data = "0x00000000" + }, + new MetaTransactionData + { + to = to, + value = "0", + data = "0x00000000" + } + }; + + PrintInConsole("Sending Batch transaction..."); + var safeTxHash = await _wallet.SendBatchTransactions(dataArr); + PrintInConsole("Safe transaction hash: " + safeTxHash); + PrintInConsole("Transaction sent, waiting for confirmation..."); + var transactionReceipt = await _wallet.Wait(safeTxHash); + PrintInConsole("Transaction confirmed, see it on the block explorer: " + + transactionReceipt.TransactionHash); + SeeTransactionReceiptOnBlockExplorer(transactionReceipt.TransactionHash, connectAuthAdaptor.ChainId); } public void GetAddress() @@ -88,7 +129,7 @@ public void GetAddress() PrintInConsole(address); } - public async void TestCallToCount() + public override async void TestCallToCount() { const string COUNTER_TEST_ADDRESS = @@ -96,7 +137,7 @@ const string var contract = _wallet.GetContract(Constants.COUNTER_ABI, COUNTER_TEST_ADDRESS); var countFunction = contract.GetFunction("count"); var data = countFunction.GetData(); - var web3 = new Web3(Constants.GetNetworkByChainID(authAdaptor.ChainId).RPCUrl); + var web3 = new Web3(Constants.GetNetworkByChainID(connectAuthAdaptor.ChainId).RPCUrl); var nonce = await Utils.GetNonce(web3, _wallet.GetAddress()); EstimateGasAndShow(COUNTER_TEST_ADDRESS, "0", data); @@ -110,12 +151,57 @@ const string { PrintInConsole("Transaction confirmed, see it on the block explorer: " + transactionReceipt.TransactionHash); - SeeTransactionReceiptOnBlockExplorer(transactionReceipt.TransactionHash, authAdaptor.ChainId); + SeeTransactionReceiptOnBlockExplorer(transactionReceipt.TransactionHash, connectAuthAdaptor.ChainId); } else { PrintInConsole("Issue with event, redirecting to wallet to see the transaction"); - SeeWalletOnBlockExplorer(_wallet.GetAddress(), authAdaptor.ChainId); + SeeWalletOnBlockExplorer(_wallet.GetAddress(), connectAuthAdaptor.ChainId); + } + } + + public async void TestCallToCountBatch() + { + const string + COUNTER_TEST_ADDRESS = + "0x3633A1bE570fBD902D10aC6ADd65BB11FC914624"; //On polygon : 0x84ADD3fa2c2463C8cF2C95aD70e4b5F602332160"; + var contract = _wallet.GetContract(Constants.COUNTER_ABI, COUNTER_TEST_ADDRESS); + var countFunction = contract.GetFunction("count"); + var data = countFunction.GetData(); + EstimateGasAndShow(COUNTER_TEST_ADDRESS, "0", data); + + var dataArr = new[] + { + new MetaTransactionData + { + to = COUNTER_TEST_ADDRESS, + value = "0x00", + data = data + }, + new MetaTransactionData + { + to = COUNTER_TEST_ADDRESS, + value = "0x00", + data = data + } + }; + + PrintInConsole("Sending Batch transaction..."); + var safeTxHash = await _wallet.SendBatchTransactions(dataArr); + PrintInConsole("Safe Transaction hash: " + safeTxHash); + PrintInConsole("Transaction sent, waiting for confirmation..."); + var transactionReceipt = await _wallet.Wait(safeTxHash); + + if (transactionReceipt != null) + { + PrintInConsole("Transaction confirmed, see it on the block explorer: " + + transactionReceipt.TransactionHash); + SeeTransactionReceiptOnBlockExplorer(transactionReceipt.TransactionHash, connectAuthAdaptor.ChainId); + } + else + { + PrintInConsole("Issue with event, redirecting to wallet to see the transaction"); + SeeWalletOnBlockExplorer(_wallet.GetAddress(), connectAuthAdaptor.ChainId); } } @@ -131,13 +217,47 @@ const string PrintInConsole("Query successful, Counter = " + counterAmount); } + public async void TestEstimateSafeTxGasWithSimulate() + { + var value = "0"; + var data = "0x00"; + var to = "0x6e13dA17777a7325DcCF7FAa2358Ef3Db6E452cE"; + + EstimateGasAndShowWithSimulate(to, value, data); + EstimateGasAndShow(to, value, data); + } + private async void EstimateGasAndShow(string to, string value, string data) { - var web3 = new Web3(Constants.GetNetworkByChainID(authAdaptor.ChainId).RPCUrl); - var nonce = await Utils.GetNonce(web3, _wallet.GetAddress()); + var provider = Constants.GetNetworkByChainID(connectAuthAdaptor.ChainId).RPCUrl; + var txData = new SafeTx + { + to = to, + value = value, + data = data + }; + + IMetaTransactionData[] safeTxDataArray = { txData }; - var gas = await GasService.CalculateMaxFees(_wallet.GetAddress(), to, value, data, nonce, web3); - PrintInConsole("Estimated max gas: " + gas); + var estimates = await GasService.EstimateTransactionGas(safeTxDataArray, walletAddress, provider); + PrintInConsole("Estimated safeTxGas Normal: " + estimates); + } + + private async void EstimateGasAndShowWithSimulate(string to, string value, string data) + { + var provider = Constants.GetNetworkByChainID(connectAuthAdaptor.ChainId).RPCUrl; + var txData = new SafeTx + { + to = to, + value = value, + data = data + }; + + var txDataArray = new IMetaTransactionData[] { txData }; + var gas = await GasService.EstimateSafeTxGasWithSimulate(walletAddress, txDataArray, "", + Constants.MUMBAI_SAFE_SINGLETON_ADDRESS, Constants.MUMBAI_SAFE_TX_ACCESSOR_ADDRESS, provider); + + PrintInConsole("Estimated safeTxGas gas Simulated: " + gas); } private void SeeTransactionReceiptOnBlockExplorer(string txHash, string chainId) @@ -155,6 +275,16 @@ private void SeeWalletOnBlockExplorer(string walletAddressToSee, string chainId) private void PrintInConsole(string str) { console.text += str + "\n"; + Debug.Log(str); + } + + private async void AddSignerRequestExamples() + { + var addSignerRequest = await connectAuthAdaptor.InitNewSignerRequest(walletAddress); + var newSignerRequests = await connectAuthAdaptor.GetNewSignerRequests(); + + var newSignerRequest = newSignerRequests[0]; + await _wallet.AddOwner(newSignerRequest.signerAddress); } } } \ No newline at end of file diff --git a/Assets/ComethSDK/Examples/Scripts/TestWalletConnectWithJwt.cs b/Assets/ComethSDK/Examples/Scripts/TestWalletConnectWithJwt.cs index 415c23d..4560edc 100644 --- a/Assets/ComethSDK/Examples/Scripts/TestWalletConnectWithJwt.cs +++ b/Assets/ComethSDK/Examples/Scripts/TestWalletConnectWithJwt.cs @@ -29,7 +29,7 @@ private void Start() public override async void Connect() { PrintInConsole("Connecting..."); - await _wallet.Connect(""); + await _wallet.Connect(); PrintInConsole("Connected"); } @@ -78,7 +78,7 @@ public override async void SendTestTransaction(string to) SeeTransactionReceiptOnBlockExplorer(transactionReceipt.TransactionHash, authWithJwtAdaptor.ChainId); } - public override async void GetUserInfo() + public override void GetUserInfo() { var userInfos = _wallet.GetUserInfos(); PrintUserInfosInConsole(userInfos); @@ -107,9 +107,12 @@ const string private async void EstimateGasAndShow(string to, string value, string data) { - var web3 = new Web3(Constants.GetNetworkByChainID(authWithJwtAdaptor.ChainId).RPCUrl); + var provider = Constants.GetNetworkByChainID(authWithJwtAdaptor.ChainId).RPCUrl; + var web3 = new Web3(provider); var nonce = await Utils.GetNonce(web3, _wallet.GetAddress()); - var gas = await GasService.CalculateMaxFees(_wallet.GetAddress(), to, value, data, nonce, web3); + var baseGas = Constants.DEFAULT_BASE_GAS; + var gas = await GasService.CalculateMaxFees(_wallet.GetAddress(), to, value, data, nonce, baseGas, + provider); PrintInConsole("Estimated max gas: " + gas); } diff --git a/Assets/ComethSDK/Scripts/Adapters/ConnectAdaptor.cs b/Assets/ComethSDK/Scripts/Adapters/ConnectAdaptor.cs index fd4b7e8..0b0cef2 100644 --- a/Assets/ComethSDK/Scripts/Adapters/ConnectAdaptor.cs +++ b/Assets/ComethSDK/Scripts/Adapters/ConnectAdaptor.cs @@ -8,7 +8,6 @@ using ComethSDK.Scripts.Tools.Signers; using ComethSDK.Scripts.Tools.Signers.Interfaces; using ComethSDK.Scripts.Types; -using JetBrains.Annotations; using Nethereum.Signer; using UnityEngine; @@ -30,18 +29,16 @@ private void Awake() throw new Exception("ChainId is not set"); if (string.IsNullOrEmpty(apiKey)) throw new Exception("ApiKey is not set"); - if (string.IsNullOrEmpty(baseUrl)) - throw new Exception("BaseUrl is not set"); if (!Utils.IsNetworkSupported(chainId.ToString())) throw new Exception("This network is not supported"); ChainId = chainId.ToString(); - _api = new API(apiKey, chainId); + _api = string.IsNullOrEmpty(baseUrl) ? new API(apiKey, chainId) : new API(apiKey, chainId, baseUrl); } public string ChainId { get; private set; } - public async Task Connect([CanBeNull] string burnerAddress = "") + public async Task Connect(string burnerAddress = "") { if (!string.IsNullOrEmpty(burnerAddress)) { @@ -110,7 +107,7 @@ public async Task InitNewSignerRequest(string walletAddres return addNewSignerRequest; } - public async Task GetNewSignerRequest() + public async Task GetNewSignerRequests() { var walletAddress = await GetWalletAddress(); return await _api.GetNewSignerRequests(walletAddress); diff --git a/Assets/ComethSDK/Scripts/Core/ComethWallet.cs b/Assets/ComethSDK/Scripts/Core/ComethWallet.cs index 2cd6b96..0be1e48 100644 --- a/Assets/ComethSDK/Scripts/Core/ComethWallet.cs +++ b/Assets/ComethSDK/Scripts/Core/ComethWallet.cs @@ -4,8 +4,10 @@ using System.Text; using System.Threading.Tasks; using ComethSDK.Scripts.Adapters.Interfaces; +using ComethSDK.Scripts.Enums; using ComethSDK.Scripts.HTTP; using ComethSDK.Scripts.HTTP.Responses; +using ComethSDK.Scripts.Interfaces; using ComethSDK.Scripts.Services; using ComethSDK.Scripts.Tools; using ComethSDK.Scripts.Types; @@ -29,19 +31,22 @@ public class ComethWallet private readonly IAuthAdaptor _authAdaptor; private readonly string _chainId; private readonly Uri _uri = new("https://api.connect.cometh.io"); + private readonly BigInteger BASE_GAS = Constants.DEFAULT_BASE_GAS; private bool _connected; private EventHandler _eventHandler; private Constants.Network _network; + private ProjectParams _projectParams; + private string _provider; private List _sponsoredAddresses = new(); private string _walletAddress; private Web3 _web3; - public ComethWallet(IAuthAdaptor authAdaptor, string apiKey) + public ComethWallet(IAuthAdaptor authAdaptor, string apiKey, string baseUrl = "") { if (!Utils.IsNetworkSupported(authAdaptor.ChainId)) throw new Exception("This network is not supported"); _chainId = authAdaptor.ChainId; - _api = new API(apiKey, int.Parse(_chainId)); + _api = string.IsNullOrEmpty(baseUrl) ? new API(apiKey, int.Parse(_chainId)) : new API(apiKey, int.Parse(_chainId), baseUrl); _authAdaptor = authAdaptor; } @@ -49,10 +54,12 @@ public async Task Connect([CanBeNull] string burnerAddress = "") { if (_authAdaptor == null) throw new Exception("No auth adaptor found"); - _web3 = new Web3(Constants.GetNetworkByChainID(_chainId).RPCUrl); + _provider = Constants.GetNetworkByChainID(_chainId).RPCUrl; + _web3 = new Web3(_provider); await _authAdaptor.Connect(burnerAddress); + _projectParams = await _api.GetProjectParams(); var account = _authAdaptor.GetAccount(); var predictedWalletAddress = await _api.GetWalletAddress(account); _walletAddress = predictedWalletAddress ?? throw new Exception("Error while getting wallet address"); @@ -194,6 +201,7 @@ public async Task SignMessage(string message) /** * Transaction Section */ + //TODO: change return type to SendTransactionResponse public async Task SendTransaction(string to, string value, string data) { if (!_connected) @@ -208,8 +216,9 @@ public async Task SendTransaction(string to, string value, string data) if (!ToSponsoredAddress(safeTx.to)) { - safeTx = await GasService.SetTransactionGas(safeTx, _walletAddress, _web3); - await GasService.VerifyHasEnoughBalance(_walletAddress, to, value, data, nonce, _web3); + safeTx = await GasService.SetTransactionGasWithSimulate(safeTx, _walletAddress, "", + Constants.MUMBAI_SAFE_SINGLETON_ADDRESS, Constants.MUMBAI_SAFE_TX_ACCESSOR_ADDRESS, _provider); + await GasService.VerifyHasEnoughBalance(_walletAddress, to, value, data, nonce, _provider); } var txSignature = await SignTypedData(safeTx, typedData); @@ -220,6 +229,61 @@ public async Task SendTransaction(string to, string value, string data) ); } + //TODO: change return type to SendTransactionResponse + public async Task SendBatchTransactions(IMetaTransactionData[] safeTxData) + { + if (safeTxData.Length == 0) throw new Exception("Empty array provided, no transaction to send"); + + if (!_connected) + { + Debug.Log("Please Login First"); + return ""; + } + + if (_projectParams == null) throw new Exception("Project params not found"); + + var nonce = await Utils.GetNonce(_web3, _walletAddress); + var multiSendData = MultiSend + .EncodeMultiSendArray(safeTxData, _provider, _projectParams.MultiSendContractAddress) + .data; + var safeTx = Utils.CreateSafeTx(_projectParams.MultiSendContractAddress, "0x00", multiSendData, nonce, + OperationType.DELEGATE_CALL); + var dataType = Utils.CreateSafeTxTypedData(_chainId, _walletAddress); + + if (!await IsSponsoredTransaction(safeTxData)) + { + var safeTxGasString = await GasService.EstimateSafeTxGasWithSimulate(_walletAddress, safeTxData, + _projectParams.MultiSendContractAddress, + Constants.MUMBAI_SAFE_SINGLETON_ADDRESS, Constants.MUMBAI_SAFE_TX_ACCESSOR_ADDRESS, _provider); + + var gasEstimates = new GasEstimates + { + baseGas = BASE_GAS, + gasPrice = await GasService.GetGasPrice(_provider), + safeTxGas = BigInteger.Parse(safeTxGasString) + }; + + var txValue = SafeService.GetTransactionsTotalValue(safeTxData); + await GasService.VerifyHasEnoughBalance(_walletAddress, gasEstimates, txValue, _provider); + + safeTx.safeTxGas += gasEstimates.safeTxGas; + safeTx.baseGas = gasEstimates.baseGas; + safeTx.gasPrice += gasEstimates.gasPrice; + } + + return await SignAndSendTransaction(safeTx, dataType); + } + + public async Task SignAndSendTransaction(SafeTx safeTx, + TypedData typedData) + { + var txSignature = await SignTypedData(safeTx, typedData); + + return await _api.RelayTransaction(new RelayTransactionType( + safeTx, txSignature, _walletAddress) + ); + } + /** * Private Methods */ @@ -254,6 +318,19 @@ private async Task SignTypedData(T message, TypedData IsSponsoredTransaction(IMetaTransactionData[] safeTxDataArray) + { + foreach (var safeTxData in safeTxDataArray) + { + var functionSelector = SafeService.GetFunctionSelector(safeTxData); + var sponsoredAddress = ToSponsoredAddress(safeTxData.to); + + if (!sponsoredAddress && functionSelector != Constants.ADD_OWNER_FUNCTION_SELECTOR) return false; + } + + return true; + } + private bool ToSponsoredAddress(string to) { //if index >= 0 then address is sponsored diff --git a/Assets/ComethSDK/Scripts/Core/Core.asmdef b/Assets/ComethSDK/Scripts/Core/Core.asmdef index 1516477..39e9a94 100644 --- a/Assets/ComethSDK/Scripts/Core/Core.asmdef +++ b/Assets/ComethSDK/Scripts/Core/Core.asmdef @@ -11,7 +11,8 @@ "GUID:34c16d0e9a6d109409d191c3c79fb3f6", "GUID:899b09f289b75794fb3f05e12d822ffd", "GUID:7dd4f8caf82c243428d2e7006f515400", - "GUID:a624b62300d14c788d1e3cedda57dc69" + "GUID:a624b62300d14c788d1e3cedda57dc69", + "GUID:d33d6685c40a472d891006b0f547ddbf" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Assets/ComethSDK/Scripts/Enums.meta b/Assets/ComethSDK/Scripts/Enums.meta new file mode 100644 index 0000000..64500c2 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Enums.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3ef7ca82e7ae413e8da5398a11510a21 +timeCreated: 1699371620 \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Enums/Enums.asmdef b/Assets/ComethSDK/Scripts/Enums/Enums.asmdef new file mode 100644 index 0000000..65453b5 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Enums/Enums.asmdef @@ -0,0 +1,3 @@ +{ + "name": "Enums" +} \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Enums/Enums.asmdef.meta b/Assets/ComethSDK/Scripts/Enums/Enums.asmdef.meta new file mode 100644 index 0000000..faf9124 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Enums/Enums.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d33d6685c40a472d891006b0f547ddbf +timeCreated: 1699371633 \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Types/OperationType.cs b/Assets/ComethSDK/Scripts/Enums/OperationType.cs similarity index 64% rename from Assets/ComethSDK/Scripts/Types/OperationType.cs rename to Assets/ComethSDK/Scripts/Enums/OperationType.cs index 3f071f7..45daeef 100644 --- a/Assets/ComethSDK/Scripts/Types/OperationType.cs +++ b/Assets/ComethSDK/Scripts/Enums/OperationType.cs @@ -1,4 +1,4 @@ -namespace ComethSDK.Scripts.Types +namespace ComethSDK.Scripts.Enums { public enum OperationType { diff --git a/Assets/ComethSDK/Scripts/Types/OperationType.cs.meta b/Assets/ComethSDK/Scripts/Enums/OperationType.cs.meta similarity index 100% rename from Assets/ComethSDK/Scripts/Types/OperationType.cs.meta rename to Assets/ComethSDK/Scripts/Enums/OperationType.cs.meta diff --git a/Assets/ComethSDK/Scripts/HTTP/API.cs b/Assets/ComethSDK/Scripts/HTTP/API.cs index 5b18e05..7ee54be 100644 --- a/Assets/ComethSDK/Scripts/HTTP/API.cs +++ b/Assets/ComethSDK/Scripts/HTTP/API.cs @@ -8,7 +8,6 @@ using ComethSDK.Scripts.Tools; using ComethSDK.Scripts.Types; using ComethSDK.Scripts.Types.MessageTypes; -using Microsoft.Extensions.Logging.Abstractions; using Nethereum.ABI.EIP712; using Nethereum.Siwe.Core; using Newtonsoft.Json; @@ -32,21 +31,21 @@ public RelayTransactionType(SafeTx safeTxData, string signatures, string walletA public class API { - private readonly HttpClient api; - - public API(string apiKey, int chainId) + private readonly HttpClient _api; + + public API(string apiKey, int chainId, string baseUrl = Constants.API_URL) { - api = new HttpClient(); - api.DefaultRequestHeaders.Add("apikey", apiKey); - api.DefaultRequestHeaders.Add("chainId", chainId.ToString()); - api.BaseAddress = new Uri(Constants.API_URL); + _api = new HttpClient(); + _api.DefaultRequestHeaders.Add("apikey", apiKey); + _api.DefaultRequestHeaders.Add("chainId", chainId.ToString()); + _api.BaseAddress = new Uri(baseUrl); } public async Task RelayTransaction(RelayTransactionType relayTransactionType) { var safeTxDataTypedWithSignature = new SafeTxWithSignature { - to = relayTransactionType.safeTxData.to.ToLower(), + to = relayTransactionType.safeTxData.to, value = relayTransactionType.safeTxData.value, data = relayTransactionType.safeTxData.data, operation = relayTransactionType.safeTxData.operation, @@ -62,7 +61,7 @@ public async Task RelayTransaction(RelayTransactionType relayTransaction var json = JsonConvert.SerializeObject(safeTxDataTypedWithSignature); var content = new StringContent(json, Encoding.UTF8, "application/json"); var requestUri = "/wallets/" + relayTransactionType.walletAddress + "/relay"; - var response = await api.PostAsync(requestUri, content); + var response = await _api.PostAsync(requestUri, content); var contentReceived = response.Content.ReadAsStringAsync().Result; var contentDeserializeObject = JsonConvert.DeserializeObject(contentReceived); @@ -75,7 +74,7 @@ public async Task RelayTransaction(RelayTransactionType relayTransaction public async Task> GetSponsoredAddresses() { - var response = await api.GetAsync("/sponsored-address"); + var response = await _api.GetAsync("/sponsored-address"); var content = response.Content.ReadAsStringAsync().Result; var sponsoredAddressesResponse = JsonConvert.DeserializeObject(content); @@ -97,7 +96,7 @@ public async Task RelayTransaction(RelayTransactionType relayTransaction //OwnerAddress is the address of the OEA public async Task GetPredictedSafeAddress(string ownerAddress) { - var response = await api.GetAsync($"/wallets/{ownerAddress}/getWalletAddress"); + var response = await _api.GetAsync($"/wallets/{ownerAddress}/getWalletAddress"); var result = response.Content.ReadAsStringAsync().Result; var predictedSafeAddressResponse = JsonConvert.DeserializeObject(result); @@ -120,7 +119,7 @@ public async Task ConnectToComethWallet(SiweMessage message, string sign }; var json = JsonConvert.SerializeObject(body); var content = new StringContent(json, Encoding.UTF8, "application/json"); - var response = await api.PostAsync(requestUri, content); + var response = await _api.PostAsync(requestUri, content); var contentReceived = response.Content.ReadAsStringAsync().Result; var contentDeserializeObject = JsonConvert.DeserializeObject(contentReceived); @@ -136,7 +135,7 @@ public async Task ConnectToComethAuth(string jwtToken) const string requestUri = "/key-store/connect"; var request = new HttpRequestMessage(HttpMethod.Post, requestUri); request.Headers.Add("token", jwtToken); - var response = await api.SendAsync(request); + var response = await _api.SendAsync(request); var contentReceived = response.Content.ReadAsStringAsync().Result; var contentDeserializeObject = JsonConvert.DeserializeObject(contentReceived); @@ -165,7 +164,7 @@ public async Task SignTypedDataWithComethAuth(string jwtToken, var content = new StringContent(json, Encoding.UTF8, "application/json"); request.Content = content; - var response = await api.SendAsync(request); + var response = await _api.SendAsync(request); var contentReceived = response.Content.ReadAsStringAsync().Result; var contentDeserializeObject = JsonConvert.DeserializeObject(contentReceived); @@ -185,7 +184,7 @@ public async Task IsValidSignature(string walletAddress, string message, s }; var json = JsonConvert.SerializeObject(isValidSignatureBody); var content = new StringContent(json, Encoding.UTF8, "application/json"); - var response = await api.PostAsync(requestUri, content); + var response = await _api.PostAsync(requestUri, content); var contentReceived = response.Content.ReadAsStringAsync().Result; if (contentReceived == "{\"success\":true,\"result\":true}") return true; @@ -200,7 +199,7 @@ public async Task GetWalletAddressFromUserID(string jwtToken) var request = new HttpRequestMessage(HttpMethod.Get, requestUri); request.Headers.Add("token", jwtToken); - var response = await api.SendAsync(request); + var response = await _api.SendAsync(request); var contentReceived = response.Content.ReadAsStringAsync().Result; var deserializedResponse = JsonConvert.DeserializeObject(contentReceived); @@ -225,7 +224,7 @@ public async Task InitWalletForUserID(string jwtToken, string ownerAddress) var content = new StringContent(json, Encoding.UTF8, "application/json"); request.Content = content; - var response = await api.SendAsync(request); + var response = await _api.SendAsync(request); var contentReceived = response.Content.ReadAsStringAsync().Result; Debug.Log(contentReceived); @@ -244,12 +243,12 @@ public async Task InitWallet(string ownerAddress) var content = new StringContent(json, Encoding.UTF8, "application/json"); request.Content = content; - var response = await api.SendAsync(request); + var response = await _api.SendAsync(request); var contentReceived = response.Content.ReadAsStringAsync().Result; - + var initWalletResponse = JsonConvert.DeserializeObject(contentReceived); - if(initWalletResponse is { success: true }) return initWalletResponse.walletAddress; + if (initWalletResponse is { success: true }) return initWalletResponse.walletAddress; Debug.LogError("Error in InitWallet"); return null; @@ -257,7 +256,7 @@ public async Task InitWallet(string ownerAddress) public async Task GetNonce(string walletAddress) { - var response = await api.GetAsync($"/wallets/{walletAddress}/connection-nonce"); + var response = await _api.GetAsync($"/wallets/{walletAddress}/connection-nonce"); var result = response.Content.ReadAsStringAsync().Result; var nonceResponse = JsonConvert.DeserializeObject(result); @@ -267,7 +266,7 @@ public async Task GetNonce(string walletAddress) public async Task GetWalletAddress(string ownerAddress) { - var response = await api.GetAsync($"/wallets/{ownerAddress}/wallet-address"); + var response = await _api.GetAsync($"/wallets/{ownerAddress}/wallet-address"); var result = response.Content.ReadAsStringAsync().Result; var predictedSafeAddressResponse = JsonConvert.DeserializeObject(result); @@ -293,7 +292,7 @@ public async Task Connect(SiweMessage messageToSign, string signature, string wa var content = new StringContent(json, Encoding.UTF8, "application/json"); request.Content = content; - var response = await api.SendAsync(request); + var response = await _api.SendAsync(request); var contentReceived = response.Content.ReadAsStringAsync().Result; Debug.Log(contentReceived); @@ -301,7 +300,7 @@ public async Task Connect(SiweMessage messageToSign, string signature, string wa public async Task GetWalletInfos(string walletAddress) { - var response = await api.GetAsync($"/wallets/{walletAddress}/wallet-infos"); + var response = await _api.GetAsync($"/wallets/{walletAddress}/wallet-infos"); var result = response.Content.ReadAsStringAsync().Result; var getWalletInfosResponse = JsonConvert.DeserializeObject(result); @@ -314,7 +313,7 @@ public async Task GetWalletInfos(string walletAddress) public async Task GetNewSignerRequests(string walletAddress) { - var response = await api.GetAsync($"/new-signer-request/{walletAddress}"); + var response = await _api.GetAsync($"/new-signer-request/{walletAddress}"); var result = response.Content.ReadAsStringAsync().Result; var getNewSignerRequestsResponse = JsonConvert.DeserializeObject(result); @@ -324,5 +323,18 @@ public async Task GetNewSignerRequests(string walletAddr Debug.LogError("Error in GetNewSignerRequests"); return null; } + + public async Task GetProjectParams() + { + var response = await _api.GetAsync("/project/params"); + var result = response.Content.ReadAsStringAsync().Result; + + var getNewSignerRequestsResponse = JsonConvert.DeserializeObject(result); + + if (getNewSignerRequestsResponse is { success: true }) return getNewSignerRequestsResponse.projectParams; + + Debug.LogError("Error in GetProjectParams"); + return null; + } } } \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/HTTP/HTTP.asmdef b/Assets/ComethSDK/Scripts/HTTP/HTTP.asmdef index 137aaca..65627c4 100644 --- a/Assets/ComethSDK/Scripts/HTTP/HTTP.asmdef +++ b/Assets/ComethSDK/Scripts/HTTP/HTTP.asmdef @@ -5,6 +5,7 @@ "GUID:34c16d0e9a6d109409d191c3c79fb3f6", "GUID:27966a43d31610b4eac00ec7ddc7308f", "GUID:66ce3b4eab902fe469aa1f5b8634d609", - "GUID:ea2c79e77633b404980a719967bcd567" + "GUID:ea2c79e77633b404980a719967bcd567", + "GUID:d33d6685c40a472d891006b0f547ddbf" ] } diff --git a/Assets/ComethSDK/Scripts/HTTP/Responses/GetProjectParamsResponse.cs b/Assets/ComethSDK/Scripts/HTTP/Responses/GetProjectParamsResponse.cs new file mode 100644 index 0000000..ec71001 --- /dev/null +++ b/Assets/ComethSDK/Scripts/HTTP/Responses/GetProjectParamsResponse.cs @@ -0,0 +1,10 @@ +using ComethSDK.Scripts.Types; + +namespace ComethSDK.Scripts.HTTP.Responses +{ + public class GetProjectParamsResponse + { + public bool success { get; set; } + public ProjectParams projectParams { get; set; } + } +} \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/HTTP/Responses/GetProjectParamsResponse.cs.meta b/Assets/ComethSDK/Scripts/HTTP/Responses/GetProjectParamsResponse.cs.meta new file mode 100644 index 0000000..bb106e1 --- /dev/null +++ b/Assets/ComethSDK/Scripts/HTTP/Responses/GetProjectParamsResponse.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4fe0d80f1c1f494398e3253d59b9d8d9 +timeCreated: 1699287897 \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Interfaces/IMetaTransactionData.cs b/Assets/ComethSDK/Scripts/Interfaces/IMetaTransactionData.cs index 0b95ce6..8074813 100644 --- a/Assets/ComethSDK/Scripts/Interfaces/IMetaTransactionData.cs +++ b/Assets/ComethSDK/Scripts/Interfaces/IMetaTransactionData.cs @@ -1,9 +1,12 @@ -namespace ComethSDK.Scripts.Interfaces +using ComethSDK.Scripts.Enums; + +namespace ComethSDK.Scripts.Interfaces { public interface IMetaTransactionData { string to { get; set; } string value { get; set; } string data { get; set; } + OperationType? operation { get; set; } } } \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Interfaces/ISafeTransactionDataPartial.cs b/Assets/ComethSDK/Scripts/Interfaces/ISafeTransactionDataPartial.cs index 4efc24e..850311d 100644 --- a/Assets/ComethSDK/Scripts/Interfaces/ISafeTransactionDataPartial.cs +++ b/Assets/ComethSDK/Scripts/Interfaces/ISafeTransactionDataPartial.cs @@ -1,11 +1,10 @@ using System.Numerics; -using ComethSDK.Scripts.Types; +using ComethSDK.Scripts.Enums; namespace ComethSDK.Scripts.Interfaces { public interface ISafeTransactionDataPartial : IMetaTransactionData { - OperationType? operation { get; } BigInteger safeTxGas { get; } BigInteger baseGas { get; } BigInteger gasPrice { get; } diff --git a/Assets/ComethSDK/Scripts/Interfaces/Interfaces.asmdef b/Assets/ComethSDK/Scripts/Interfaces/Interfaces.asmdef index 4a5b9a5..86ad5a9 100644 --- a/Assets/ComethSDK/Scripts/Interfaces/Interfaces.asmdef +++ b/Assets/ComethSDK/Scripts/Interfaces/Interfaces.asmdef @@ -3,7 +3,7 @@ "rootNamespace": "", "references": [ "GUID:20ff5bdc0066ab04a80b5c6707f10371", - "GUID:34c16d0e9a6d109409d191c3c79fb3f6" + "GUID:d33d6685c40a472d891006b0f547ddbf" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Assets/ComethSDK/Scripts/Services/GasService.cs b/Assets/ComethSDK/Scripts/Services/GasService.cs index c9bae0d..c6902a6 100644 --- a/Assets/ComethSDK/Scripts/Services/GasService.cs +++ b/Assets/ComethSDK/Scripts/Services/GasService.cs @@ -1,14 +1,19 @@ using System; using System.Numerics; using System.Threading.Tasks; +using ComethSDK.Scripts.Enums; using ComethSDK.Scripts.Interfaces; using ComethSDK.Scripts.Tools; using ComethSDK.Scripts.Types; using ComethSDK.Scripts.Types.MessageTypes; +using Nethereum.Contracts; +using Nethereum.Hex.HexConvertors.Extensions; using Nethereum.Hex.HexTypes; +using Nethereum.JsonRpc.Client; using Nethereum.RPC.Eth.DTOs; using Nethereum.RPC.Eth.Transactions; using Nethereum.Web3; +using UnityEngine; namespace ComethSDK.Scripts.Services { @@ -17,8 +22,9 @@ public static class GasService private static readonly decimal REWARD_PERCENTILE = Constants.DEFAULT_REWARD_PERCENTILE; private static readonly BigInteger BASE_GAS = Constants.DEFAULT_BASE_GAS; - public static async Task GetGasPrice(IWeb3 web3) + public static async Task GetGasPrice(string provider) { + var web3 = new Web3(provider); var ethFeeHistory = await web3.Eth.FeeHistory.SendRequestAsync( new HexBigInteger(1), new BlockParameter(), @@ -31,47 +37,164 @@ public static async Task GetGasPrice(IWeb3 web3) } public static async Task VerifyHasEnoughBalance(string from, string to, string value, string data, int nonce, - IWeb3 web3) + string provider) { + var web3 = new Web3(provider); var walletBalance = await web3.Eth.GetBalance.SendRequestAsync(from); - var totalGasCost = await CalculateMaxFees(from, to, value, data, nonce, web3); + var totalGasCost = await CalculateMaxFees(from, to, value, data, nonce, BASE_GAS, provider); if (walletBalance.Value < totalGasCost) throw new Exception("Not enough balance to send this value and pay for gas"); } - public static async Task EstimateTransactionGas(ISafeTransactionDataPartial safeTxData, - string from, IWeb3 web3) + public static async Task VerifyHasEnoughBalance(string walletAddress, GasEstimates gasEstimates, string txValue, + string provider) { - var safeTxGas = safeTxData.safeTxGas; - safeTxGas += await CalculateSafeTxGas(safeTxData.data, safeTxData.to, from, web3); + var web3 = new Web3(provider); + var walletBalance = await web3.Eth.GetBalance.SendRequestAsync(walletAddress); + var totalGasCost = GetTotalGasCost(gasEstimates); + var totalValue = BigInteger.Parse(txValue); + if (walletBalance.Value < BigInteger.Add(totalGasCost, totalValue)) + throw new Exception("Not enough balance to send this value and pay for gas"); + } + + private static BigInteger GetTotalGasCost(GasEstimates gasEstimates) + { + var totalGasCost = BigInteger.Add(gasEstimates.safeTxGas, gasEstimates.baseGas); + return BigInteger.Multiply(totalGasCost, gasEstimates.gasPrice); + } + + + public static async Task EstimateTransactionGas(IMetaTransactionData[] safeTxDataArray, + string from, string provider) + { + var safeTxGas = BigInteger.Zero; - var gasPrice = safeTxData.gasPrice; - gasPrice += await GetGasPrice(web3); + foreach (var safeTxData in safeTxDataArray) + safeTxGas += await CalculateSafeTxGas(safeTxData.data, safeTxData.to, from, provider); - return new GasEstimates { safeTxGas = safeTxGas, baseGas = BASE_GAS, gasPrice = gasPrice }; + return safeTxGas; } - public static async Task SetTransactionGas(SafeTx safeTxDataTyped, string from, IWeb3 web3) + public static async Task SetTransactionGas(SafeTx safeTxDataTyped, string from, BigInteger baseGas, + string provider) { - var gasEstimates = await EstimateTransactionGas(safeTxDataTyped, from, web3); - safeTxDataTyped.safeTxGas = gasEstimates.safeTxGas; - safeTxDataTyped.baseGas = gasEstimates.baseGas; - safeTxDataTyped.gasPrice = gasEstimates.gasPrice; + safeTxDataTyped.safeTxGas = + await EstimateTransactionGas(new IMetaTransactionData[] { safeTxDataTyped }, from, provider); + safeTxDataTyped.baseGas = baseGas; + safeTxDataTyped.gasPrice = await GetGasPrice(provider); + + return safeTxDataTyped; + } + + public static async Task SetTransactionGasWithSimulate(SafeTx safeTxDataTyped, string walletAddress, + string multiSendAddress, string singletonAddress, string simulateTxAccessorAddress, string provider) + { + var safeTxDataArray = new IMetaTransactionData[] { safeTxDataTyped }; + var gasEstimates = await EstimateSafeTxGasWithSimulate(walletAddress, safeTxDataArray, + multiSendAddress, singletonAddress, simulateTxAccessorAddress, provider); + + safeTxDataTyped.safeTxGas = BigInteger.Parse(gasEstimates); + safeTxDataTyped.baseGas = BASE_GAS; + safeTxDataTyped.gasPrice += await GetGasPrice(provider); return safeTxDataTyped; } public static async Task CalculateMaxFees(string from, string to, string value, string data, - int nonce, IWeb3 web3) + int nonce, BigInteger baseGas, string provider) { var safeTx = Utils.CreateSafeTx(to, value, data, nonce); - safeTx = await SetTransactionGas(safeTx, from, web3); + safeTx = await SetTransactionGas(safeTx, from, baseGas, provider); + var totalGasCost = (safeTx.safeTxGas + safeTx.baseGas) * safeTx.gasPrice; return totalGasCost + BigInteger.Parse(value); } - private static async Task CalculateSafeTxGas(string data, string to, string from, IWeb3 web3) + public static async Task EstimateSafeTxGasWithSimulate(string walletAddress, + IMetaTransactionData[] safeTxData, + string multiSendAddress, string singletonAddress, string simulateTxAccessorAddress, + string provider) //TODO: transaction data into a n array to handle multisend + { + IMetaTransactionData transaction; + + if (safeTxData.Length != 1) + { + var multiSendData = MultiSend.EncodeMultiSendArray(safeTxData, provider, multiSendAddress).data; + + transaction = new MetaTransactionData + { + to = multiSendAddress, + value = "0", + data = multiSendData, + operation = OperationType.DELEGATE_CALL + }; + } + else + { + transaction = safeTxData[0]; + transaction.operation = 0; + } + + //var isSafeDeployed = await SafeService.IsDeployed(walletAddress, provider); //TODO: this is not implemented + + var simulateTxContract = SimulateTxAcessorService.GetContract(simulateTxAccessorAddress, provider); + var simulateFunction = simulateTxContract.GetFunction("simulate"); + + object[] simulateFunctionInputs = + { transaction.to, transaction.value, transaction.data.HexToByteArray(), transaction.operation }; + var transactionDataToEstimate = simulateFunction.GetData(simulateFunctionInputs); + + // if the Safe is not deployed we can use the singleton address to simulate + var to = singletonAddress; //isSafeDeployed ? walletAddress : singletonAddress; + + var web3 = new Web3(provider); + var safeContract = web3.Eth.GetContract(Constants.SAFE_ABI, to); + var safeFunction = safeContract.GetFunction("simulateAndRevert"); + object[] simulateAndRevertFunctionInputs = + { simulateTxAccessorAddress, transactionDataToEstimate.HexToByteArray() }; + var safeFunctionToEstimate = safeFunction.GetData(simulateAndRevertFunctionInputs); + + var transactionToEstimateGas = new CallInput + { + Data = safeFunctionToEstimate, + To = to, + Value = new HexBigInteger(0) + }; + + try + { + var encodedResponse = await safeFunction.CallRawAsync(transactionToEstimateGas); + Debug.Log("encodedResponse :" + encodedResponse); + } + catch (SmartContractCustomErrorRevertException smartContractCustomErrorRevertException) + { + Debug.Log("Revert Reason:" + smartContractCustomErrorRevertException); + var safeTxGas = DecodeSafeTxGas(smartContractCustomErrorRevertException.ExceptionEncodedData); + return AddExtraGasForSafety(BigInteger.Parse(safeTxGas)); + } + catch (RpcResponseException revertResponseException) + { + Debug.Log("Revert Reason:" + revertResponseException.RpcError.Message); + } + catch (RpcClientUnknownException e) + { + Debug.Log(e); + } + + return ""; + } + + private static string DecodeSafeTxGas(string encodedSafeTxGas) { + Debug.Log("encodedSafeTxGasLenght:" + encodedSafeTxGas.Length); + var gasHex = encodedSafeTxGas.Substring(184, 10); + var gasNum = Convert.ToUInt64(gasHex, 16); + return gasNum.ToString(); + } + + private static async Task CalculateSafeTxGas(string data, string to, string from, string provider) + { + var web3 = new Web3(provider); var ethEstimateGas = new EthEstimateGas(web3.Client); var transactionInput = new CallInput @@ -82,5 +205,25 @@ private static async Task CalculateSafeTxGas(string data, string to, }; return await ethEstimateGas.SendRequestAsync(transactionInput); } + + private static async Task CalculateSafeTxGas(string data, string to, string provider) + { + var web3 = new Web3(provider); + var ethEstimateGas = new EthEstimateGas(web3.Client); + + var transactionInput = new CallInput + { + Data = data, + To = to + }; + return await ethEstimateGas.SendRequestAsync(transactionInput); + } + + private static string AddExtraGasForSafety(BigInteger safeTxGas) + { + var safeTxGasInt = (int)safeTxGas; + + return Math.Round(safeTxGasInt * 1.1, 0).ToString(); + } } } \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Services/SafeService.cs b/Assets/ComethSDK/Scripts/Services/SafeService.cs index 0fc0428..3c7b7ad 100644 --- a/Assets/ComethSDK/Scripts/Services/SafeService.cs +++ b/Assets/ComethSDK/Scripts/Services/SafeService.cs @@ -1,6 +1,8 @@ using System; using System.Threading.Tasks; using ComethSDK.Scripts.HTTP; +using ComethSDK.Scripts.Tools; +using ComethSDK.Scripts.Interfaces; using Nethereum.GnosisSafe; using Nethereum.Web3; @@ -28,6 +30,15 @@ public static async Task IsSigner(string signerAddress, string walletAddre return true; } + public static string EncodeFunctionData(string functionName, string safeAddress, string provider, params object[] functionInput) + { + var web3 = new Web3(provider); + var contract = web3.Eth.GetContract(Constants.SAFE_ABI, safeAddress); + var function = contract.GetFunction(functionName); + var data = function.GetData(functionInput); + return data; + } + private static async Task IsSafeOwner(string walletAddress, string signerAddress, string provider) { var web3 = new Web3(provider); @@ -35,9 +46,27 @@ private static async Task IsSafeOwner(string walletAddress, string signerA return await service.IsOwnerQueryAsync(signerAddress); } - private static async Task IsDeployed(string walletAddress, string provider) + //TODO: Implement this + public static async Task IsDeployed(string walletAddress, string provider) { throw new NotImplementedException(); } + + public static string GetFunctionSelector(IMetaTransactionData metaTransactionData) + { + return metaTransactionData.data[..10]; + } + + public static string GetTransactionsTotalValue(IMetaTransactionData[] safeTxData) + { + var txValue = 0; + + foreach (var safeTx in safeTxData) + { + txValue += int.Parse(safeTx.value); + } + + return txValue.ToString(); + } } } \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Services/Services.asmdef b/Assets/ComethSDK/Scripts/Services/Services.asmdef index 492491e..67d3a55 100644 --- a/Assets/ComethSDK/Scripts/Services/Services.asmdef +++ b/Assets/ComethSDK/Scripts/Services/Services.asmdef @@ -8,7 +8,8 @@ "GUID:34c16d0e9a6d109409d191c3c79fb3f6", "GUID:ea2c79e77633b404980a719967bcd567", "GUID:899b09f289b75794fb3f05e12d822ffd", - "GUID:0f10bb1ed19dbc149b242394e50d9774" + "GUID:0f10bb1ed19dbc149b242394e50d9774", + "GUID:d33d6685c40a472d891006b0f547ddbf" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Assets/ComethSDK/Scripts/Services/SimulateTxAcessorService.cs b/Assets/ComethSDK/Scripts/Services/SimulateTxAcessorService.cs new file mode 100644 index 0000000..59dc229 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Services/SimulateTxAcessorService.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Nethereum.Contracts; +using Nethereum.Web3; + +namespace ComethSDK.Scripts.Services +{ + public class SimulateTxAcessorService + { + private const string _abi = "[{inputs:[],stateMutability:\"nonpayable\",type:\"constructor\",},{inputs:[{internalType:\"address\",name:\"to\",type:\"address\",},{internalType:\"uint256\",name:\"value\",type:\"uint256\",},{internalType:\"bytes\",name:\"data\",type:\"bytes\",},{internalType:\"enumEnum.Operation\",name:\"operation\",type:\"uint8\",},],name:\"simulate\",outputs:[{internalType:\"uint256\",name:\"estimate\",type:\"uint256\",},{internalType:\"bool\",name:\"success\",type:\"bool\",},{internalType:\"bytes\",name:\"returnData\",type:\"bytes\",},],stateMutability:\"nonpayable\",type:\"function\",},]"; + + public static Contract GetContract(string address, string provider) + { + var web3 = new Web3(provider); + return web3.Eth.GetContract(_abi, address); + } + } +} \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Services/SimulateTxAcessorService.cs.meta b/Assets/ComethSDK/Scripts/Services/SimulateTxAcessorService.cs.meta new file mode 100644 index 0000000..02cfb43 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Services/SimulateTxAcessorService.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 90a2e38d68d74aa19c441162b45f5d76 +timeCreated: 1698924875 \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Tools/Constants.cs b/Assets/ComethSDK/Scripts/Tools/Constants.cs index ef369d1..0f22cc8 100644 --- a/Assets/ComethSDK/Scripts/Tools/Constants.cs +++ b/Assets/ComethSDK/Scripts/Tools/Constants.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Numerics; using UnityEngine; @@ -16,15 +15,23 @@ public enum Network public const string API_URL = "https://api.connect.cometh.io/"; public const string ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; + public const string MULTI_SEND_ABI = + "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"}],\"name\":\"multiSend\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}]"; + public const string SAFE_ABI = "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"AddedOwner\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"approvedHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ApproveHash\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"handler\",\"type\":\"address\"}],\"name\":\"ChangedFallbackHandler\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"guard\",\"type\":\"address\"}],\"name\":\"ChangedGuard\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"}],\"name\":\"ChangedThreshold\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"module\",\"type\":\"address\"}],\"name\":\"DisabledModule\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"module\",\"type\":\"address\"}],\"name\":\"EnabledModule\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"}],\"name\":\"ExecutionFailure\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"module\",\"type\":\"address\"}],\"name\":\"ExecutionFromModuleFailure\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"module\",\"type\":\"address\"}],\"name\":\"ExecutionFromModuleSuccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"}],\"name\":\"ExecutionSuccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"RemovedOwner\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"module\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"enum Enum.Operation\",\"name\":\"operation\",\"type\":\"uint8\"}],\"name\":\"SafeModuleTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"enum Enum.Operation\",\"name\":\"operation\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"safeTxGas\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"baseGas\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasPrice\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"gasToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address payable\",\"name\":\"refundReceiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"additionalInfo\",\"type\":\"bytes\"}],\"name\":\"SafeMultiSigTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"SafeReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"owners\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"initializer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"fallbackHandler\",\"type\":\"address\"}],\"name\":\"SafeSetup\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"msgHash\",\"type\":\"bytes32\"}],\"name\":\"SignMsg\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"VERSION\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"addOwnerWithThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashToApprove\",\"type\":\"bytes32\"}],\"name\":\"approveHash\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"approvedHashes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"changeThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"dataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"requiredSignatures\",\"type\":\"uint256\"}],\"name\":\"checkNSignatures\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"dataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"checkSignatures\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"prevModule\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"module\",\"type\":\"address\"}],\"name\":\"disableModule\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"domainSeparator\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"module\",\"type\":\"address\"}],\"name\":\"enableModule\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"enum Enum.Operation\",\"name\":\"operation\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"safeTxGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"baseGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"gasToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"refundReceiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_nonce\",\"type\":\"uint256\"}],\"name\":\"encodeTransactionData\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"enum Enum.Operation\",\"name\":\"operation\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"safeTxGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"baseGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"gasToken\",\"type\":\"address\"},{\"internalType\":\"address payable\",\"name\":\"refundReceiver\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"execTransaction\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"enum Enum.Operation\",\"name\":\"operation\",\"type\":\"uint8\"}],\"name\":\"execTransactionFromModule\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"enum Enum.Operation\",\"name\":\"operation\",\"type\":\"uint8\"}],\"name\":\"execTransactionFromModuleReturnData\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getChainId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"start\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"pageSize\",\"type\":\"uint256\"}],\"name\":\"getModulesPaginated\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"array\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"next\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getOwners\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"offset\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"getStorageAt\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"enum Enum.Operation\",\"name\":\"operation\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"safeTxGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"baseGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"gasToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"refundReceiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_nonce\",\"type\":\"uint256\"}],\"name\":\"getTransactionHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"module\",\"type\":\"address\"}],\"name\":\"isModuleEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"isOwner\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"prevOwner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"removeOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"enum Enum.Operation\",\"name\":\"operation\",\"type\":\"uint8\"}],\"name\":\"requiredTxGas\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"handler\",\"type\":\"address\"}],\"name\":\"setFallbackHandler\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"guard\",\"type\":\"address\"}],\"name\":\"setGuard\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_owners\",\"type\":\"address[]\"},{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"fallbackHandler\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"paymentToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"payment\",\"type\":\"uint256\"},{\"internalType\":\"address payable\",\"name\":\"paymentReceiver\",\"type\":\"address\"}],\"name\":\"setup\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"signedMessages\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"targetContract\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"calldataPayload\",\"type\":\"bytes\"}],\"name\":\"simulateAndRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"prevOwner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"oldOwner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"swapOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]"; public const string COUNTER_ABI = "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"CalledFrom\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"count\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"countFail\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"counters\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"repeat\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"name\":\"gasWaster\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"justemit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"offset\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"xxx\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]"; + public const decimal DEFAULT_REWARD_PERCENTILE = 80; + public const string ADD_OWNER_FUNCTION_SELECTOR = "0x0d582f13"; + public static readonly BigInteger BLOCK_EVENT_GAP = -500; public static readonly BigInteger DEFAULT_BASE_GAS = 80000; - public static readonly decimal DEFAULT_REWARD_PERCENTILE = 80; + + public static readonly string MUMBAI_SAFE_SINGLETON_ADDRESS = "0x3E5c63644E683549055b9Be8653de26E0B4CD36E"; + public static readonly string MUMBAI_SAFE_TX_ACCESSOR_ADDRESS = "0x59AD6735bCd8152B84860Cb256dD9e96b85F69Da"; public static readonly Dictionary Networks = new() diff --git a/Assets/ComethSDK/Scripts/Tools/MultiSend.cs b/Assets/ComethSDK/Scripts/Tools/MultiSend.cs new file mode 100644 index 0000000..a971ead --- /dev/null +++ b/Assets/ComethSDK/Scripts/Tools/MultiSend.cs @@ -0,0 +1,50 @@ +using ComethSDK.Scripts.Interfaces; +using ComethSDK.Scripts.Types; +using Nethereum.ABI; +using Nethereum.Hex.HexConvertors.Extensions; +using Nethereum.Web3; + +namespace ComethSDK.Scripts.Tools +{ + public static class MultiSend + { + public static IMetaTransactionData EncodeMultiSendArray(IMetaTransactionData[] safeTransactionDataPartials, + string provider, string multiSendContractAddress) + { + var transactionEncoded = "0x"; + foreach (var safeTransaction in safeTransactionDataPartials) + { + var encodedData = EncodeMultiSend(safeTransaction); + var encodedHexData = encodedData.ToHex(); + var encodedHexDataWithoutPrefix = encodedHexData.RemoveHexPrefix(); + transactionEncoded += encodedHexDataWithoutPrefix; + } + + var web3 = new Web3(provider); + + var contract = web3.Eth.GetContract(Constants.MULTI_SEND_ABI, multiSendContractAddress); + var countFunction = contract.GetFunction("multiSend"); + var data = countFunction.GetData(transactionEncoded.HexToByteArray()); + + var metaTransaction = new MetaTransactionData + { + to = multiSendContractAddress, + value = "0x00", + data = data + }; + return metaTransaction; + } + + private static byte[] EncodeMultiSend(IMetaTransactionData safeTxData) + { + var callData = safeTxData.data.HexToByteArray(); + var operation = new ABIValue("uint8", 1); + var address = new ABIValue("address", safeTxData.to); + var value = new ABIValue("uint256", safeTxData.value); + var dataLength = new ABIValue("uint256", callData.Length); + var data = new ABIValue("bytes", callData); + + return new ABIEncode().GetABIEncodedPacked(operation, address, value, dataLength, data); + } + } +} \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Tools/MultiSend.cs.meta b/Assets/ComethSDK/Scripts/Tools/MultiSend.cs.meta new file mode 100644 index 0000000..82ada78 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Tools/MultiSend.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 42847bc76a1c452482db71734b521793 +timeCreated: 1699450672 \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Tools/Tools.asmdef b/Assets/ComethSDK/Scripts/Tools/Tools.asmdef index 7953f9b..2556934 100644 --- a/Assets/ComethSDK/Scripts/Tools/Tools.asmdef +++ b/Assets/ComethSDK/Scripts/Tools/Tools.asmdef @@ -3,6 +3,7 @@ "references": [ "GUID:899b09f289b75794fb3f05e12d822ffd", "GUID:34c16d0e9a6d109409d191c3c79fb3f6", - "GUID:0f10bb1ed19dbc149b242394e50d9774" + "GUID:0f10bb1ed19dbc149b242394e50d9774", + "GUID:d33d6685c40a472d891006b0f547ddbf" ] } diff --git a/Assets/ComethSDK/Scripts/Tools/Utils.cs b/Assets/ComethSDK/Scripts/Tools/Utils.cs index f6c3f7d..f24bb25 100644 --- a/Assets/ComethSDK/Scripts/Tools/Utils.cs +++ b/Assets/ComethSDK/Scripts/Tools/Utils.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Numerics; using System.Threading.Tasks; +using ComethSDK.Scripts.Enums; using ComethSDK.Scripts.Interfaces; using ComethSDK.Scripts.Types.MessageTypes; using Nethereum.ABI.EIP712; @@ -10,14 +11,15 @@ namespace ComethSDK.Scripts.Tools { public static class Utils { - public static SafeTx CreateSafeTx(string to, string value, string data, int nonce) + public static SafeTx CreateSafeTx(string to, string value, string data, int nonce, + OperationType operationType = 0) { var safeTx = new SafeTx { to = to, value = value, data = data, - operation = 0, + operation = operationType, safeTxGas = new BigInteger(0), baseGas = new BigInteger(0), gasPrice = new BigInteger(0), @@ -96,7 +98,7 @@ public static async Task GetNonce(Web3 web3, string contractAddress) var result = await function.CallAsync(); return result; } - + public static bool IsNetworkSupported(string chainId) { foreach (var network in Constants.Networks.Values) diff --git a/Assets/ComethSDK/Scripts/Types/MessageTypes/MessageTypes.asmdef b/Assets/ComethSDK/Scripts/Types/MessageTypes/MessageTypes.asmdef index 6e29c48..751fe28 100644 --- a/Assets/ComethSDK/Scripts/Types/MessageTypes/MessageTypes.asmdef +++ b/Assets/ComethSDK/Scripts/Types/MessageTypes/MessageTypes.asmdef @@ -2,6 +2,8 @@ "name": "MessageTypes", "references": [ "GUID:34c16d0e9a6d109409d191c3c79fb3f6", + "GUID:d33d6685c40a472d891006b0f547ddbf", "GUID:0f10bb1ed19dbc149b242394e50d9774" + ] } diff --git a/Assets/ComethSDK/Scripts/Types/MessageTypes/SafeTx.cs b/Assets/ComethSDK/Scripts/Types/MessageTypes/SafeTx.cs index 8f2b96a..05940d2 100644 --- a/Assets/ComethSDK/Scripts/Types/MessageTypes/SafeTx.cs +++ b/Assets/ComethSDK/Scripts/Types/MessageTypes/SafeTx.cs @@ -1,4 +1,5 @@ using System.Numerics; +using ComethSDK.Scripts.Enums; using ComethSDK.Scripts.Interfaces; using Nethereum.ABI.FunctionEncoding.Attributes; diff --git a/Assets/ComethSDK/Scripts/Types/MetaTransactionData.cs b/Assets/ComethSDK/Scripts/Types/MetaTransactionData.cs new file mode 100644 index 0000000..930f5f1 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Types/MetaTransactionData.cs @@ -0,0 +1,13 @@ +using ComethSDK.Scripts.Enums; +using ComethSDK.Scripts.Interfaces; + +namespace ComethSDK.Scripts.Types +{ + public class MetaTransactionData : IMetaTransactionData + { + public string to { get; set; } + public string value { get; set; } + public string data { get; set; } + public OperationType? operation { get; set; } + } +} \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Types/MetaTransactionData.cs.meta b/Assets/ComethSDK/Scripts/Types/MetaTransactionData.cs.meta new file mode 100644 index 0000000..de45008 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Types/MetaTransactionData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b5f1880051994406896152a48a3f56f1 +timeCreated: 1699370830 \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Types/ProjectParams.cs b/Assets/ComethSDK/Scripts/Types/ProjectParams.cs new file mode 100644 index 0000000..2bd8584 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Types/ProjectParams.cs @@ -0,0 +1,9 @@ +namespace ComethSDK.Scripts.Types +{ + public class ProjectParams + { + public string ChainId; + public string P256FactoryContractAddress; + public string MultiSendContractAddress; + } +} \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Types/ProjectParams.cs.meta b/Assets/ComethSDK/Scripts/Types/ProjectParams.cs.meta new file mode 100644 index 0000000..17fbeb3 --- /dev/null +++ b/Assets/ComethSDK/Scripts/Types/ProjectParams.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 076ace3f06a74691a5dacb928951e900 +timeCreated: 1699275623 \ No newline at end of file diff --git a/Assets/ComethSDK/Scripts/Types/SafeTxWithSignature.cs b/Assets/ComethSDK/Scripts/Types/SafeTxWithSignature.cs index c44221c..714f704 100644 --- a/Assets/ComethSDK/Scripts/Types/SafeTxWithSignature.cs +++ b/Assets/ComethSDK/Scripts/Types/SafeTxWithSignature.cs @@ -1,4 +1,6 @@ -namespace ComethSDK.Scripts.Types +using ComethSDK.Scripts.Enums; + +namespace ComethSDK.Scripts.Types { public class SafeTxWithSignature { diff --git a/Assets/ComethSDK/Scripts/Types/Types.asmdef b/Assets/ComethSDK/Scripts/Types/Types.asmdef index b5d648e..3add788 100644 --- a/Assets/ComethSDK/Scripts/Types/Types.asmdef +++ b/Assets/ComethSDK/Scripts/Types/Types.asmdef @@ -1,6 +1,8 @@ { "name": "Types", "references": [ - "GUID:20ff5bdc0066ab04a80b5c6707f10371" + "GUID:20ff5bdc0066ab04a80b5c6707f10371", + "GUID:d33d6685c40a472d891006b0f547ddbf", + "GUID:0f10bb1ed19dbc149b242394e50d9774" ] } diff --git a/README.md b/README.md index 0a0afcd..bd4b9c2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ Cometh Connect SDK allows developers to onboard their users with a seedless, gasless experience familiar to Web2. Account Abstraction (AA) improves transaction user experience by using smart contract wallets as primary accounts. -To get an API key please [Contact us](https://cometh.io/) +To get an API key please [Contact us](https://cometh.io/) \ +You can view the documentation [here](https://docs.cometh.io/connect/quickstart/what-is-connect) ## Test Cometh Connect SDK @@ -87,3 +88,21 @@ var messageSigned = await _wallet.SignMessage("Hello World!"); ``` Sign the given message using the EOA, owner of the smart wallet. + +### Init a new Signer Request + +```C# +var addSignerRequest = await connectAuthAdaptor.InitNewSignerRequest(walletAddress); +``` + +Initialize a new SignerRequest to be used by your backend. + +### Get all Signer Requests + +```C# +var newSignerRequests = await connectAuthAdaptor.GetNewSignerRequests(); +``` + +Get all signerRequests for the connected address. + +For more information about adding new devices please refer to this [documentation](https://docs.cometh.io/connect/features/add-a-new-device)