0.0.26 • Published 1 year ago

@ngeth/ethers-angular v0.0.26

Weekly downloads
-
License
-
Repository
-
Last release
1 year ago

Angular toolkit for ethers

Getting Started

npm install @ngeth/ethers-core @ngeth/ethers-angular ethers

In the app.module.ts add the providers:

import { ngEthersProviders, EthersModule } from '@ngeth/ethers-angular';
import { InjectedProviders } form '@ngeth/ethers-core';

@NgModule({
  // Use EthersModule only for the pipes, directive or components
  imports: [BrowserModule, EthersModule],
  providers: [ngEthersProviders(InjectedProviders)]
})

This will exposes the NgERC1193 provider

Use it in a component:

import { NgERC1193 } from '@ngeth/ethers-angular';
@Component({
  template: `
    <p *ngIf="account$ | async as account; else noAccount">{{ account | address }}</p>
    <ng-template #noAccount>
      <button (click)="connect()">Connect your wallet</button>
    </ng-template>
  `
})
export class AppComponent {
  account$ = this.erc1193.account$;
  constructor(private erc1193: NgERC1193) {}
  connect() {
    this.erc1193.enable();
  }
}

Install

Standalone

npm install @ngeth/ethers-core @ngeth/ethers-angular ethers

With @ngeth/hardhat

npx nx @ngeth/hardhat:ng-add --outputType angular

Contract

ngContract

You can wrap any contract generated by @ngeth/hardhat with the ngContract mixin :

import { Signer } from '@ethersproject/abstract-signer';
import { ngContract } from '@ngeth/ethers-angular';

@Injectable({ providedIn: 'root' })
export class MyERC721 extends ngContract(ERC721) {
  // Use `ngEthersProviders` to inject the Signer. See how below
  constructor(signer: Signer) {
    super(addresses.hardhat.ERC721, signer);
  }

  tokensOwnedBy(address: string): Observable<BigNumber[]> {
    const receivedFilter = this.filters.Transfer(undefined, address);
    const sentFilter = this.filters.Transfer(address);
    return combineLatest([
      this.from(receivedFilter),
      this.from(sentFilter),
    ]).pipe(
      map(([received, sent]) => erc721Tokens(received, sent))
    );
  }
}

The ngContract wrap the events from ethers inside as Observable with the from method. Every observable is cached and multicasted.

Now you can inject it in your component:

import { Signer } from '@ethersproject/abstract-signer';
import {} from '@ngeth/ethers-core';

@Component({})
export class AppComponent {
  myTokens$: Observable<BigNumber[]>;
  constructor(private signer: Signer, private erc721: MyERC721) {}
  async ngOnInit() {
    const address = await this.signer.getAddress();
    this.myTokens$ = this.erc721.tokensOwnedBy(address);
  }
}

Providers

ngEthersProviders

Utils function to bind the Provider & Signer to the NgERC1193 wallet manager

import { InjectedProviders } form '@ngeth/ethers-core';
...
providers: [
  ngEthersProviders(InjectedProviders)
]

NgERC1193

The NgERC1193 manages the signers and providers. It's an extension of the ERC1193 for @ngeth/ethers-core with observable/zone helpers for angular. You can inject an ERC1193 with ngEthersProviders.

Select an ERC1193 You can create your own ERC1193 wallet manager or use an existing one. In this example we'll use the InjectedProviders from @ngeth/ethers-core. This will provide access to the ethereum providers in the browser (MetaMask and Coinbase).

import { ngEthersProviders } from '@ngeth/ethers-angular';
import { InjectedProviders } form '@ngeth/ethers-core';

@NgModule({
  providers: [ngEthersProviders(InjectedProviders)]
})

Then you can access it like that:

@Component({...})
export class AppComponent {
  account$ = this.erc1193.account$;
  constructor(private erc1193: NgERC1193) {}
  connect() {
    this.erc1193.enable();
  }
  async changeChain(chainId: string) {
    await this.erc1193.addChain(chainId);
    await this.erc1193.switchChain(chainId);
  }
}

Extends it If you want to extends the NgERC1193 class you can use the mixin ngErc1193. It'll wrap an existing ERC1193 class with NgERC1193. Here is an example with a custom wallet selector using @angular/material.

@Injectable({ providedIn: 'root' })
export class MyInjectedERC1193 extends ngErc1193(InjectedProviders) {
  constructor(private dialog: MatDialog) {
    super();
  }

  protected override async getWallet() {
    if (!this.wallets.length) return;
    if (this.wallets.length === 1) return this.wallets[0];
    const ref = this.dialog.open(SelectWalletComponent, { labels: this.wallets.map(w => w.label) });
    const label = await firstValueFrom(ref.afterClosed());
    return this.wallets.find(w => w.label === label);
  }
}

Now you can inject it as any other Injectable class:

@Component({...})
export class AppComponent {
  constructor(private erc1193: MyInjectedERC1193) {}
}

Note: If you want to inject the Provider or Signer you'll still need to use the ngEthersProviders in the providers list.

@NgModule({
  providers: [ngEthersProviders(MyInjectedERC1193)]
})

SUPPORTED_CHAINS

Specify a list of supported chain for the application. If value is "*", all chains are supported

{ provide: SUPPORTED_CHAINS, useValue: [1] }

CUSTOM_CHAINS

Add a list of custom chains for the ChainManager. Useful for local development with hardhat for example

{ provide: CUSTOM_CHAINS, useValue: { '0x1234': { name: 'Custom Chain', chain: 'ETH',... } } }

rpcProvider

Utils function to set the ethers.js's Provider as injectable dependancy

providers: [rpcProvider()]

EthersModule

Provides useful components & pipes for an angular web3 project

@NgModule({
  imports: [EthersModule]
})

Components

eth-blocky

A blocky stuled representation of an address

<eth-blocky address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"></eth-blocky>

Directives

input[type="ethAddress"]

A ControlValueAccessor for ethereum addresses

<input type="ethAddress" [formControl]="control" />

input[type="ether"]

A ControlValueAccessor to transform an ether value into a BigNumber of wei

<input type="ether" [formControl]="control" />

Pipes

address

Checksum & format an ethereum address

<!-- 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -->
<p>{{ account | address }}</p>
<!-- 0xf39F...2266 -->
<p>{{ account | address:'short' }}</p>

bignumber

Transform a bignumber-like value into a string

<p>{{ gasUsed | bignumber }}</p>

ether

Transform a bignumber-like value into a ether string using the ether symbol

<p>{{ price | ether }}</p>

ethCurrency

Transform a bignumber-like value into a the native currency of a chain. It'll default to the current chain if not chainId is specified.

<!-- Use the current selected chain -->
<p>{{ price | ethCurrency | async }}</p>
<!-- Support number or hex chainID -->
<p>{{ price | ethCurrency:1 | async }}</p>
<p>{{ price | ethCurrency:'0x01' | async }}</p>

This is an async pipe because it relies on the chain metadata.

chain

Get the chain metadata of a chainId

<menu>
  <ng-container *ngFor="let chainId of availableChainIds">
    <li *ngIf="chainId | chain | async as chain">
      <button (click)="select(chainId)">{{ chain.name }}</button>
    <li>
  </ng-container>
</menu>

explore

Link to the block explorer search of a specific chain

<ng-container *ngIf="chainId$ | async as chainId">
  <article *ngIf="chainId | chain | async as chain">
    <a [href]="account | explorer:chain">
      <eth-blocky [address]="account"></eth-blocky>
      <h3>{{ account | address }}</h3>
    </a>
  </article>
</ng-container>

Guards

HasInjectedProviderGuard

Check if the user has an injected provided by looking at window.ethereum

  • Redirect to /no-injected-provider
  • Custom redirect: data.hasInjectedProviderRedirect
{
  path: 'account',
  canActivate: [HasInjectedProviderGuard],
  data: {
    hasInjectedProviderRedirect: '/get-provider'
  }
}

HasWalletGuard

Check if the user has an active erc1193 wallet selected. In the case of InjectedProviders it can be either MetaMask or Coinbase

  • Redirect to: /no-wallet
  • Custom redirect: data.hasWalletRedirect
{
  path: 'wallet',
  canActivate: [HasWalletGuard],
  data: {
    hasWalletRedirect: '/get-wallet'
  }
}

IsConnectedGuard

Check if the user has a connected account

  • Redirect to: /no-connected
  • Custom redirect: data.isConnectedRedirect
{
  path: 'wallet',
  canActivate: [IsConnectedGuard],
  data: {
    isConnectedRedirect: '/connect-to-metamask'
  }
}

HasSignerGuard

Check if the user has a connected account

  • Redirect to: /no-signer
  • Custom redirect: data.hasSignerRedirect
{
  path: 'wallet',
  canActivate: [IsConnectedGuard],
  data: {
    hasSignerRedirect: '/select-account'
  }
}

IsSupportedChainGuard

Check if the current user chain is supported by the application

  • Redirect to: /unsupported-chain
  • Custom redirect: data.isSupportedChainRedirect
@NgModule({
  imports: [
    RouterModule.forChild({
      path: '',
      canActivate: [IsSupportedChainGuard],
      data: {
        isSupportedChainRedirect: '/change-chain'
      }
    })
  ],
  providers: [
    { provide: SUPPORTED_CHAINS, useValue: [1] }
  ]
})
0.0.25

1 year ago

0.0.26

1 year ago

0.0.24

1 year ago

0.0.23

2 years ago

0.0.22

2 years ago

0.0.21

2 years ago

0.0.20

2 years ago