Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/bindings/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class Client extends ContractClient {
const name = sanitizeIdentifier(func.name().toString());
const inputs = func.inputs().map((input: any) => ({
name: input.name().toString(),
type: parseTypeFromTypeDef(input.type()),
type: parseTypeFromTypeDef(input.type(), true),
}));
const outputType =
func.outputs().length > 0
Expand Down Expand Up @@ -136,7 +136,7 @@ export class Client extends ContractClient {
}
const inputs = constructorFunc.inputs().map((input) => ({
name: input.name().toString(),
type: parseTypeFromTypeDef(input.type()),
type: parseTypeFromTypeDef(input.type(), true),
}));

const params = this.formatConstructorParameters(inputs);
Expand Down
49 changes: 39 additions & 10 deletions src/bindings/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ export function sanitizeIdentifier(identifier: string): string {
/**
* Generate TypeScript type from XDR type definition
*/
export function parseTypeFromTypeDef(typeDef: xdr.ScSpecTypeDef): string {
export function parseTypeFromTypeDef(
typeDef: xdr.ScSpecTypeDef,
isFunctionInput = false,
): string {
switch (typeDef.switch()) {
case xdr.ScSpecType.scSpecTypeVal():
return "any";
Expand Down Expand Up @@ -109,22 +112,39 @@ export function parseTypeFromTypeDef(typeDef: xdr.ScSpecTypeDef): string {
case xdr.ScSpecType.scSpecTypeSymbol():
return "string";
case xdr.ScSpecType.scSpecTypeAddress():
case xdr.ScSpecType.scSpecTypeMuxedAddress():
return "string | Address";
case xdr.ScSpecType.scSpecTypeMuxedAddress(): {
// function inputs can accept either string or Address
if (isFunctionInput) {
return "string | Address";
}
// Otherwise for backward compatibility use string
return "string";
}
case xdr.ScSpecType.scSpecTypeVec(): {
const vecType = parseTypeFromTypeDef(typeDef.vec().elementType());
const vecType = parseTypeFromTypeDef(
typeDef.vec().elementType(),
isFunctionInput,
);
return `Array<${vecType}>`;
}
case xdr.ScSpecType.scSpecTypeMap(): {
const keyType = parseTypeFromTypeDef(typeDef.map().keyType());
const valueType = parseTypeFromTypeDef(typeDef.map().valueType());
const keyType = parseTypeFromTypeDef(
typeDef.map().keyType(),
isFunctionInput,
);
const valueType = parseTypeFromTypeDef(
typeDef.map().valueType(),
isFunctionInput,
);
return `Map<${keyType}, ${valueType}>`;
}
case xdr.ScSpecType.scSpecTypeTuple(): {
const tupleTypes = typeDef
.tuple()
.valueTypes()
.map((t: xdr.ScSpecTypeDef) => parseTypeFromTypeDef(t));
.map((t: xdr.ScSpecTypeDef) =>
parseTypeFromTypeDef(t, isFunctionInput),
);
return `[${tupleTypes.join(", ")}]`;
}
case xdr.ScSpecType.scSpecTypeOption(): {
Expand All @@ -135,13 +155,22 @@ export function parseTypeFromTypeDef(typeDef: xdr.ScSpecTypeDef): string {
) {
typeDef = typeDef.option().valueType();
}
const optionType = parseTypeFromTypeDef(typeDef.option().valueType());
const optionType = parseTypeFromTypeDef(
typeDef.option().valueType(),
isFunctionInput,
);

return `${optionType} | null`;
}
case xdr.ScSpecType.scSpecTypeResult(): {
const okType = parseTypeFromTypeDef(typeDef.result().okType());
const errorType = parseTypeFromTypeDef(typeDef.result().errorType());
const okType = parseTypeFromTypeDef(
typeDef.result().okType(),
isFunctionInput,
);
const errorType = parseTypeFromTypeDef(
typeDef.result().errorType(),
isFunctionInput,
);
return `Result<${okType}, ${errorType}>`;
}
case xdr.ScSpecType.scSpecTypeUdt(): {
Expand Down
110 changes: 67 additions & 43 deletions test/integration/bindings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,84 +170,100 @@ describe("BindingGenerator", () => {
{
name: "address",
type: xdr.ScSpecTypeDef.scSpecTypeAddress(),
expected: "string | Address",
expectedOutput: "string",
expectedInput: "string | Address",
},
{
name: "bool",
type: xdr.ScSpecTypeDef.scSpecTypeBool(),
expected: "boolean",
expectedOutput: "boolean",
expectedInput: "boolean",
},
{
name: "bytes",
type: xdr.ScSpecTypeDef.scSpecTypeBytes(),
expected: "Buffer",
expectedOutput: "Buffer",
expectedInput: "Buffer",
},
{
name: "bytesN",
type: xdr.ScSpecTypeDef.scSpecTypeBytesN(
new xdr.ScSpecTypeBytesN({ n: 32 }),
),
expected: "Buffer",
expectedOutput: "Buffer",
expectedInput: "Buffer",
},
{
name: "duration",
type: xdr.ScSpecTypeDef.scSpecTypeDuration(),
expected: "bigint",
expectedOutput: "bigint",
expectedInput: "bigint",
},
{
name: "i128",
type: xdr.ScSpecTypeDef.scSpecTypeI128(),
expected: "bigint",
expectedOutput: "bigint",
expectedInput: "bigint",
},
{
name: "i256",
type: xdr.ScSpecTypeDef.scSpecTypeI256(),
expected: "bigint",
expectedOutput: "bigint",
expectedInput: "bigint",
},
{
name: "i32",
type: xdr.ScSpecTypeDef.scSpecTypeI32(),
expected: "number",
expectedOutput: "number",
expectedInput: "number",
},
{
name: "i64",
type: xdr.ScSpecTypeDef.scSpecTypeI64(),
expected: "bigint",
expectedOutput: "bigint",
expectedInput: "bigint",
},
{
name: "string",
type: xdr.ScSpecTypeDef.scSpecTypeString(),
expected: "string",
expectedOutput: "string",
expectedInput: "string",
},
{
name: "symbol",
type: xdr.ScSpecTypeDef.scSpecTypeSymbol(),
expected: "string",
expectedOutput: "string",
expectedInput: "string",
},
{
name: "timepoint",
type: xdr.ScSpecTypeDef.scSpecTypeTimepoint(),
expected: "bigint",
expectedOutput: "bigint",
expectedInput: "bigint",
},
{
name: "u128",
type: xdr.ScSpecTypeDef.scSpecTypeU128(),
expected: "bigint",
expectedOutput: "bigint",
expectedInput: "bigint",
},
{
name: "u256",
type: xdr.ScSpecTypeDef.scSpecTypeU256(),
expected: "bigint",
expectedOutput: "bigint",
expectedInput: "bigint",
},
{
name: "u32",
type: xdr.ScSpecTypeDef.scSpecTypeU32(),
expected: "number",
expectedOutput: "number",
expectedInput: "number",
},
{
name: "u64",
type: xdr.ScSpecTypeDef.scSpecTypeU64(),
expected: "bigint",
expectedOutput: "bigint",
expectedInput: "bigint",
},
{
name: "vec",
Expand All @@ -256,7 +272,8 @@ describe("BindingGenerator", () => {
elementType: xdr.ScSpecTypeDef.scSpecTypeU32(),
}),
),
expected: "Array<number>",
expectedOutput: "Array<number>",
expectedInput: "Array<number>",
},
{
name: "map",
Expand All @@ -266,7 +283,8 @@ describe("BindingGenerator", () => {
valueType: xdr.ScSpecTypeDef.scSpecTypeU32(),
}),
),
expected: "Map<string, number>",
expectedOutput: "Map<string, number>",
expectedInput: "Map<string, number>",
},
{
name: "option",
Expand All @@ -275,7 +293,8 @@ describe("BindingGenerator", () => {
valueType: xdr.ScSpecTypeDef.scSpecTypeU32(),
}),
),
expected: "number | null",
expectedOutput: "number | null",
expectedInput: "number | null",
},
{
name: "result",
Expand All @@ -285,7 +304,8 @@ describe("BindingGenerator", () => {
errorType: xdr.ScSpecTypeDef.scSpecTypeString(),
}),
),
expected: "Result<boolean, string>",
expectedOutput: "Result<boolean, string>",
expectedInput: "Result<boolean, string>",
},
{
name: "tuple",
Expand All @@ -297,36 +317,40 @@ describe("BindingGenerator", () => {
],
}),
),
expected: "[boolean, number]",
expectedOutput: "[boolean, number]",
expectedInput: "[boolean, number]",
},
{
name: "val",
type: xdr.ScSpecTypeDef.scSpecTypeVal(),
expected: "any",
expectedOutput: "any",
expectedInput: "any",
},
];

primitiveTypes.forEach(({ name, type, expected }) => {
it(`maps ${name} input to ${expected}`, () => {
const funcSpec = createFunctionSpec("test_fn", [
{ name: "input", type },
]);
const spec = new contract.Spec([funcSpec.toXDR("base64")]);
const result =
BindingGenerator.fromSpec(spec).generate(defaultOptions);
expect(result.client).toContain(`input: ${expected}`);
});

it(`maps ${name} output to ${expected}`, () => {
const funcSpec = createFunctionSpec("test_fn", [], [type]);
const spec = new contract.Spec([funcSpec.toXDR("base64")]);
const result =
BindingGenerator.fromSpec(spec).generate(defaultOptions);
expect(result.client).toContain(
`Promise<AssembledTransaction<${expected}>>`,
);
});
});
primitiveTypes.forEach(
({ name, type, expectedOutput, expectedInput }) => {
it(`maps ${name} input to ${expectedInput}`, () => {
const funcSpec = createFunctionSpec("test_fn", [
{ name: "input", type },
]);
const spec = new contract.Spec([funcSpec.toXDR("base64")]);
const result =
BindingGenerator.fromSpec(spec).generate(defaultOptions);
expect(result.client).toContain(`input: ${expectedInput}`);
});

it(`maps ${name} output to ${expectedOutput}`, () => {
const funcSpec = createFunctionSpec("test_fn", [], [type]);
const spec = new contract.Spec([funcSpec.toXDR("base64")]);
const result =
BindingGenerator.fromSpec(spec).generate(defaultOptions);
expect(result.client).toContain(
`Promise<AssembledTransaction<${expectedOutput}>>`,
);
});
},
);

it("maps void output to null", () => {
const funcSpec = createFunctionSpec(
Expand Down
Loading