Skip to content

Commit bf20c85

Browse files
committed
update
1 parent 46c3abb commit bf20c85

File tree

8 files changed

+285
-1
lines changed

8 files changed

+285
-1
lines changed

.changeset/stupid-buses-wink.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": minor
3+
---
4+
5+
Add 2 new Pay functions: convertFiatToCrypto and convertCryptoToFiat

packages/thirdweb/src/exports/pay.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,13 @@ export type {
6666
PayTokenInfo,
6767
PayOnChainTransactionDetails,
6868
} from "../pay/utils/commonTypes.js";
69+
70+
export {
71+
convertFiatToCrypto,
72+
type ConvertFiatToCryptoParams,
73+
} from "../pay/convert/fiatToCrypto.js";
74+
75+
export {
76+
convertCryptoToFiat,
77+
type ConvertCryptoToFiatParams,
78+
} from "../pay/convert/cryptoToFiat.js";
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { describe, expect, it } from "vitest";
2+
import { TEST_CLIENT } from "~test/test-clients.js";
3+
import { base } from "../../chains/chain-definitions/base.js";
4+
import { ethereum } from "../../chains/chain-definitions/ethereum.js";
5+
import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
6+
import { convertCryptoToFiat } from "./cryptoToFiat.js";
7+
8+
describe.runIf(process.env.TW_SECRET_KEY)("Pay: crypto-to-fiat", () => {
9+
it("should convert ETH price to USD on Ethereum mainnet", async () => {
10+
const data = await convertCryptoToFiat({
11+
chain: ethereum,
12+
fromTokenAddress: NATIVE_TOKEN_ADDRESS,
13+
fromAmount: 1,
14+
to: "usd",
15+
client: TEST_CLIENT,
16+
});
17+
expect(data.result).toBeDefined();
18+
// Should be a number
19+
expect(!Number.isNaN(data.result)).toBe(true);
20+
// Since eth is around US$3000, we can add a test to check if the price is greater than $1500 (as a safe margin)
21+
// let's hope that scenario does not happen :(
22+
expect(Number(data.result) > 1500).toBe(true);
23+
});
24+
25+
it("should convert ETH price to USD on Base mainnet", async () => {
26+
const data = await convertCryptoToFiat({
27+
chain: base,
28+
fromTokenAddress: NATIVE_TOKEN_ADDRESS,
29+
fromAmount: 1,
30+
to: "usd",
31+
client: TEST_CLIENT,
32+
});
33+
expect(data.result).toBeDefined();
34+
// Should be a number
35+
expect(!Number.isNaN(data.result)).toBe(true);
36+
// Since eth is around US$3000, we can add a test to check if the price is greater than $1500 (as a safe margin)
37+
// let's hope that scenario does not happen :(
38+
expect(Number(data.result) > 1500).toBe(true);
39+
});
40+
});
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import type { Address } from "abitype";
2+
import type { Chain } from "../../chains/types.js";
3+
import type { ThirdwebClient } from "../../client/client.js";
4+
import { getClientFetch } from "../../utils/fetch.js";
5+
import { getPayConvertCryptoToFiatEndpoint } from "../utils/definitions.js";
6+
7+
/**
8+
* Props for the `convertCryptoToFiat` function
9+
* @convertCrypto
10+
*/
11+
export type ConvertCryptoToFiatParams = {
12+
client: ThirdwebClient;
13+
/**
14+
* The contract address of the token
15+
* For native token, use NATIVE_TOKEN_ADDRESS
16+
*/
17+
fromTokenAddress: Address;
18+
/**
19+
* The amount of token to convert to fiat value
20+
*/
21+
fromAmount: number;
22+
/**
23+
* The chain that the token is deployed to
24+
*/
25+
chain: Chain;
26+
/**
27+
* The fiat symbol. e.g "usd"
28+
*/
29+
to: string;
30+
};
31+
32+
/**
33+
* Get a price of a token (using tokenAddress + chainId) in fiat.
34+
* Only USD is supported at the moment.
35+
* @example
36+
* ### Basic usage
37+
* For native token (non-ERC20), you should use NATIVE_TOKEN_ADDRESS as the value for `tokenAddress`
38+
* ```ts
39+
* import { convertCryptoToFiat } from "thirdweb/pay";
40+
*
41+
* // Get Ethereum price
42+
* const result = convertCryptoToFiat({
43+
* fromTokenAddress: NATIVE_TOKEN_ADDRESS,
44+
* // This is not case sensitive, so either "USD" or "usd" is fine
45+
* to: "USD",
46+
* chain: ethereum,
47+
* fromAmount: 1,
48+
* });
49+
*
50+
* // Result: 3404.11
51+
* ```
52+
* @convertCrypto
53+
* @returns a number representing the price (in selected fiat) of "x" token, with "x" being the `fromAmount`.
54+
*/
55+
export async function convertCryptoToFiat(
56+
options: ConvertCryptoToFiatParams,
57+
): Promise<{ result: number }> {
58+
const { client, fromTokenAddress, to, chain, fromAmount } = options;
59+
if (Number(fromAmount) === 0) {
60+
return { result: 0 };
61+
}
62+
try {
63+
// Testnets just don't work with our current provider(s)
64+
if (chain.testnet === true) {
65+
throw new Error(
66+
`Cannot fetch price for a testnet (chainId: ${chain.id})`,
67+
);
68+
}
69+
const params = {
70+
fromTokenAddress,
71+
to,
72+
chainId: String(chain.id),
73+
fromAmount: String(fromAmount),
74+
};
75+
const queryString = new URLSearchParams(params).toString();
76+
const url = `${getPayConvertCryptoToFiatEndpoint()}?${queryString}`;
77+
const response = await getClientFetch(client)(url);
78+
if (!response.ok) {
79+
response.body?.cancel();
80+
throw new Error(`HTTP error! status: ${response.status}`);
81+
}
82+
83+
const data: { result: number } = await response.json();
84+
return data;
85+
} catch (error) {
86+
console.error("Fetch error:", error);
87+
throw new Error(`Fetch failed: ${error}`);
88+
}
89+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { describe, expect, it } from "vitest";
2+
import { TEST_CLIENT } from "~test/test-clients.js";
3+
import { base } from "../../chains/chain-definitions/base.js";
4+
import { ethereum } from "../../chains/chain-definitions/ethereum.js";
5+
import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
6+
import { convertFiatToCrypto } from "./fiatToCrypto.js";
7+
8+
describe.runIf(process.env.TW_SECRET_KEY)("Pay: fiatToCrypto", () => {
9+
it("should convert fiat price to token on Ethereum mainnet", async () => {
10+
const data = await convertFiatToCrypto({
11+
chain: ethereum,
12+
from: "usd",
13+
fromAmount: 1,
14+
to: NATIVE_TOKEN_ADDRESS,
15+
client: TEST_CLIENT,
16+
});
17+
expect(data.result).toBeDefined();
18+
// Should be a number
19+
expect(!Number.isNaN(data.result)).toBe(true);
20+
// Since eth is around US$3000, 1 USD should be around 0.0003
21+
// we give it some safe margin so the test won't be flaky
22+
expect(Number(data.result) < 0.001).toBe(true);
23+
});
24+
25+
it("should convert fiat price to token on Base mainnet", async () => {
26+
const data = await convertFiatToCrypto({
27+
chain: base,
28+
from: "usd",
29+
fromAmount: 1,
30+
to: NATIVE_TOKEN_ADDRESS,
31+
client: TEST_CLIENT,
32+
});
33+
34+
expect(data.result).toBeDefined();
35+
// Should be a number
36+
expect(!Number.isNaN(data.result)).toBe(true);
37+
// Since eth is around US$3000, 1 USD should be around 0.0003
38+
// we give it some safe margin so the test won't be flaky
39+
expect(Number(data.result) < 0.001).toBe(true);
40+
});
41+
});
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import type { Address } from "abitype";
2+
import type { Chain } from "../../chains/types.js";
3+
import type { ThirdwebClient } from "../../client/client.js";
4+
import { getClientFetch } from "../../utils/fetch.js";
5+
import { getPayConvertFiatToCryptoEndpoint } from "../utils/definitions.js";
6+
7+
/**
8+
* Props for the `convertFiatToCrypto` function
9+
* @convertFiat
10+
*/
11+
export type ConvertFiatToCryptoParams = {
12+
client: ThirdwebClient;
13+
/**
14+
* The fiat symbol. e.g: "USD"
15+
* Currently only USD is supported.
16+
*/
17+
from: string;
18+
/**
19+
* The total amount of fiat to convert
20+
* e.g: If you want to convert 2 cents to USD, enter `0.02`
21+
*/
22+
fromAmount: number;
23+
/**
24+
* The token address
25+
* For native token, use NATIVE_TOKEN_ADDRESS
26+
*/
27+
to: Address;
28+
/**
29+
* The chain that the token is deployed to
30+
*/
31+
chain: Chain;
32+
};
33+
34+
/**
35+
* Convert a fiat value to a token.
36+
* Currently only USD is supported.
37+
* @example
38+
* ### Basic usage
39+
* ```ts
40+
* import { convertFiatToCrypto } from "thirdweb/pay";
41+
*
42+
* // Convert 2 cents to ETH
43+
* const result = await convertFiatToCrypto({
44+
* // this is not case-sensitive, so "USD" is fine
45+
* from: "usd",
46+
* // the token address. For native token, use NATIVE_TOKEN_ADDRESS
47+
* to: "0x...",
48+
* // the chain (of the chain where the token belong to)
49+
* chain: ethereum,
50+
* // 2 cents
51+
* fromAmount: 0.02,
52+
* });
53+
* ```
54+
* Result: `0.0000057` (a number)
55+
* @convertFiat
56+
*/
57+
export async function convertFiatToCrypto(
58+
options: ConvertFiatToCryptoParams,
59+
): Promise<{ result: number }> {
60+
const { client, from, to, chain, fromAmount } = options;
61+
if (Number(fromAmount) === 0) {
62+
return { result: 0 };
63+
}
64+
try {
65+
// Testnets just don't work with our current provider(s)
66+
if (chain.testnet === true) {
67+
throw new Error(
68+
`Cannot fetch price for a testnet (chainId: ${chain.id})`,
69+
);
70+
}
71+
const params = {
72+
from,
73+
to,
74+
chainId: String(chain.id),
75+
fromAmount: String(fromAmount),
76+
};
77+
const queryString = new URLSearchParams(params).toString();
78+
const url = `${getPayConvertFiatToCryptoEndpoint()}?${queryString}`;
79+
const response = await getClientFetch(client)(url);
80+
if (!response.ok) {
81+
response.body?.cancel();
82+
throw new Error(`HTTP error! status: ${response.status}`);
83+
}
84+
85+
const data: { result: number } = await response.json();
86+
return data;
87+
} catch (error) {
88+
console.error("Fetch error:", error);
89+
throw new Error(`Fetch failed: ${error}`);
90+
}
91+
}

packages/thirdweb/src/pay/utils/definitions.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,9 @@ export const getPaySupportedSources = () =>
7676
*/
7777
export const getPayBuyHistoryEndpoint = () =>
7878
`${getPayBaseUrl()}/wallet/history/v1`;
79+
80+
export const getPayConvertFiatToCryptoEndpoint = () =>
81+
`${getPayBaseUrl()}/convert/fiat-to-crypto/v1`;
82+
83+
export const getPayConvertCryptoToFiatEndpoint = () =>
84+
`${getPayBaseUrl()}/convert/crypto-to-fiat/v1`;

packages/thirdweb/tsdoc.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@
113113
"@client": true,
114114
"@nft": true,
115115
"@account": true,
116-
"@beta": true
116+
"@beta": true,
117+
"@convertCrypto": true,
118+
"@convertFiat": true
117119
}
118120
}

0 commit comments

Comments
 (0)