@0xsequence/connect
Version:
Connect package for Sequence Web SDK
413 lines âĒ 21.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SequenceWaasProvider = void 0;
exports.sequenceWaasWallet = sequenceWaasWallet;
exports.randomName = randomName;
const network_1 = require("@0xsequence/network");
const waas_1 = require("@0xsequence/waas");
const ethers_1 = require("ethers");
const uuid_1 = require("uuid");
const viem_1 = require("viem");
const wagmi_1 = require("wagmi");
const localStorage_js_1 = require("../../constants/localStorage.js");
const helpers_js_1 = require("../../utils/helpers.js");
const XAuth_js_1 = require("../X/XAuth.js");
sequenceWaasWallet.type = 'sequence-waas';
function sequenceWaasWallet(params) {
const nodesUrl = params.nodesUrl ?? 'https://nodes.sequence.app';
const showConfirmationModal = params.enableConfirmationModal ?? false;
const sequenceWaas = new waas_1.SequenceWaaS({
waasConfigKey: params.waasConfigKey,
projectAccessKey: params.projectAccessKey,
network: params.network ?? 137
});
const sequenceWaasProvider = new SequenceWaasProvider(sequenceWaas, showConfirmationModal, nodesUrl);
return (0, wagmi_1.createConnector)(config => ({
id: `sequence-waas`,
name: 'Sequence WaaS',
type: sequenceWaasWallet.type,
sequenceWaas,
sequenceWaasProvider,
params,
async setup() {
if (typeof window !== 'object') {
// (for SSR) only run in browser client
return;
}
if (params.googleClientId) {
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasGoogleClientID, params.googleClientId);
}
if (params.appleClientId) {
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasAppleClientID, params.appleClientId);
}
if (params.appleRedirectURI) {
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasAppleRedirectURI, params.appleRedirectURI);
}
if (params.epicAuthUrl) {
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasEpicAuthUrl, params.epicAuthUrl);
}
if (params.XClientId && params.XRedirectURI) {
const { code_challenge, code_verifier } = await (0, XAuth_js_1.getPkcePair)();
const authUrl = await (0, XAuth_js_1.getXOauthUrl)(params.XClientId, params.XRedirectURI, code_challenge);
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasXAuthUrl, authUrl);
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasXClientID, params.XClientId);
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasXRedirectURI, params.XRedirectURI);
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasXCodeVerifier, code_verifier);
}
sequenceWaasProvider.on('error', error => {
if (isSessionInvalidOrNotFoundError(error)) {
this.disconnect();
}
});
},
async connect(_connectInfo) {
const provider = await this.getProvider();
const isSignedIn = await provider.sequenceWaas.isSignedIn();
if (!isSignedIn) {
const googleIdToken = await config.storage?.getItem(localStorage_js_1.LocalStorageKey.WaasGoogleIdToken);
const emailIdToken = await config.storage?.getItem(localStorage_js_1.LocalStorageKey.WaasEmailIdToken);
const appleIdToken = await config.storage?.getItem(localStorage_js_1.LocalStorageKey.WaasAppleIdToken);
const epicIdToken = await config.storage?.getItem(localStorage_js_1.LocalStorageKey.WaasEpicIdToken);
const xIdToken = await config.storage?.getItem(localStorage_js_1.LocalStorageKey.WaasXIdToken);
let idToken;
if (params.loginType === 'google' && googleIdToken) {
idToken = googleIdToken;
}
else if (params.loginType === 'email' && emailIdToken) {
idToken = emailIdToken;
}
else if (params.loginType === 'apple' && appleIdToken) {
idToken = appleIdToken;
}
else if (params.loginType === 'epic' && epicIdToken) {
idToken = epicIdToken;
}
else if (params.loginType === 'X' && xIdToken) {
idToken = xIdToken;
}
await config.storage?.removeItem(localStorage_js_1.LocalStorageKey.WaasGoogleIdToken);
await config.storage?.removeItem(localStorage_js_1.LocalStorageKey.WaasEmailIdToken);
await config.storage?.removeItem(localStorage_js_1.LocalStorageKey.WaasAppleIdToken);
await config.storage?.removeItem(localStorage_js_1.LocalStorageKey.WaasEpicIdToken);
await config.storage?.removeItem(localStorage_js_1.LocalStorageKey.WaasXIdToken);
if (idToken) {
try {
let signInResponse;
if (params.loginType === 'X') {
signInResponse = await provider.sequenceWaas.signIn({ xAccessToken: idToken }, randomName());
}
else {
signInResponse = await provider.sequenceWaas.signIn({ idToken }, randomName());
}
if (signInResponse?.email) {
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasSignInEmail, signInResponse.email);
}
}
catch (e) {
console.log(e);
await this.disconnect();
throw e;
}
}
}
const accounts = await this.getAccounts();
if (accounts.length) {
await config.storage?.setItem(localStorage_js_1.LocalStorageKey.WaasActiveLoginType, params.loginType);
}
else {
throw new Error('No accounts found');
}
return {
accounts,
chainId: await this.getChainId()
};
},
async disconnect() {
const provider = await this.getProvider();
try {
await provider.sequenceWaas.dropSession({ sessionId: await provider.sequenceWaas.getSessionId(), strict: false });
}
catch (e) {
console.log(e);
}
await config.storage?.removeItem(localStorage_js_1.LocalStorageKey.WaasActiveLoginType);
await config.storage?.removeItem(localStorage_js_1.LocalStorageKey.WaasSignInEmail);
config.emitter.emit('disconnect');
},
async getAccounts() {
const provider = await this.getProvider();
try {
const isSignedIn = await provider.sequenceWaas.isSignedIn();
if (isSignedIn) {
const address = await provider.sequenceWaas.getAddress();
return [(0, viem_1.getAddress)(address)];
}
}
catch (err) {
return [];
}
return [];
},
async getProvider() {
return sequenceWaasProvider;
},
async isAuthorized() {
const provider = await this.getProvider();
const activeWaasOption = await config.storage?.getItem(localStorage_js_1.LocalStorageKey.WaasActiveLoginType);
if (params.loginType !== activeWaasOption) {
return false;
}
try {
return await provider.sequenceWaas.isSignedIn();
}
catch (e) {
return false;
}
},
async switchChain({ chainId }) {
const provider = await this.getProvider();
const chain = config.chains.find(c => c.id === chainId) || config.chains[0];
await provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: (0, viem_1.toHex)(chainId) }]
});
config.emitter.emit('change', { chainId });
return chain;
},
async getChainId() {
const provider = await this.getProvider();
return Number(provider.getChainId());
},
async onAccountsChanged(accounts) {
return { account: accounts[0] };
},
async onChainChanged(chain) {
config.emitter.emit('change', { chainId: (0, helpers_js_1.normalizeChainId)(chain) });
},
async onConnect(_connectInfo) { },
async onDisconnect() {
await this.disconnect();
}
}));
}
class SequenceWaasProvider extends ethers_1.ethers.AbstractProvider {
sequenceWaas;
showConfirmation;
nodesUrl;
jsonRpcProvider;
requestConfirmationHandler;
feeConfirmationHandler;
currentNetwork;
constructor(sequenceWaas, showConfirmation, nodesUrl) {
super(sequenceWaas.config.network);
this.sequenceWaas = sequenceWaas;
this.showConfirmation = showConfirmation;
this.nodesUrl = nodesUrl;
const initialChain = sequenceWaas.config.network;
const initialChainName = network_1.allNetworks.find(n => n.chainId === initialChain || n.name === initialChain)?.name;
const initialJsonRpcProvider = new ethers_1.ethers.JsonRpcProvider(`${nodesUrl}/${initialChainName}/${sequenceWaas.config.projectAccessKey}`);
this.jsonRpcProvider = initialJsonRpcProvider;
this.currentNetwork = ethers_1.ethers.Network.from(sequenceWaas.config.network);
}
async request({ method, params }) {
if (method === 'wallet_switchEthereumChain') {
const chainId = (0, helpers_js_1.normalizeChainId)(params?.[0].chainId);
const networkName = network_1.allNetworks.find(n => n.chainId === chainId)?.name;
const jsonRpcProvider = new ethers_1.ethers.JsonRpcProvider(`${this.nodesUrl}/${networkName}/${this.sequenceWaas.config.projectAccessKey}`);
this.jsonRpcProvider = jsonRpcProvider;
this.currentNetwork = ethers_1.ethers.Network.from(chainId);
return null;
}
if (method === 'eth_chainId') {
return (0, viem_1.toHex)(this.currentNetwork.chainId);
}
if (method === 'eth_accounts') {
const address = await this.sequenceWaas.getAddress();
const account = (0, viem_1.getAddress)(address);
return [account];
}
if (method === 'eth_sendTransaction') {
const txns = await ethers_1.ethers.resolveProperties(params?.[0]);
const chainId = this.getChainId();
let feeOptionsResponse;
try {
feeOptionsResponse = await this.checkTransactionFeeOptions({ transactions: [txns], chainId });
}
catch (error) {
if (isSessionInvalidOrNotFoundError(error)) {
await this.emit('error', error);
throw new viem_1.ProviderDisconnectedError(new Error('Provider is not connected'));
}
else {
const message = typeof error === 'object' && error !== null && 'cause' in error
? String(error.cause) || 'Failed to check transaction fee options'
: 'Failed to check transaction fee options';
throw new viem_1.InternalRpcError(new Error(message));
}
}
const feeOptions = feeOptionsResponse?.feeOptions;
let selectedFeeOption;
if (!feeOptionsResponse?.isSponsored && feeOptions && feeOptions.length > 0) {
if (!this.feeConfirmationHandler) {
throw new viem_1.TransactionRejectedRpcError(new Error('Unable to send transaction: please use useWaasFeeOptions hook and pick a fee option'));
}
const id = (0, uuid_1.v4)();
const confirmation = await this.feeConfirmationHandler.confirmFeeOption(id, feeOptions, txns, chainId);
if (!confirmation.confirmed) {
throw new viem_1.UserRejectedRequestError(new Error('User rejected send transaction request'));
}
if (id !== confirmation.id) {
throw new viem_1.UserRejectedRequestError(new Error('User confirmation ids do not match'));
}
selectedFeeOption = feeOptions.find(feeOption => {
// Handle the case where feeTokenAddress is ZeroAddress and contractAddress is null
if (confirmation.feeTokenAddress === viem_1.zeroAddress && feeOption.token.contractAddress === null) {
return true;
}
return feeOption.token.contractAddress === confirmation.feeTokenAddress;
});
}
if (this.requestConfirmationHandler && this.showConfirmation) {
const id = (0, uuid_1.v4)();
const confirmation = await this.requestConfirmationHandler.confirmSignTransactionRequest(id, txns, chainId);
if (!confirmation.confirmed) {
throw new viem_1.UserRejectedRequestError(new Error('User rejected send transaction request'));
}
if (id !== confirmation.id) {
throw new viem_1.UserRejectedRequestError(new Error('User confirmation ids do not match'));
}
}
let response;
try {
response = await this.sequenceWaas.sendTransaction({
transactions: [await ethers_1.ethers.resolveProperties(params?.[0])],
network: chainId,
transactionsFeeOption: selectedFeeOption,
transactionsFeeQuote: feeOptionsResponse?.feeQuote
});
}
catch (error) {
if (isSessionInvalidOrNotFoundError(error)) {
await this.emit('error', error);
throw new viem_1.ProviderDisconnectedError(new Error('Provider is not connected'));
}
else {
const message = typeof error === 'object' && error !== null && 'cause' in error
? String(error.cause) || 'Failed to send transaction'
: 'Failed to send transaction';
throw new viem_1.InternalRpcError(new Error(message));
}
}
if (response.code === 'transactionFailed') {
// Failed
throw new viem_1.TransactionRejectedRpcError(new Error(`Unable to send transaction: ${response.data.error}`));
}
if (response.code === 'transactionReceipt') {
// Success
const { txHash } = response.data;
return txHash;
}
}
if (method === 'eth_sign' || method === 'personal_sign') {
if (this.requestConfirmationHandler && this.showConfirmation) {
const id = (0, uuid_1.v4)();
const confirmation = await this.requestConfirmationHandler.confirmSignMessageRequest(id, params?.[0], Number(this.currentNetwork.chainId));
if (!confirmation.confirmed) {
throw new viem_1.UserRejectedRequestError(new Error('User rejected sign message request'));
}
if (id !== confirmation.id) {
throw new viem_1.UserRejectedRequestError(new Error('User confirmation ids do not match'));
}
}
let sig;
try {
sig = await this.sequenceWaas.signMessage({ message: params?.[0], network: Number(this.currentNetwork.chainId) });
}
catch (error) {
if (isSessionInvalidOrNotFoundError(error)) {
await this.emit('error', error);
throw new viem_1.ProviderDisconnectedError(new Error('Provider is not connected'));
}
else {
const message = typeof error === 'object' && error !== null && 'cause' in error
? String(error.cause) || 'Failed to sign message'
: 'Failed to sign message';
throw new viem_1.InternalRpcError(new Error(message));
}
}
return sig.data.signature;
}
if (method === 'eth_signTypedData' || method === 'eth_signTypedData_v4') {
if (this.requestConfirmationHandler && this.showConfirmation) {
const id = (0, uuid_1.v4)();
const confirmation = await this.requestConfirmationHandler.confirmSignMessageRequest(id, JSON.stringify(JSON.parse(params?.[1]), null, 2), // Pretty print the typed data for confirmation
Number(this.currentNetwork.chainId));
if (!confirmation.confirmed) {
throw new viem_1.UserRejectedRequestError(new Error('User rejected sign typed data request'));
}
if (id !== confirmation.id) {
throw new viem_1.UserRejectedRequestError(new Error('User confirmation ids do not match'));
}
}
let sig;
try {
sig = await this.sequenceWaas.signTypedData({
typedData: JSON.parse(params?.[1]),
network: Number(this.currentNetwork.chainId)
});
}
catch (error) {
if (isSessionInvalidOrNotFoundError(error)) {
await this.emit('error', error);
throw new viem_1.ProviderDisconnectedError(new Error('Provider is not connected'));
}
else {
const message = typeof error === 'object' && error !== null && 'cause' in error
? String(error.cause) || 'Failed to sign typed data'
: 'Failed to sign typed data';
throw new viem_1.InternalRpcError(new Error(message));
}
}
return sig.data.signature;
}
return await this.jsonRpcProvider.send(method, params ?? []);
}
async getTransaction(txHash) {
return await this.jsonRpcProvider.getTransaction(txHash);
}
detectNetwork() {
return Promise.resolve(this.currentNetwork);
}
getChainId() {
return Number(this.currentNetwork.chainId);
}
async checkTransactionFeeOptions({ transactions, chainId }) {
const resp = await this.sequenceWaas.feeOptions({
transactions: transactions,
network: chainId
});
if (resp.data.feeQuote && resp.data.feeOptions) {
return { feeQuote: resp.data.feeQuote, feeOptions: resp.data.feeOptions, isSponsored: false };
}
return { feeQuote: resp.data.feeQuote, feeOptions: resp.data.feeOptions, isSponsored: true };
}
}
exports.SequenceWaasProvider = SequenceWaasProvider;
const DEVICE_EMOJIS = [
// 256 emojis for unsigned byte range 0 - 255
...'ðķðąððđð°ðĶðŧðžðĻðŊðĶðŪð·ð―ðļðĩðððððð§ðĶðĪðĢðĨðĶðĶ
ðĶðĶðšððīðĶðððĶððððĶðĶð·ðļðĶðĒððĶðĶðĶððĶðĶðĶðĶðĄð ððŽðģððĶðð
ððĶðĶðͧððĶðĶðŠðŦðĶðĶððððððððĶððĶððĐðĶŪðððĶðĶðĶðĶĒðĶĐðððĶðĶĻðĶĄðĶĶðĶĨðððŋðĶðūððēðĩððēðģðīðąðŋðððððĢðððððūðð·ðđðĨðšðļðžðŧðððððððððððððĨððĨĨðĨð
ðĨðĨĶðĨŽðĨðķð―ðĨð§ð§
ðĨð ðĨðĨŊððĨðĨĻð§ðĨðģð§ðĨð§ðĨðĨĐðððĶīðððððĨŠðĨð§ðŪðŊðĨðĨðĨŦðððēððĢðąðĨðĶŠðĪððððĨðĨ ðĨŪðĒðĄð§ðĻðĶðĨ§ð§ð°ððŪððŽðŦðŋðĐðŠð°ðĨððððð
ðððððððððððððððŊð°ðąðēðģðūðŊðšðŧð―ððð'
];
// Generate a random name for the session, using a single random emoji and 2 random words
// from the list of words of ethers
function randomName() {
const wordlistSize = 2048;
const words = ethers_1.ethers.wordlists.en;
const randomEmoji = DEVICE_EMOJIS[Math.floor(Math.random() * DEVICE_EMOJIS.length)];
const randomWord1 = words.getWord(Math.floor(Math.random() * wordlistSize));
const randomWord2 = words.getWord(Math.floor(Math.random() * wordlistSize));
return `${randomEmoji} ${randomWord1} ${randomWord2}`;
}
function isSessionInvalidOrNotFoundError(error) {
return error instanceof waas_1.WebrpcEndpointError && error.cause === 'session invalid or not found';
}
//# sourceMappingURL=sequenceWaasConnector.js.map