Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

All notable changes to `@fundable/sdk` are documented here.

## 0.2.1

### Fixed

- parse the Fundable backend's `{ status, data }` success envelope across wallet
authentication and sponsorship requests;
- call the browser's native `fetch` with its required global receiver.

## 0.2.0

Mainnet-readiness integration release.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fundable/sdk",
"version": "0.2.0",
"version": "0.2.1",
"private": false,
"description": "Multichain TypeScript SDK for Fundable Protocol; Stellar adapter included",
"license": "MIT",
Expand Down
34 changes: 29 additions & 5 deletions src/stellar/sponsorship-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,31 @@ function jsonResponse(body: object, status = 200): Response {
});
}

function successfulResponse(data: object, status = 200): Response {
return jsonResponse({ status: true, data }, status);
}

describe("StellarSponsorshipClient", () => {
it("authenticates a wallet and uses the bearer session for sponsorship", async () => {
const fetcher = vi
.fn<typeof fetch>()
.mockResolvedValueOnce(
jsonResponse({
successfulResponse({
challenge_id: "challenge-id",
message: "Sign this Fundable challenge",
expires_at: "2030-01-01T00:00:00.000Z",
}),
)
.mockResolvedValueOnce(
jsonResponse({
successfulResponse({
access_token: "session-token",
token_type: "Bearer",
expires_at: "2030-01-01T00:15:00.000Z",
wallet: { address: "GACCOUNT", network: "TESTNET" },
}),
)
.mockResolvedValueOnce(
jsonResponse({
successfulResponse({
fee_token: "CFEE",
fee_in_token: "123",
fee_in_token_ui: "0.0000123",
Expand Down Expand Up @@ -66,7 +70,7 @@ describe("StellarSponsorshipClient", () => {
const fetcher = vi
.fn<typeof fetch>()
.mockResolvedValueOnce(
jsonResponse({
successfulResponse({
transaction_xdr: "built-xdr",
user_auth_entry: "auth-entry",
fee_token: "CFEE",
Expand All @@ -79,7 +83,7 @@ describe("StellarSponsorshipClient", () => {
}),
)
.mockResolvedValueOnce(
jsonResponse({
successfulResponse({
intentId: "intent-1",
submissionId: "submission-1",
status: "pending",
Expand Down Expand Up @@ -121,6 +125,26 @@ describe("StellarSponsorshipClient", () => {
);
});

it("keeps compatibility with unwrapped successful responses", async () => {
const fetcher = vi.fn<typeof fetch>().mockResolvedValueOnce(
jsonResponse({
challenge_id: "challenge-id",
message: "Sign this Fundable challenge",
expires_at: "2030-01-01T00:00:00.000Z",
}),
);
const client = new StellarSponsorshipClient({
backendUrl: "https://api.example.com",
fetch: fetcher,
});

await expect(client.createChallenge("GACCOUNT", "TESTNET")).resolves.toEqual({
challengeId: "challenge-id",
message: "Sign this Fundable challenge",
expiresAt: "2030-01-01T00:00:00.000Z",
});
});

it("rejects expired builds before calling the backend", async () => {
const fetcher = vi.fn<typeof fetch>();
const client = new StellarSponsorshipClient({
Expand Down
11 changes: 9 additions & 2 deletions src/stellar/sponsorship-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ function optionalString(value: unknown): string | null {
return typeof value === "string" && value.trim() ? value : null;
}

function unwrapResponseData(payload: Record<string, any>): Record<string, any> {
const data = payload.data;
return payload.status === true && data !== null && typeof data === "object" && !Array.isArray(data)
? data
: payload;
}

export class StellarSponsorshipClient {
private readonly fetcher: typeof fetch;
private readonly backendUrl: string;
Expand All @@ -159,7 +166,7 @@ export class StellarSponsorshipClient {
this.backendUrl = config.backendUrl.replace(/\/$/, "");
this.accessToken = config.accessToken;
this.headers = config.headers ?? {};
this.fetcher = config.fetch ?? fetch;
this.fetcher = config.fetch ?? ((input, init) => globalThis.fetch(input, init));
}

setAccessToken(accessToken: string | undefined): void {
Expand Down Expand Up @@ -331,6 +338,6 @@ export class StellarSponsorshipClient {
chain: "stellar",
});
}
return payload;
return unwrapResponseData(payload);
}
}
Loading