Quickstart - Browser
This page will introduce the primary operations provided by Request Network’s SDK while using the
Web3SignatureProvider
to sign requests with a private key stored inside a wallet.This approach works well for Browser environments with access to a web3 wallet.
You will learn:
- How to create a request
- How to update a request (coming soon...)
- How to pay a request
- How to detect a payment
- How to retrieve a user’s requests
To create an unencrypted ERC-20 request, first connect to an
ethers
v5 Provider
and Signer
or wagmi
/ viem
WalletClient.
Unfortunately, the Request Network SDK does not yet support ethers v6.
ethers v5
wagmi
viem
import { providers } from "ethers";
let provider;
if (process.env.WEB3_PROVIDER_URL === undefined) {
// Connect to Metamask and other injected wallets
provider = new providers.Web3Provider(window.ethereum);
} else {
// Connect to your own Ethereum node or 3rd party node provider
provider = new providers.JsonRpcProvider(process.env.WEB3_PROVIDER_URL);
}
// getDefaultProvider() won't work because it doesn't include a Signer.
import { useWalletClient } from "wagmi";
const { data: walletClient } = useWalletClient();
Very similar to wagmi, but without using hooks. Construct your own
WalletClient
object.Then, construct a
Web3SignatureProvider
, passing in the ethers
Provider
or viem
WalletClient.
import { Web3SignatureProvider } from "@requestnetwork/web3-signature";
const web3SignatureProvider = new Web3SignatureProvider(provider);
Then, construct a
RequestNetwork
, passing in the:- Request Node URL. In this example, we use the Goerli Request Node Gateway.
Web3SignatureProvider
constructed in the previous step.
import { RequestNetwork } from "@requestnetwork/request-client.js"
const requestClient = new RequestNetwork({
nodeConnectionConfig: {
baseURL: "https://goerli.gateway.request.network/",
},
signatureProvider: web3SignatureProvider,
});
Then, prepare the Request creation parameters:
import { Types, Utils } from "@requestnetwork/request-client.js";
const payeeIdentity = '0x7eB023BFbAeE228de6DC5B92D0BeEB1eDb1Fd567';
const payerIdentity = '0x519145B771a6e450461af89980e5C17Ff6Fd8A92';
const paymentRecipient = payeeIdentity;
const feeRecipient = '0x0000000000000000000000000000000000000000';
const requestCreateParameters = {
requestInfo: {
// The currency in which the request is denominated
currency: {
type: Types.RequestLogic.CURRENCY.ERC20,
value: '0xBA62BCfcAaFc6622853cca2BE6Ac7d845BC0f2Dc',
network: 'goerli',
},
// The expected amount as a string, in parsed units, respecting `decimals`
// Consider using `parseUnits()` from ethers or viem
expectedAmount: '1000000000000000000',
// The payee identity. Not necessarily the same as the payment recipient.
payee: {
type: Types.Identity.TYPE.ETHEREUM_ADDRESS,
value: payeeIdentity,
},
// The payer identity. If omitted, any identity can pay the request.
payer: {
type: Types.Identity.TYPE.ETHEREUM_ADDRESS,
value: payerIdentity,
},
// The request creation timestamp.
timestamp: Utils.getCurrentTimestampInSecond(),
},
// The paymentNetwork is the method of payment and related details.
paymentNetwork: {
id: Types.Extension.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT,
parameters: {
paymentNetworkName: 'goerli',
paymentAddress: payeeIdentity,
feeAddress: feeRecipient,
feeAmount: '0',
},
},
// The contentData can contain anything.
// Consider using rnf_invoice format from @requestnetwork/data-format
contentData: {
reason: '🍕',
dueDate: '2023.06.16',
},
// The identity that signs the request, either payee or payer identity.
signer: {
type: Types.Identity.TYPE.ETHEREUM_ADDRESS,
value: payeeIdentity,
},
};
Then, call
createRequest()
to prepare a Request
object.const request = await requestClient.createRequest(requestCreateParameters);
Finally, call
request.waitForConfirmation()
to wait until:- The request contents are persisted in IPFS
- The Content-addressable ID (CID) is stored on-chain
- The resulting on-chain event is indexed by the storage subgraph.
const confirmedRequestData = await request.waitForConfirmation();
First, construct a
RequestNetwork
object and connect it to a Request Node. In this example, we use the Goerli Request Node Gateway:Note that paying a request doesn't require a
SignatureProvider
be passed into the RequestNetwork
object.import { RequestNetwork, Types } from "@requestnetwork/request-client.js";
const requestClient = new RequestNetwork({
nodeConnectionConfig: {
baseURL: "https://goerli.gateway.request.network/",
}
});
Then, retrieve the request and get the request data. Take note of the current request balance, to be used later for payment detection.
const request = await requestClient.fromRequestId(
'019830e9ec0439e53ec41fc627fd1d0293ec4bc61c2a647673ec5aaaa0e6338855',
);
const requestData = request.getData();
Then, construct an
ethers
v5 Provider
and Signer
. These allow you to read and write to the chain, respectively.Unfortunately, the Request Network SDK does not yet support ethers v6.
ethers v5
wagmi
viem
import { providers } from "ethers";
let provider;
if (process.env.WEB3_PROVIDER_URL === undefined) {
// Connect to Metamask and other injected wallets
provider = new providers.Web3Provider(window.ethereum);
} else {
// Connect to your own Ethereum node or 3rd party node provider
provider = new providers.JsonRpcProvider(process.env.WEB3_PROVIDER_URL);
}
// getDefaultProvider() won't work because it doesn't include a Signer.
const signer = await provider.getSigner();
page.tsx
import { useEthersV5Provider } from './use-ethers-v5-provider';
import { useEthersV5Signer } from './use-ethers-v5-signer';
return Page() {
const provider = useEthersV5Provider();
const signer = useEthersV5Signer();
...
}
use-ethers-v5-provider.ts
import { useMemo } from "react";
import { providers } from "ethers";
import { type HttpTransport } from "viem";
import { type PublicClient, usePublicClient } from "wagmi";
export function publicClientToProvider(publicClient: PublicClient) {
const { chain, transport } = publicClient;
const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
};
if (transport.type === "fallback")
return new providers.FallbackProvider(
(transport.transports as ReturnType<HttpTransport>[]).map(
({ value }) => new providers.JsonRpcProvider(value?.url, network)
)
);
return new providers.JsonRpcProvider(transport.url as string, network);
}
/** Hook to convert a viem Public Client to an ethers.js Provider. */
export function useEthersV5Provider({ chainId }: { chainId?: number } = {}) {
const publicClient = usePublicClient({ chainId });
return useMemo(() => publicClientToProvider(publicClient), [publicClient]);
}
use-ethers-v5-signer.ts
import { useMemo } from "react";
import { providers } from "ethers";
import { type WalletClient, useWalletClient } from "wagmi";
export function walletClientToSigner(walletClient: WalletClient) {
const { account, chain, transport } = walletClient;
const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
};
const provider = new providers.Web3Provider(transport, network);
const signer = provider.getSigner(account.address);
return signer;
}
/** Hook to convert a viem Wallet Client to an ethers.js Signer. */
export function useEthersV5Signer({ chainId }: { chainId?: number } = {}) {
const { data: walletClient } = useWalletClient({ chainId });
return useMemo(
() => (walletClient ? walletClientToSigner(walletClient) : undefined),
[walletClient]
);
}
Very similar to wagmi, but without using hooks. Instead, call
publicClientToProvider()
or walletClientToSigner()
Then, check that the payer has sufficient funds using
hasSufficientFunds()
import { hasSufficientFunds } from "@requestnetwork/payment-processor";
const _hasSufficientFunds = await hasSufficientFunds(
requestData,
payerAddress,
{
provider: provider,
},
);
Then, in the case of an ERC-20 request, check that the payer has granted sufficient approval using
hasErc20Approval()
. If not, submit an approval transaction using approveErc20
. Wait for an appropriate number of block confirmations. On Goerli or Ethereum, 2 block confirmations should suffice. Other chains may require more.import { approveErc20, hasErc20Approval } from "@requestnetwork/payment-processor";
const _hasErc20Approval = await hasErc20Approval(
requestData,
payerAddress,
provider
);
if (!_hasErc20Approval) {
const approvalTx = await approveErc20(requestData, signer);
await approvalTx.wait(2);
}
Finally, pay the request using
payRequest()
import { payRequest } from "@requestnetwork/payment-processor";
const paymentTx = await payRequest(requestData, signer);
await paymentTx.wait(2);
You can detect that the payment was successful by polling the request and waiting until the request balance is greater than or equal to the expected amount.
const request = await requestClient.fromRequestId(requestData.requestId);
let requestData = request.getData();
while (requestData.balance?.balance < requestData.expectedAmount) {
requestData = await request.refresh();
await new Promise((resolve) => setTimeout(resolve, 1000));
}
First, construct a
RequestNetwork
object and connect it to a Request Node. In this example, we use the Goerli Request Node Gateway:import { RequestNetwork, Types } from "@requestnetwork/request-client.js";
const requestClient = new RequestNetwork({
nodeConnectionConfig: {
baseURL: "https://goerli.gateway.request.network/",
},
});
Then, call
fromIdentity()
to get an array of Request
objects or fromRequestId()
to get a single Request
object. This function retrieves the Request
s stored in IPFS and queries on-chain events to determine the balances paid so far. Finally, call getData()
on each Request
to get the request contents.fromIdentity()
fromRequestId()
const identityAddress = "0x519145B771a6e450461af89980e5C17Ff6Fd8A92";
const requests = await requestClient.fromIdentity({
type: Types.Identity.TYPE.ETHEREUM_ADDRESS,
value: identityAddress,
});
const requestDatas = requests.map((request) => request.getData());
const request = await requestClient.fromRequestId(
'019830e9ec0439e53ec41fc627fd1d0293ec4bc61c2a647673ec5aaaa0e6338855',
);
const requestData = request.getData();
Last modified 2d ago