Skip to content

Commit bce7bcc

Browse files
authored
Extract func sig from more kinds of inputs (#56)
1 parent 1899360 commit bce7bcc

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

Thirdweb.Tests/Thirdweb.Contracts/Thirdweb.Contracts.Tests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ public async Task ReadTest_Tuple()
7878
Assert.Equal(0, result.ReturnValue2);
7979
}
8080

81+
[Fact(Timeout = 120000)]
82+
public async Task ReadTest_FullSig()
83+
{
84+
var contract = await GetContract();
85+
var result = await ThirdwebContract.Read<string>(contract, "function name() view returns (string)");
86+
Assert.Equal("Kitty DropERC20", result);
87+
}
88+
89+
[Fact(Timeout = 120000)]
90+
public async Task ReadTest_PartialSig()
91+
{
92+
var contract = await GetContract();
93+
var result = await ThirdwebContract.Read<string>(contract, "name()");
94+
Assert.Equal("Kitty DropERC20", result);
95+
}
96+
8197
private class AllowlistProof
8298
{
8399
public List<byte[]> Proof { get; set; } = new();
@@ -104,6 +120,35 @@ public async Task WriteTest_SmartAccount()
104120
Assert.Equal(result.TransactionHash, receipt.TransactionHash);
105121
}
106122

123+
[Fact(Timeout = 120000)]
124+
public async Task WriteTest_SmartAccount_FullSig()
125+
{
126+
var contract = await GetContract();
127+
var smartAccount = await GetAccount();
128+
var receiver = await smartAccount.GetAddress();
129+
var quantity = BigInteger.One;
130+
var currency = Constants.NATIVE_TOKEN_ADDRESS;
131+
var pricePerToken = BigInteger.Zero;
132+
var allowlistProof = new object[] { new byte[] { }, BigInteger.Zero, BigInteger.Zero, Constants.ADDRESS_ZERO };
133+
var data = new byte[] { };
134+
var result = await ThirdwebContract.Write(
135+
smartAccount,
136+
contract,
137+
"claim(address, uint256, address, uint256, (bytes32[], uint256, uint256, address), bytes)",
138+
0,
139+
receiver,
140+
quantity,
141+
currency,
142+
pricePerToken,
143+
allowlistProof,
144+
data
145+
);
146+
Assert.NotNull(result);
147+
var receipt = await ThirdwebTransaction.WaitForTransactionReceipt(contract.Client, contract.Chain, result.TransactionHash);
148+
Assert.NotNull(receipt);
149+
Assert.Equal(result.TransactionHash, receipt.TransactionHash);
150+
}
151+
107152
[Fact(Timeout = 120000)]
108153
public async Task WriteTest_PrivateKeyAccount()
109154
{

Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,27 @@ public static async Task<string> FetchAbi(ThirdwebClient client, string address,
9999
public static async Task<T> Read<T>(ThirdwebContract contract, string method, params object[] parameters)
100100
{
101101
var rpc = ThirdwebRPC.GetRpcInstance(contract.Client, contract.Chain);
102-
103102
var contractRaw = new Contract(null, contract.Abi, contract.Address);
103+
104104
var function = GetFunctionMatchSignature(contractRaw, method, parameters);
105-
var data = function.GetData(parameters);
105+
if (function == null)
106+
{
107+
if (method.Contains("("))
108+
{
109+
try
110+
{
111+
var canonicalSignature = ExtractCanonicalSignature(method);
112+
var selector = Nethereum.Util.Sha3Keccack.Current.CalculateHash(canonicalSignature)[..8];
113+
function = contractRaw.GetFunctionBySignature(selector);
114+
}
115+
catch
116+
{
117+
function = contractRaw.GetFunction(method);
118+
}
119+
}
120+
}
106121

122+
var data = function.GetData(parameters);
107123
var resultData = await rpc.SendRequestAsync<string>("eth_call", new { to = contract.Address, data = data }, "latest").ConfigureAwait(false);
108124

109125
return function.DecodeTypeOutput<T>(resultData);
@@ -122,6 +138,23 @@ public static async Task<ThirdwebTransaction> Prepare(IThirdwebWallet wallet, Th
122138
{
123139
var contractRaw = new Contract(null, contract.Abi, contract.Address);
124140
var function = GetFunctionMatchSignature(contractRaw, method, parameters);
141+
if (function == null)
142+
{
143+
if (method.Contains("("))
144+
{
145+
try
146+
{
147+
var canonicalSignature = ExtractCanonicalSignature(method);
148+
var selector = Nethereum.Util.Sha3Keccack.Current.CalculateHash(canonicalSignature)[..8];
149+
function = contractRaw.GetFunctionBySignature(selector);
150+
}
151+
catch
152+
{
153+
function = contractRaw.GetFunction(method);
154+
}
155+
}
156+
}
157+
125158
var data = function.GetData(parameters);
126159
var transaction = new ThirdwebTransactionInput
127160
{
@@ -171,5 +204,35 @@ private static Function GetFunctionMatchSignature(Contract contract, string func
171204
}
172205
return null;
173206
}
207+
208+
/// <summary>
209+
/// Extracts the canonical signature from the specified method.
210+
/// </summary>
211+
/// <param name="method">The method to extract the signature from.</param>
212+
/// <returns>The canonical signature.</returns>
213+
/// <exception cref="ArgumentException"></exception>
214+
private static string ExtractCanonicalSignature(string method)
215+
{
216+
method = method.Split("returns")[0];
217+
var startOfParameters = method.IndexOf('(');
218+
if (startOfParameters == -1)
219+
{
220+
throw new ArgumentException("Invalid function signature: Missing opening parenthesis.");
221+
}
222+
223+
var endOfParameters = method.LastIndexOf(')');
224+
if (endOfParameters == -1)
225+
{
226+
throw new ArgumentException("Invalid function signature: Missing closing parenthesis.");
227+
}
228+
229+
var functionName = method.Substring(0, startOfParameters).Trim().Split(' ').Last(); // Get the last part after any spaces (in case of "function name(...)")
230+
var parameters = method.Substring(startOfParameters + 1, endOfParameters - startOfParameters - 1);
231+
232+
var paramTypes = parameters.Split(',').Select(param => param.Trim().Split(' ')[0]).ToArray();
233+
234+
var canonicalSignature = $"{functionName}({string.Join(",", paramTypes)})";
235+
return canonicalSignature;
236+
}
174237
}
175238
}

0 commit comments

Comments
 (0)