@ngeth/ethers-angular
v0.0.26
Published
``` npm install @ngeth/ethers-core @ngeth/ethers-angular ethers ```
Downloads
10
Readme
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] }
]
})