Skip to content

Commit

Permalink
feat: address to transfer (#56)
Browse files Browse the repository at this point in the history
<!--- Provide a general summary of your changes in the Title above -->

## Description

<!--- Describe your changes in detail -->
This PR adds toggle and input to introduce address to transfer

## Related Issue Or Context

<!--- If suggesting a new feature or change, please discuss it in an
issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps
to reproduce -->
<!--- Otherwise, describe context and motivation for change here -->

Closes: #20 

## How Has This Been Tested? Testing details.

<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation

## Checklist:

<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->

- [ ] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have ensured that all acceptance criteria (or expected behavior)
from issue are met
- [ ] I have updated the documentation locally and in sygma-docs.
- [ ] I have added tests to cover my changes.
- [ ] I have ensured that all the checks are passing and green, I've
signed the CLA bot

---------

Signed-off-by: Marin Petrunic <[email protected]>
Co-authored-by: Marin Petrunic <[email protected]>
Co-authored-by: Filip Štoković <[email protected]>
Co-authored-by: Marin Petrunić <[email protected]>
Co-authored-by: Filip Štoković <[email protected]>
  • Loading branch information
5 people authored Feb 1, 2024
1 parent 003416e commit 9fa87ad
Show file tree
Hide file tree
Showing 13 changed files with 507 additions and 284 deletions.
6 changes: 4 additions & 2 deletions packages/widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
},
"author": "Sygmaprotocol Product Team",
"dependencies": {
"@buildwithsygma/sygma-sdk-core": "^2.6.0",
"@buildwithsygma/sygma-sdk-core": "^2.6.2",
"@ethersproject/abstract-signer": "^5.7.0",
"@ethersproject/contracts": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
Expand All @@ -36,6 +36,8 @@
"@lit/reactive-element": "^2.0.3",
"@polkadot/api": "^10.11.2",
"@polkadot/extension-dapp": "^0.46.6",
"@polkadot/keyring": "^12.6.2",
"@polkadot/util": "^12.6.2",
"ethers": "5.7.2",
"events": "^3.3.0",
"lit": "3.0.0"
Expand All @@ -45,7 +47,7 @@
"@open-wc/testing-helpers": "^3.0.0",
"eslint": "^8.48.0",
"eslint-plugin-lit": "^1.9.1",
"jsdom": "^23.2.0",
"happy-dom": "^13.3.1",
"lit-analyzer": "^2.0.3",
"rollup-plugin-visualizer": "^5.9.2",
"typescript": "5.2.2",
Expand Down
80 changes: 80 additions & 0 deletions packages/widget/src/components/address-input/address-input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { LitElement, html } from 'lit';
import type { HTMLTemplateResult } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { Network } from '@buildwithsygma/sygma-sdk-core';
import { when } from 'lit/directives/when.js';
import { validateAddress } from '../../utils';
import { styles } from './styles';

@customElement('sygma-address-input')
export class AddressInput extends LitElement {
static styles = styles;
@property({
type: String
})
address: string = '';

@property({ attribute: false })
onAddressChange: (address: string) => void = () => {};

@property({
type: String
})
network: Network = Network.EVM;

@state()
errorMessage: string | null = null;

connectedCallback(): void {
super.connectedCallback();
this.handleAddressChange(this.address);
}

private handleAddressChange = (value: string): void => {
const trimedValue = value.trim();

if (this.errorMessage) {
this.errorMessage = null;
}

if (!trimedValue) {
return;
}

this.errorMessage = validateAddress(trimedValue, this.network);

if (!this.errorMessage) {
void this.onAddressChange(trimedValue);
}
};

render(): HTMLTemplateResult {
return html` <section class="inputAddressSection">
<div class="inputAddressContainer">
<label class="labelContainer">
<span>Send to </span>
${when(
this.errorMessage,
() => html` <span class="errorMessage">${this.errorMessage}</span>`
)}</label
>
<textarea
class=${this.errorMessage ? 'inputAddress error' : 'inputAddress'}
name="address"
@change=${(evt: Event) =>
this.handleAddressChange((evt.target as HTMLInputElement).value)}
>
${ifDefined(this.address)}
</textarea
>
</div>
</section>`;
}
}

declare global {
interface HTMLElementTagNameMap {
'sygma-address-input': AddressInput;
}
}
1 change: 1 addition & 0 deletions packages/widget/src/components/address-input/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { AddressInput } from './address-input';
51 changes: 51 additions & 0 deletions packages/widget/src/components/address-input/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { css } from 'lit';

export const styles = css`
.inputAddressSection {
display: flex;
flex-direction: column;
justify-content: center;
gap: 0.5rem;
min-height: 7.75rem; // TOO: remove this hardcoded value
}
.inputAddressContainer {
display: flex;
flex-direction: column;
width: 100%;
min-height: 7.75rem; // TOO: remove this hardcoded value
gap: 0.5rem;
}
.inputAddress {
border-radius: 1.5rem;
border: 0.063rem solid var(--zinc-200);
font-size: 0.875rem;
text-align: center;
resize: none;
box-sizing: border-box;
overflow: hidden;
padding: 1rem;
}
.inputAddress:focus {
outline: none;
border: 0.063rem solid var(--zinc-200);
}
.error {
border-color: red;
}
.errorMessage {
color: red;
font-weight: 300;
font-size: 0.75rem;
}
.labelContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
}
`;
6 changes: 3 additions & 3 deletions packages/widget/src/components/amount-selector/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const styles = css`
display: flex;
width: 100%;
justify-content: flex-start;
color: #525252;
color: var(--neutral-600);
font-size: 14px;
font-weight: 500;
line-height: 20px; /* 142.857% */
Expand All @@ -30,7 +30,7 @@ export const styles = css`
.amountSelectorInput {
border: none;
color: #525252;
color: var(--neutral-600);
font-size: 34px;
font-weight: 500;
line-height: 40px;
Expand All @@ -52,7 +52,7 @@ export const styles = css`
}
.maxButton {
color: #2563eb;
color: var(--blue-600);
border: none;
background: none;
font-weight: 500;
Expand Down
1 change: 1 addition & 0 deletions packages/widget/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { AmountSelector } from './amount-selector';
export { NetworkSelector } from './network-selector';
export { AddressInput } from './address-input';
6 changes: 6 additions & 0 deletions packages/widget/src/controllers/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class WidgetController implements ReactiveController {
public supportedSourceNetworks: Domain[] = [];
public supportedDestinationNetworks: Domain[] = [];
public supportedResources: Resource[] = [];
public destinatonAddress?: string = '';

//@ts-expect-error it will be used
private assetTransfer?: EVMAssetTransfer | SubstrateAssetTransfer;
Expand Down Expand Up @@ -126,4 +127,9 @@ export class WidgetController implements ReactiveController {
console.log('resource amount', amount);
this.resourceAmount = amount;
};

onDestinationAddressChange = (address: string): void => {
console.log('destination address', address);
this.destinatonAddress = address;
};
}
28 changes: 19 additions & 9 deletions packages/widget/src/styles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import { css } from 'lit';

export const styles = css`
:host {
--zinc-200: #e4e4e7;
--zinc-400: #a1a1aa;
--white: #fff;
--gray-100: #f3f4f6;
--neutral-600: #525252;
--primary-300: #a5b4fc;
--primary-500: #6366f1;
--blue-600: #2563eb;
}
@font-face {
font-family: 'Inter';
font-style: normal;
Expand All @@ -15,11 +26,10 @@ export const styles = css`
gap: 16px;
padding: 24px;
width: 350px; /* TODO: remove these hardcoded values */
height: 476px; /* TODO: ↑ */
width: 21.875rem; /* TODO: remove these hardcoded values */
border-radius: 12px;
border: 1px solid #f3f4f6;
background-color: #fff;
border: 1px solid var(--gray-100);
background-color: var(--white);
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
Expand Down Expand Up @@ -47,7 +57,7 @@ export const styles = css`
border-radius: 16px;
border: none;
background-color: #a5b4fc;
background-color: var(--primary-300);
color: #ffffff;
width: 314px; /* TODO: remove these hardcoded values */
Expand All @@ -62,7 +72,7 @@ export const styles = css`
}
.actionButton:active {
background-color: #6366f1;
background-color: var(--primary-500);
}
.actionButtonReady {
Expand All @@ -75,13 +85,13 @@ export const styles = css`
padding: 12px 20px;
border-radius: 16px;
background-color: #6366f1;
background-color: var(--primary-500);
color: #ffffff;
border: none;
}
.actionButtonReady:active {
background-color: #a5b4fc;
background-color: var(--primary-300);
}
.actionButtonReady:hover {
Expand All @@ -94,7 +104,7 @@ export const styles = css`
gap: 6px;
align-self: flex-start;
color: #525252;
color: var(--neutral-600);
font-size: 12px;
line-height: 150%;
}
Expand Down
32 changes: 32 additions & 0 deletions packages/widget/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { HTMLTemplateResult } from 'lit';
import { html } from 'lit';
import { decodeAddress, encodeAddress } from '@polkadot/keyring';
import { hexToU8a, isHex } from '@polkadot/util';
import { Network } from '@buildwithsygma/sygma-sdk-core';
import { ethers } from 'ethers';
import {
baseNetworkIcon,
cronosNetworkIcon,
Expand Down Expand Up @@ -46,3 +50,31 @@ export const capitalize = (s: string): string => {
const rest = s.slice(1);
return `${firstLetter}${rest}`;
};

export const validateSubstrateAddress = (address: string): boolean => {
try {
encodeAddress(isHex(address) ? hexToU8a(address) : decodeAddress(address));
return true;
} catch (error) {
return false;
}
};

export const validateAddress = (
address: string,
network: Network
): string | null => {
switch (network) {
case Network.SUBSTRATE: {
const validPolkadotAddress = validateSubstrateAddress(address);
return validPolkadotAddress ? null : 'invalid Substrate address';
}
case Network.EVM: {
const isAddress = ethers.utils.isAddress(address);

return isAddress ? null : 'invalid Ethereum address';
}
default:
return 'unsupported network';
}
};
9 changes: 9 additions & 0 deletions packages/widget/src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { switchNetworkIcon, sygmaLogo } from './assets';
import { WidgetController } from './controllers/widget';
import './components/network-selector';
import './components/amount-selector';
import './components/address-input';
import { Directions } from './components/network-selector/network-selector';

@customElement('sygmaprotocol-widget')
Expand Down Expand Up @@ -51,6 +52,14 @@ class SygmaProtocolWidget extends LitElement {
>
</sygma-resource-selector>
</section>
<section>
<sygma-address-input
.address=${this.widgetController.destinatonAddress}
.onAddressChange=${this.widgetController
.onDestinationAddressChange}
>
</sygma-address-input>
</section>
<section>
<button
.disabled=${!this.widgetController.isReadyForTransfer}
Expand Down
Loading

0 comments on commit 9fa87ad

Please sign in to comment.