Skip to content

Commit

Permalink
feat(): add token send (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreasGassmann authored Oct 20, 2023
1 parent ef598bd commit 2a51e84
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { NftListComponent } from './components/nft-list/nft-list.component';
import { LandingComponent } from './components/landing/landing.component';
import { QrModalComponent } from './modals/qr-modal/qr-modal.component';
import { SendTezModalComponent } from './modals/send-tez-modal/send-tez-modal.component';
import { SendTokenModalComponent } from './modals/send-token-modal/send-token-modal.component';
import { ConnectedModalComponent } from './modals/connected-modal/connected-modal.component';
import { SendNftModalComponent } from './modals/send-nft-modal/send-nft-modal.component';
import { ConfirmTxModalComponent } from './modals/confirm-tx-modal/confirm-tx-modal.component';
Expand Down Expand Up @@ -66,6 +67,7 @@ import { ToastrModule } from 'ngx-toastr';
LandingComponent,
QrModalComponent,
SendTezModalComponent,
SendTokenModalComponent,
ConnectedModalComponent,
SendNftModalComponent,
ConfirmTxModalComponent,
Expand Down
13 changes: 13 additions & 0 deletions src/app/components/account/account.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ <h2 class="card-title">Balance</h2>
splitNumber(token?.humanReadableBalance)[1]
}}</span>
</span>
<button
style="float: right; width: 40px"
type="button"
class="btn btn-outline-primary btn-circle"
(click)="sendToken(token)"
>
<img
class="copy-button"
src="./assets/icons/MdOutlineCallReceivedBlue.svg"
alt="Sent"
style="width: 16px; height: 16px; transform: rotate(180deg)"
/>
</button>
</div>
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions src/app/components/account/account.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,8 @@ export class AccountComponent implements OnChanges {
}
return number.split('.');
}

sendToken(token: Token) {
this.modalService.showSendTokenModal(token);
}
}
2 changes: 1 addition & 1 deletion src/app/modals/send-tez-modal/send-tez-modal.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class SendTezModalComponent implements OnInit {
.toString();

const result = await sendOperationRequest(operations);
this.toastService.showTxSucessToast();
this.toastService.showTxSuccessToast();

console.log('RESULT', result);
} catch (e) {
Expand Down
51 changes: 51 additions & 0 deletions src/app/modals/send-token-modal/send-token-modal.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<div class="modal-header send-header">
<button
type="button"
class="btn-close close pull-right"
aria-label="Close"
(click)="bsModalRef.hide()"
>
<span aria-hidden="true" class="visually-hidden">&times;</span>
</button>
</div>
<div class="modal-body text-center" style="padding: 0">
<h4 class="modal-title">Send {{ token?.token?.metadata?.symbol }}</h4>
<div class="text-left">
<label for="amount" style="margin: 0">
<h5 class="amount-text">Amount:</h5>
</label>
<input
[ngModel]="amount"
(ngModelChange)="onAmountChanged($event)"
type="input"
id="amount"
class="form-control custom-input"
placeholder="0.00"
/>
<div class="d-flex justify-content-between">
<small *ngIf="fee" class="form-text text-muted">Fee: {{ fee }} ꜩ</small>
</div>
</div>

<div class="text-left">
<label for="sendTo" style="margin: 0">
<h5 class="send-text">Send To:</h5>
</label>
<input
[(ngModel)]="recipient"
type="input"
id="sendTo"
class="form-control custom-input"
placeholder="Paste tz address here"
/>
</div>

<button
(click)="send()"
type="button"
class="btn button btn-circle btn-block"
[disabled]="amount.length === 0 || recipient.length === 0"
>
Send
</button>
</div>
55 changes: 55 additions & 0 deletions src/app/modals/send-token-modal/send-token-modal.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.custom-input {
padding: 0px 24px 0px 24px;
border-radius: 100px;
border: 1px solid #d2d2d7;
min-height: 52px;
}

.button {
width: 100%;
height: 52px;
padding: 0px 24px 0px 24px;
border-radius: 100px;
gap: 10px;
background-color: #0f61ff;
color: white;
display: flex;
justify-content: center;
align-items: center;
border: none;
outline: none;
cursor: pointer;
margin-top: 30px;
transition: background-color 0.3s ease-in-out;
}

.button:hover {
background-color: #183fd7;
}

.btn:disabled {
background-color: #d2d2d7;
color: #86868b;
}

.send-header {
padding: 0;
padding-top: 10px;
padding-right: 10px;
}

.amount-text {
margin: 0;
margin-bottom: 12px;
}

.form-text {
margin: 0;
margin-top: 12px;
}

.send-text {
margin: 0;
margin-top: 24px;
margin-bottom: 12px;
}
24 changes: 24 additions & 0 deletions src/app/modals/send-token-modal/send-token-modal.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { SendTokenModalComponent } from './send-token-modal.component';

describe('SendTokenModalComponent', () => {
let component: SendTokenModalComponent;
let fixture: ComponentFixture<SendTokenModalComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [SendTokenModalComponent],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(SendTokenModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
179 changes: 179 additions & 0 deletions src/app/modals/send-token-modal/send-token-modal.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import {
NetworkType,
PartialTezosTransactionOperation,
TezosOperationType,
TezosTransactionOperation,
} from '@airgap/beacon-wallet';
import { Component, OnInit } from '@angular/core';
import BigNumber from 'bignumber.js';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { first } from 'rxjs/operators';
import { AccountService } from 'src/app/services/account.service';
import { ApiService } from 'src/app/services/api.service';
import { BeaconService } from 'src/app/services/beacon.service';
import { ToastService } from 'src/app/services/toast.service';
import { Token } from 'src/app/types';
import { sendOperationRequest } from 'src/app/utils/snap';
import { prepareOperations } from 'src/app/utils/tezos/prepare-operations';

@Component({
selector: 'app-send-token-modal',
templateUrl: './send-token-modal.component.html',
styleUrls: ['./send-token-modal.component.scss'],
})
export class SendTokenModalComponent implements OnInit {
token: Token | undefined;
recipient: string = '';
amount: string = '';
fee?: string;

constructor(
public readonly bsModalRef: BsModalRef,
public readonly apiService: ApiService,
public readonly accountService: AccountService,
public readonly beaconService: BeaconService,
public readonly toastService: ToastService,
) {}

ngOnInit(): void {}

async send() {
const FA2Transfer = (
contract: string,
from: string,
to: string,
tokenId: string,
amount: string,
): PartialTezosTransactionOperation => {
return {
kind: TezosOperationType.TRANSACTION,
amount: '0',
destination: contract,
parameters: {
entrypoint: 'transfer',
value: [
{
prim: 'Pair',
args: [
{
string: from,
},
[
{
prim: 'Pair',
args: [
{
string: to,
},
{
prim: 'Pair',
args: [
{
int: tokenId,
},
{
int: amount,
},
],
},
],
},
],
],
},
],
},
};
};

const FA1p2Transfer = (
contract: string,
from: string,
to: string,
amount: string,
): PartialTezosTransactionOperation => {
return {
kind: TezosOperationType.TRANSACTION,
amount: '0',
destination: contract,
parameters: {
entrypoint: 'transfer',
value: {
prim: 'Pair',
args: [
{
string: from,
},
{
prim: 'Pair',
args: [
{
string: to,
},
{
int: amount,
},
],
},
],
},
},
};
};

this.accountService.accounts$.pipe(first()).subscribe(async (accounts) => {
if (!this.token) {
return;
}

const account = accounts[0];
const adjustedAmount = new BigNumber(this.amount)
.shiftedBy(new BigNumber(this.token.token.metadata.decimals).toNumber())
.toString(10);

const operations: PartialTezosTransactionOperation[] = [
this.token.token.standard === 'fa2'
? FA2Transfer(
this.token.token.contract.address,
account.address,
this.recipient,
this.token.token.tokenId,
adjustedAmount,
)
: FA1p2Transfer(
this.token.token.contract.address,
account.address,
this.recipient,
adjustedAmount,
),
];

try {
const estimated = await prepareOperations(
account.address,
account.publicKey,
operations,
(this.apiService.RPCs as any)[NetworkType.MAINNET].selected + '/',
);

this.fee = new BigNumber(
(estimated.contents[0] as TezosTransactionOperation).fee,
)
.div(1_000_000)
.toString();

const result = await sendOperationRequest(operations);
this.toastService.showTxSuccessToast();

console.log('RESULT', result);
} catch (e) {
this.toastService.showTxErrorToast();
}
this.bsModalRef.hide();
});
}

onAmountChanged(amount: string) {
this.amount = amount;
}
}
4 changes: 2 additions & 2 deletions src/app/services/beacon.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export class BeaconService {
this.handleOperationRequest(account, message)
.then(() => {
toast.toastRef.close();
this.toastService.showTxSucessToast();
this.toastService.showTxSuccessToast();
})
.catch(() => {
toast.toastRef.close();
Expand All @@ -183,7 +183,7 @@ export class BeaconService {
this.handleSignPayload(account, message)
.then(() => {
toast.toastRef.close();
this.toastService.showTxSucessToast();
this.toastService.showTxSuccessToast();
})
.catch(() => {
toast.toastRef.close();
Expand Down
Loading

0 comments on commit 2a51e84

Please sign in to comment.