Skip to content

Commit cd62b88

Browse files
deepti-imxZacharyCouchmansharonsheahimx-mikhala
authored
[WT-1686] Add on-ramp widget (#770)
Co-authored-by: Zach Couchman <[email protected]> Co-authored-by: Sharon Sheah <[email protected]> Co-authored-by: Mikhala <[email protected]>
1 parent e0c7e16 commit cd62b88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+3751
-330
lines changed

packages/checkout/sdk/src/Checkout.test.ts

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { ExternalProvider, Web3Provider } from '@ethersproject/providers';
55
import { Environment } from '@imtbl/config';
66
import { BigNumber, ethers } from 'ethers';
7+
import { Passport, UserProfile } from '@imtbl/passport';
78
import { getNetworkAllowList, getNetworkInfo, switchWalletNetwork } from './network';
89

910
import { Checkout } from './Checkout';
@@ -22,6 +23,9 @@ import {
2223
ItemType,
2324
TransactionOrGasType,
2425
SmartCheckoutParams,
26+
NetworkInfo,
27+
GetTokenAllowListResult,
28+
TokenInfo,
2529
} from './types';
2630
import { getAllBalances, getBalance, getERC20Balance } from './balances';
2731
import { sendTransaction } from './transaction';
@@ -36,6 +40,8 @@ import { buy } from './smartCheckout/buy';
3640
import { sell } from './smartCheckout/sell';
3741
import { smartCheckout } from './smartCheckout';
3842
import { cancel } from './smartCheckout/cancel';
43+
import { FiatRampService } from './fiatRamp';
44+
import { FiatRampParams, ExchangeType } from './types/fiatRamp';
3945

4046
jest.mock('./connect');
4147
jest.mock('./network');
@@ -50,6 +56,7 @@ jest.mock('./smartCheckout/buy');
5056
jest.mock('./smartCheckout/sell');
5157
jest.mock('./smartCheckout/cancel');
5258
jest.mock('./smartCheckout');
59+
jest.mock('./fiatRamp');
5360

5461
describe('Connect', () => {
5562
let providerMock: ExternalProvider;
@@ -629,4 +636,189 @@ describe('Connect', () => {
629636
const result = await Checkout.isWeb3Provider(new Web3Provider(providerMock, ChainId.ETHEREUM));
630637
expect(result).toBeTruthy();
631638
});
639+
640+
describe('createFiatRampUrl', () => {
641+
let createWidgetUrlMock: jest.Mock;
642+
let checkout: Checkout;
643+
let mockProvider: Web3Provider;
644+
let networkInfoResult: NetworkInfo;
645+
let getTokenAllowListResult: GetTokenAllowListResult;
646+
647+
const defaultWidgetUrl = 'https://global-stg.transak.com?apiKey=41ad2da7-ed5a-4d89-a90b-c751865effc2'
648+
+ '&network=immutablezkevm&defaultPaymentMethod=credit_debit_card&disablePaymentMethods='
649+
+ 'sepa_bank_transfer,gbp_bank_transfer,pm_cash_app,pm_jwire,pm_paymaya,pm_bpi,pm_ubp,pm_grabpay,'
650+
+ 'pm_shopeepay,pm_gcash,pm_pix,pm_astropay,pm_pse,inr_bank_transfer&productsAvailed=buy'
651+
+ '&exchangeScreenTitle=Buy&themeColor=0D0D0D';
652+
653+
beforeEach(() => {
654+
createWidgetUrlMock = jest.fn().mockResolvedValue(defaultWidgetUrl);
655+
(FiatRampService as jest.Mock).mockReturnValue({
656+
createWidgetUrl: createWidgetUrlMock,
657+
});
658+
659+
mockProvider = {
660+
getSigner: jest.fn().mockReturnValue({
661+
getAddress: jest.fn().mockResolvedValue('0xADDRESS'),
662+
}),
663+
network: {
664+
chainId: ChainId.ETHEREUM,
665+
},
666+
} as unknown as Web3Provider;
667+
668+
networkInfoResult = {
669+
name: ChainName.ETHEREUM,
670+
chainId: ChainId.ETHEREUM,
671+
nativeCurrency: {
672+
name: 'ETHEREUM',
673+
symbol: 'ETH',
674+
decimals: 18,
675+
},
676+
isSupported: true,
677+
};
678+
(getNetworkInfo as jest.Mock).mockResolvedValue(networkInfoResult);
679+
680+
getTokenAllowListResult = {
681+
tokens: [],
682+
};
683+
(getTokenAllowList as jest.Mock).mockResolvedValue(getTokenAllowListResult);
684+
685+
checkout = new Checkout({
686+
baseConfig: { environment: Environment.PRODUCTION },
687+
});
688+
});
689+
690+
it(`should call FiatRampService.createWidgetUrl with correct params
691+
when only onRampProvider, exchangeType and web3Provider are provided`, async () => {
692+
const params: FiatRampParams = {
693+
exchangeType: ExchangeType.ONRAMP,
694+
web3Provider: mockProvider,
695+
};
696+
697+
await checkout.createFiatRampUrl(params);
698+
699+
expect(createWidgetUrlMock).toBeCalledTimes(1);
700+
expect(createWidgetUrlMock).toBeCalledWith({
701+
exchangeType: ExchangeType.ONRAMP,
702+
isPassport: false,
703+
walletAddress: '0xADDRESS',
704+
tokenAmount: undefined,
705+
tokenSymbol: 'IMX',
706+
email: undefined,
707+
});
708+
});
709+
710+
it(`should call fiatRampService.createWidgetUrl with correct params
711+
when tokenAmount and tokenSymbol are provided`, async () => {
712+
getTokenAllowListResult = {
713+
tokens: [
714+
{
715+
name: 'Immutable X',
716+
address: '0xaddr',
717+
symbol: 'IMX',
718+
decimals: 18,
719+
} as TokenInfo,
720+
{
721+
name: 'Ethereum',
722+
address: '0xethAddr',
723+
symbol: 'ETH',
724+
decimals: 18,
725+
} as TokenInfo,
726+
{
727+
name: 'Matic',
728+
address: '0xmaticAddr',
729+
symbol: 'MATIC',
730+
decimals: '18',
731+
},
732+
],
733+
} as GetTokenAllowListResult;
734+
(getTokenAllowList as jest.Mock).mockResolvedValue(getTokenAllowListResult);
735+
736+
const params: FiatRampParams = {
737+
exchangeType: ExchangeType.ONRAMP,
738+
web3Provider: mockProvider,
739+
tokenAmount: '10',
740+
tokenAddress: '0xethAddr',
741+
};
742+
743+
await checkout.createFiatRampUrl(params);
744+
745+
expect(createWidgetUrlMock).toBeCalledTimes(1);
746+
expect(createWidgetUrlMock).toBeCalledWith({
747+
exchangeType: ExchangeType.ONRAMP,
748+
isPassport: false,
749+
walletAddress: '0xADDRESS',
750+
tokenAmount: '10',
751+
tokenSymbol: 'ETH',
752+
email: undefined,
753+
});
754+
});
755+
756+
it(`should call fiatRampService.createWidgetUrl with correct params
757+
when passport is provided`, async () => {
758+
mockProvider = {
759+
getSigner: jest.fn().mockReturnValue({
760+
getAddress: jest.fn().mockResolvedValue('0xADDRESS'),
761+
}),
762+
network: {
763+
chainId: ChainId.IMTBL_ZKEVM_TESTNET,
764+
},
765+
provider: {
766+
isPassport: true,
767+
},
768+
} as unknown as Web3Provider;
769+
const mockUser: UserProfile = {
770+
sub: 'email|123',
771+
772+
};
773+
const mockPassport = {
774+
getUserInfo: jest.fn().mockResolvedValue(mockUser),
775+
} as unknown as Passport;
776+
777+
const params: FiatRampParams = {
778+
exchangeType: ExchangeType.ONRAMP,
779+
web3Provider: mockProvider,
780+
passport: mockPassport,
781+
};
782+
783+
await checkout.createFiatRampUrl(params);
784+
785+
expect(createWidgetUrlMock).toBeCalledTimes(1);
786+
expect(createWidgetUrlMock).toBeCalledWith({
787+
exchangeType: ExchangeType.ONRAMP,
788+
isPassport: true,
789+
walletAddress: '0xADDRESS',
790+
tokenAmount: undefined,
791+
tokenSymbol: 'IMX',
792+
email: mockUser.email,
793+
});
794+
});
795+
});
796+
797+
describe('getExchangeFeeEstimate', () => {
798+
let feeEstimateMock: jest.Mock;
799+
let checkout: Checkout;
800+
801+
const feeEstimate = {
802+
minPercentage: '3.5',
803+
maxPercentage: '5.5',
804+
feePercentage: undefined,
805+
};
806+
807+
beforeEach(() => {
808+
feeEstimateMock = jest.fn().mockResolvedValue(feeEstimate);
809+
(FiatRampService as jest.Mock).mockReturnValue({
810+
feeEstimate: feeEstimateMock,
811+
});
812+
813+
checkout = new Checkout({
814+
baseConfig: { environment: Environment.PRODUCTION },
815+
});
816+
});
817+
818+
it('should call fiatRampService.getExchangeFeeEstimate', async () => {
819+
await checkout.getExchangeFeeEstimate();
820+
821+
expect(feeEstimateMock).toBeCalledTimes(1);
822+
});
823+
});
632824
});

packages/checkout/sdk/src/Checkout.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,15 @@ import {
4545
GasEstimateSwapResult,
4646
GasEstimateBridgeToL2Result,
4747
SmartCheckoutParams,
48+
TokenFilterTypes,
49+
OnRampProviderFees,
50+
FiatRampParams,
4851
} from './types';
4952
import { CheckoutConfiguration } from './config';
5053
import { createReadOnlyProviders } from './readOnlyProviders/readOnlyProvider';
5154
import { SellParams } from './types/sell';
5255
import { CancelParams } from './types/cancel';
56+
import { FiatRampService, FiatRampWidgetParams } from './fiatRamp';
5357

5458
const SANDBOX_CONFIGURATION = {
5559
baseConfig: {
@@ -60,6 +64,8 @@ const SANDBOX_CONFIGURATION = {
6064
export class Checkout {
6165
readonly config: CheckoutConfiguration;
6266

67+
readonly fiatRampService: FiatRampService;
68+
6369
private readOnlyProviders: Map<ChainId, ethers.providers.JsonRpcProvider>;
6470

6571
/**
@@ -70,6 +76,7 @@ export class Checkout {
7076
config: CheckoutModuleConfiguration = SANDBOX_CONFIGURATION,
7177
) {
7278
this.config = new CheckoutConfiguration(config);
79+
this.fiatRampService = new FiatRampService(this.config);
7380
this.readOnlyProviders = new Map<ChainId, ethers.providers.JsonRpcProvider>();
7481
}
7582

@@ -397,4 +404,47 @@ export class Checkout {
397404
this.config,
398405
);
399406
}
407+
408+
/**
409+
* Creates and returns a URL for the fiat ramp widget.
410+
* @param {FiatRampParams} params - The parameters for creating the url.
411+
* @returns {Promise<string>} - A promise that resolves to a string url.
412+
*/
413+
public async createFiatRampUrl(params: FiatRampParams): Promise<string> {
414+
let tokenAmount;
415+
let tokenSymbol = 'IMX';
416+
let email;
417+
418+
const walletAddress = await params.web3Provider.getSigner().getAddress();
419+
const isPassport = (params.web3Provider.provider as any)?.isPassport || false;
420+
421+
if (isPassport && params.passport) {
422+
const userInfo = await params.passport.getUserInfo();
423+
email = userInfo?.email;
424+
}
425+
426+
const tokenList = await tokens.getTokenAllowList(this.config, { type: TokenFilterTypes.ONRAMP });
427+
const token = tokenList.tokens.find((t) => t.address?.toLowerCase() === params.tokenAddress?.toLowerCase());
428+
if (token) {
429+
tokenAmount = params.tokenAmount;
430+
tokenSymbol = token.symbol;
431+
}
432+
433+
return await this.fiatRampService.createWidgetUrl({
434+
exchangeType: params.exchangeType,
435+
isPassport,
436+
walletAddress,
437+
tokenAmount,
438+
tokenSymbol,
439+
email,
440+
} as FiatRampWidgetParams);
441+
}
442+
443+
/**
444+
* Fetches fiat ramp fee estimations.
445+
* @returns {Promise<OnRampProviderFees>} - A promise that resolves to OnRampProviderFees.
446+
*/
447+
public async getExchangeFeeEstimate(): Promise<OnRampProviderFees> {
448+
return await this.fiatRampService.feeEstimate();
449+
}
400450
}

0 commit comments

Comments
 (0)