Skip to content

Commit

Permalink
Merge branch 'main' into wt-1521
Browse files Browse the repository at this point in the history
  • Loading branch information
deepti-imx committed Aug 1, 2023
2 parents 7d770fc + dde5150 commit a83a9f9
Show file tree
Hide file tree
Showing 153 changed files with 7,694 additions and 3,310 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules/
dist/
coverage/
.idea/
.DS_Store
.vscode
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- @imtbl/blockchain-data: Added Deposit and Withdrawal activity types

### Fixed

- @imtbl/immutablex_client: Updated to use core-sdk v2.0.2 with grind key fixes.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TransactionResponse } from '@ethersproject/providers';
import {
ApproveBridgeResponse,
ApproveDepositBridgeResponse,
BridgeDepositResponse,
} from '@imtbl/bridge-sdk';
import { TokenInfo } from '@imtbl/checkout-sdk';
Expand Down Expand Up @@ -55,7 +55,7 @@ interface BridgeInProgressView extends ViewType {
}

export interface ApproveERC20BridgeData {
approveTransaction: ApproveBridgeResponse;
approveTransaction: ApproveDepositBridgeResponse;
transaction: BridgeDepositResponse;
bridgeFormInfo: PrefilledBridgeForm;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { NATIVE } from '../constants';
import { isValidAddress, isValidAmount, isValidWalletProvider } from './widgetValidators';

describe('widget validators', () => {
describe('Wallet Provider Validator', () => {
it('should return true for "metamask"', () => {
const result = isValidWalletProvider('metamask');
expect(result).toBeTruthy();
});

it('should return false for "METAMASK" (all uppercase)', () => {
const result = isValidWalletProvider('METAMASK');
expect(result).toBeFalsy();
});

it('should return false for undefined', () => {
const result = isValidWalletProvider(undefined);
expect(result).toBeFalsy();
});

it('should return false for "metramask" ', () => {
const result = isValidWalletProvider('metramask');
expect(result).toBeFalsy();
});

it('should return false for empty string', () => {
const result = isValidWalletProvider('');
expect(result).toBeFalsy();
});
});

describe('Amount Validator', () => {
const validCases = ['1', '1.0', '1.234567', '100000000', '']; // empty amount should pass as valid
const invalidCases = ['acdas', '0.1234s', '1.2345678', undefined];

validCases.forEach((testCase) => {
it(`should validate amount as a float with 6 decimal places for ${testCase}`, () => {
const result = isValidAmount(testCase);
expect(result).toBeTruthy();
});
});

invalidCases.forEach((testCase) => {
it(`should return false for any amount not a float with 6 decimal places for ${testCase}`, () => {
const result = isValidAmount(testCase);
expect(result).toBeFalsy();
});
});
});

describe('Address Validator', () => {
const IMX_L1_ADDRESS = '0xF57e7e7C23978C3cAEC3C3548E3D615c346e79fF';
const INVALID_ADDRESS = '0x1234567890';

it('should return true if address is valid', () => {
const result = isValidAddress(IMX_L1_ADDRESS);
expect(result).toBeTruthy();
});

it('should return true if address is valid and lowercase', () => {
const result = isValidAddress(IMX_L1_ADDRESS.toLowerCase());
expect(result).toBeTruthy();
});

it('should return true if address is NATIVE string', () => {
const result = isValidAddress(NATIVE);
expect(result).toBeTruthy();
});

it('should return true if address is NATIVE string lowercased', () => {
const result = isValidAddress(NATIVE.toLowerCase());
expect(result).toBeTruthy();
});

it('should return false if address is valid and uppercase', () => {
const result = isValidAddress(IMX_L1_ADDRESS.toUpperCase());
expect(result).toBeFalsy();
});

it('should return false if address is not valid', () => {
const result = isValidAddress(INVALID_ADDRESS);
expect(result).toBeFalsy();
});

it('should return true if address empty', () => {
const result = isValidAddress('');
expect(result).toBeTruthy();
});

it('should return false if address is undefined', () => {
const result = isValidAddress(undefined);
expect(result).toBeFalsy();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { WalletProviderName } from '@imtbl/checkout-sdk';
import { isAddress } from 'ethers/lib/utils';
import { amountInputValidation } from './amountInputValidations';
import { NATIVE } from '../constants';

export function isValidWalletProvider(walletProvider: string | undefined) {
if (walletProvider === undefined) return false;
if (walletProvider === '') return false;
return Object.values(WalletProviderName).includes(walletProvider as WalletProviderName);
}

export function isValidAmount(amount: string | undefined) {
if (amount === undefined) return false;
if (amount === '') return true; // let empty string pass through
if (!parseFloat(amount)) return false;
if (Number.isNaN(parseFloat(amount))) return false;

return amountInputValidation(amount);
}

export function isValidAddress(address: string | undefined) {
if (address === undefined) return false;
if (address === '') return true;
if (address === NATIVE || address === NATIVE.toLowerCase()) return true;
return isAddress(address);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function ErrorView({
)}
heroContent={<SatelliteHero />}
floatHeader
testId="error-view"
>
<SimpleTextBody heading={errorText.heading}>
{errorText.body[0]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ export abstract class ImmutableWebComponent extends HTMLElement {
this.renderWidget();
}

attributeChangedCallback(name, oldValue, newValue) {
attributeChangedCallback(name: string, oldValue, newValue: any) {
if (name === 'widgetConfig') {
this.widgetConfig = this.parseWidgetConfig(newValue);
this.updateCheckoutConfig();
} else {
this[name] = newValue;
this[name] = (newValue as string)?.toLowerCase();
}
this.renderWidget();
}
Expand Down Expand Up @@ -55,4 +55,5 @@ export abstract class ImmutableWebComponent extends HTMLElement {
}

abstract renderWidget(): void;
abstract validateInputs(): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ImmutableWebComponent } from '../ImmutableWebComponent';
import { ConnectTargetLayer, getL1ChainId } from '../../lib';
import { ConnectLoader, ConnectLoaderParams } from '../../components/ConnectLoader/ConnectLoader';
import { sendBridgeWidgetCloseEvent } from './BridgeWidgetEvents';
import { isValidAddress, isValidAmount, isValidWalletProvider } from '../../lib/validations/widgetValidators';

export class ImmutableBridge extends ImmutableWebComponent {
fromContractAddress = '';
Expand All @@ -16,15 +17,38 @@ export class ImmutableBridge extends ImmutableWebComponent {

connectedCallback() {
super.connectedCallback();
this.fromContractAddress = this.getAttribute('fromContractAddress') as string;
this.amount = this.getAttribute('amount') as string;
this.fromContractAddress = this.getAttribute('fromContractAddress')?.toLowerCase() ?? '';
this.amount = this.getAttribute('amount') ?? '';
this.walletProvider = this.getAttribute(
'walletProvider',
) as WalletProviderName;
)?.toLowerCase() as WalletProviderName ?? WalletProviderName.METAMASK;

this.renderWidget();
}

validateInputs(): void {
if (!isValidWalletProvider(this.walletProvider)) {
// eslint-disable-next-line no-console
console.warn('[IMTBL]: invalid "walletProvider" widget input');
this.walletProvider = WalletProviderName.METAMASK;
}

if (!isValidAmount(this.amount)) {
// eslint-disable-next-line no-console
console.warn('[IMTBL]: invalid "amount" widget input');
this.amount = '';
}

if (!isValidAddress(this.fromContractAddress)) {
// eslint-disable-next-line no-console
console.warn('[IMTBL]: invalid "fromContractAddress" widget input');
this.fromContractAddress = '';
}
}

renderWidget() {
this.validateInputs();

const connectLoaderParams: ConnectLoaderParams = {
targetLayer: ConnectTargetLayer.LAYER1,
walletProvider: this.walletProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ function BridgeWebView() {
};

return (
<imtbl-bridge widgetConfig={JSON.stringify(config)} walletProvider="metamask" />
<imtbl-bridge
widgetConfig={JSON.stringify(config)}
walletProvider="metamask"
/>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ describe('Bridge Widget tests', () => {

describe('Bridge Submit', () => {
beforeEach(() => {
cy.stub(TokenBridge.prototype, 'getUnsignedApproveBridgeTx').as('getUnsignedApproveBridgeTxStub')
cy.stub(TokenBridge.prototype, 'getUnsignedApproveDepositBridgeTx').as('getUnsignedApproveDepositBridgeTxStub')
.resolves({
required: true,
unsignedTx: {},
Expand Down Expand Up @@ -333,7 +333,7 @@ describe('Bridge Widget tests', () => {
cySmartGet('bridge-amount-text__input').blur();
cySmartGet('bridge-form-button').click();

cySmartGet('@getUnsignedApproveBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedApproveDepositBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedDepositTxStub').should('have.been.calledOnce');

cySmartGet('simple-text-body__heading').should('have.text', approveSpending.content.heading);
Expand Down Expand Up @@ -413,7 +413,7 @@ describe('Bridge Widget tests', () => {
cySmartGet('bridge-amount-text__input').blur();
cySmartGet('bridge-form-button').click();

cySmartGet('@getUnsignedApproveBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedApproveDepositBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedDepositTxStub').should('have.been.calledOnce');

cySmartGet('footer-button').click();
Expand Down Expand Up @@ -464,7 +464,7 @@ describe('Bridge Widget tests', () => {
cySmartGet('bridge-amount-text__input').blur();
cySmartGet('bridge-form-button').click();

cySmartGet('@getUnsignedApproveBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedApproveDepositBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedDepositTxStub').should('have.been.calledOnce');

cySmartGet('footer-button').click();
Expand Down Expand Up @@ -521,7 +521,7 @@ describe('Bridge Widget tests', () => {
cySmartGet('bridge-amount-text__input').blur();
cySmartGet('bridge-form-button').click();

cySmartGet('@getUnsignedApproveBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedApproveDepositBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedDepositTxStub').should('have.been.calledOnce');

cySmartGet('footer-button').click();
Expand Down Expand Up @@ -575,7 +575,7 @@ describe('Bridge Widget tests', () => {
cySmartGet('bridge-amount-text__input').blur();
cySmartGet('bridge-form-button').click();

cySmartGet('@getUnsignedApproveBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedApproveDepositBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedDepositTxStub').should('have.been.calledOnce');
cySmartGet('footer-button').click();
cySmartGet('@sendTransactionStub').should('have.been.calledOnce');
Expand Down Expand Up @@ -629,7 +629,7 @@ describe('Bridge Widget tests', () => {
cySmartGet('bridge-amount-text__input').blur();
cySmartGet('bridge-form-button').click();

cySmartGet('@getUnsignedApproveBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedApproveDepositBridgeTxStub').should('have.been.calledOnce');
cySmartGet('@getUnsignedDepositTxStub').should('have.been.calledOnce');
cySmartGet('footer-button').click();
cySmartGet('@sendTransactionStub').should('have.been.calledOnce');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ describe('Bridge Form', () => {
// });

it('should set defaults when provided and ignore casing on token address', () => {
cy.stub(TokenBridge.prototype, 'getUnsignedApproveBridgeTx').as('getUnsignedApproveBridgeTxStub')
cy.stub(TokenBridge.prototype, 'getUnsignedApproveDepositBridgeTx').as('getUnsignedApproveDepositBridgeTxStub')
.resolves({
required: true,
unsignedTx: {},
Expand Down Expand Up @@ -205,7 +205,7 @@ describe('Bridge Form', () => {

describe('Bridge Form submit', () => {
it('should submit bridge and make required sdk calls', () => {
cy.stub(TokenBridge.prototype, 'getUnsignedApproveBridgeTx').as('getUnsignedApproveBridgeTxStub')
cy.stub(TokenBridge.prototype, 'getUnsignedApproveDepositBridgeTx').as('getUnsignedApproveDepositBridgeTxStub')
.resolves({
required: true,
unsignedTx: {},
Expand Down Expand Up @@ -264,7 +264,7 @@ describe('Bridge Form', () => {
});

it('should submit bridge and skip approval if not required', () => {
cy.stub(TokenBridge.prototype, 'getUnsignedApproveBridgeTx').as('getUnsignedApproveBridgeTxStub')
cy.stub(TokenBridge.prototype, 'getUnsignedApproveDepositBridgeTx').as('getUnsignedApproveDepositBridgeTxStub')
.resolves({
required: false,
});
Expand Down Expand Up @@ -305,11 +305,12 @@ describe('Bridge Form', () => {
cySmartGet('bridge-amount-text__input').blur();
cySmartGet('bridge-form-button').click();

cySmartGet('@getUnsignedApproveBridgeTxStub').should('have.been.calledOnce').should('have.been.calledWith', {
depositorAddress: '0x123',
token: imxAddress,
depositAmount: utils.parseUnits('0.1', 18),
});
cySmartGet('@getUnsignedApproveDepositBridgeTxStub').should('have.been.calledOnce')
.should('have.been.calledWith', {
depositorAddress: '0x123',
token: imxAddress,
depositAmount: utils.parseUnits('0.1', 18),
});

cySmartGet('@getUnsignedDepositTxStub').should('have.been.calledOnce').should('have.been.calledWith', {
depositorAddress: '0x123',
Expand All @@ -328,7 +329,7 @@ describe('Bridge Form', () => {

describe('when approval transaction is not required and user rejected signing the bridge transaction', () => {
beforeEach(() => {
cy.stub(TokenBridge.prototype, 'getUnsignedApproveBridgeTx').as('getUnsignedApproveBridgeTxStub')
cy.stub(TokenBridge.prototype, 'getUnsignedApproveDepositBridgeTx').as('getUnsignedApproveDepositBridgeTxStub')
.resolves({
required: false,
});
Expand Down Expand Up @@ -367,11 +368,12 @@ describe('Bridge Form', () => {
cySmartGet('bridge-amount-text__input').blur();
cySmartGet('bridge-form-button').click();

cySmartGet('@getUnsignedApproveBridgeTxStub').should('have.been.calledOnce').should('have.been.calledWith', {
depositorAddress: '0x123',
token: imxAddress,
depositAmount: utils.parseUnits('0.1', 18),
});
cySmartGet('@getUnsignedApproveDepositBridgeTxStub').should('have.been.calledOnce')
.should('have.been.calledWith', {
depositorAddress: '0x123',
token: imxAddress,
depositAmount: utils.parseUnits('0.1', 18),
});

cySmartGet('@getUnsignedDepositTxStub').should('have.been.calledOnce').should('have.been.calledWith', {
depositorAddress: '0x123',
Expand Down Expand Up @@ -419,7 +421,7 @@ describe('Bridge Form', () => {
}],
};

cy.stub(TokenBridge.prototype, 'getUnsignedApproveBridgeTx').as('getUnsignedApproveBridgeTxStub')
cy.stub(TokenBridge.prototype, 'getUnsignedApproveDepositBridgeTx').as('getUnsignedApproveDepositBridgeTxStub')
.resolves({
required: false,
});
Expand Down Expand Up @@ -493,7 +495,7 @@ describe('Bridge Form', () => {
// }],
// };

// cy.stub(TokenBridge.prototype, 'getUnsignedApproveBridgeTx').as('getUnsignedApproveBridgeTxStub')
// cy.stub(TokenBridge.prototype, 'getUnsignedApproveDepositBridgeTx').as('getUnsignedApproveDepositBridgeTxStub')
// .resolves({
// required: false,
// });
Expand Down
Loading

0 comments on commit a83a9f9

Please sign in to comment.