[SDK] Fix abi param parsing (#4229)

## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to enhance the parsing functionality for Solidity types in the contract UI components and transaction handling.

### Detailed summary
- Added parsing logic for `string` type in `parseAbiParams`
- Improved handling of different data types in contract transactions
- Added test cases for parsing `string` and `number` in contract transactions

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
This commit is contained in:
kien-ngo
2024-08-21 17:02:36 +00:00
parent 783e448e2e
commit 2c37732984
5 changed files with 100 additions and 0 deletions
@@ -3,6 +3,7 @@ import { parseEther } from "ethers/lib/utils";
import { useCallback } from "react";
import { Button } from "tw-components";
import type { SolidityInputWithTypeProps } from ".";
import { validateInt } from "./helpers";
export const SolidityIntInput: React.FC<SolidityInputWithTypeProps> = ({
@@ -31,4 +31,34 @@ describe.runIf(process.env.TW_SECRET_KEY)("transaction: read", () => {
);
}
});
it("should work with string and number, not just bigint", async () => {
/**
* The following 3 readContracts all result in the same value because
* the param is still parsed and encoded properly downstream by `numberToHex`
* See this docs for more context: "../utils/encoding/hex.test.js"
*/
const [bigintResult, numberResult, stringResult] = await Promise.all([
readContract({
contract: DOODLES_CONTRACT,
method: "function ownerOf(uint256 tokenId) view returns (address)",
params: [1n],
}),
readContract({
contract: DOODLES_CONTRACT,
method: "function ownerOf(uint256 tokenId) view returns (address)",
// @ts-ignore Intentional
params: [1], // <- supposed to be a bigint
}),
readContract({
contract: DOODLES_CONTRACT,
method: "function ownerOf(uint256 tokenId) view returns (address)",
// @ts-ignore Intentional
params: ["1"], // <- supposed to be a bigint
}),
]);
expect(bigintResult === numberResult).toBe(true);
expect(bigintResult === stringResult).toBe(true);
});
});
@@ -156,5 +156,17 @@ describe("parseAbiParams", () => {
const result = parseAbiParams(["bytes64"], [value]);
expect(result).toStrictEqual([value]);
});
it("should return a string for type string", () => {
const value = "this is a string";
const result = parseAbiParams(["string"], [value]);
expect(result).toStrictEqual(["this is a string"]);
});
it("should return the value itself if it falls through to the end", () => {
const value = { whatever: true };
const result = parseAbiParams(["non-existent-type"], [value]);
expect(result).toStrictEqual([{ whatever: true }]);
});
});
});
@@ -32,6 +32,17 @@ export function parseAbiParams(
constructorParamTypes: string[],
constructorParamValues: unknown[],
): Array<string | bigint | boolean> {
/**
* Internal Solidity type checklist
* 1. tuple, array -> JSON.parse | todo: Recursively parse the content
* 2. uint, int -> bigint
* 3. address -> string
* 4. string -> string
* 5. bytes, bytes32 etc. -> string
* 6. bool -> boolean
* >>> Make sure to return the original value at the end of the function <<<
*/
// Make sure they have the same length
if (constructorParamTypes.length !== constructorParamValues.length) {
throw new Error(
@@ -46,6 +57,9 @@ export function parseAbiParams(
}
return value;
}
if (type === "string") {
return String(value);
}
if (type === "bytes32") {
if (!isHex(value)) {
throw new Error(`${value} is not a valid hex string`);
@@ -86,5 +100,8 @@ export function parseAbiParams(
"Invalid boolean value. Expecting either 'true' or 'false'",
);
}
// Return the value here if none of the types match
return value;
});
}
@@ -8,4 +8,44 @@ describe("hex.ts", () => {
"0x0000000000000000000000000000000000000000000000000000000000000064",
);
});
/**
* This test is put here as a docs for the method `numberToHex` because:
*
* `numberToHex` can still work with number-convertible strings - even tho it only accepts number or bigint
* because there's is one line of code that's converting the param into bigint anyway
* ```ts
* const value = BigInt(value_);
* ```
*
* As a side effect, the following 3 return the same result:
* ```ts
* readContract({
* contract,
* method: "function tokenURI(uint256 tokenId) returns (string)",
* params: [1n],
* })
*
* readContract({
* contract,
* method: "function tokenURI(uint256 tokenId) returns (string)",
* // @ts-ignore
* params: [1],
* })
*
* readContract({
* contract,
* method: "function tokenURI(uint256 tokenId) returns (string)",
* // @ts-ignore
* params: ["1"],
* })
* ```
*/
it("should work with string !!!!", () => {
// @ts-ignore Intentional
const result = numberToHex("100", { size: 32, signed: false });
expect(result).toBe(
"0x0000000000000000000000000000000000000000000000000000000000000064",
);
});
});