@morpho-org/blue-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/package.json +54 -0
- package/src/addresses.ts +261 -0
- package/src/chain/chain.constants.ts +235 -0
- package/src/chain/chain.test.ts +51 -0
- package/src/chain/chain.types.ts +42 -0
- package/src/chain/chain.utils.ts +44 -0
- package/src/chain/index.ts +2 -0
- package/src/constants.ts +18 -0
- package/src/errors.ts +75 -0
- package/src/ethers/ethers.test.ts +17 -0
- package/src/ethers/index.ts +2 -0
- package/src/ethers/safeGetAddress.ts +4 -0
- package/src/ethers/safeParseUnits.ts +29 -0
- package/src/evm.ts +172 -0
- package/src/helpers/format/format.test.ts +340 -0
- package/src/helpers/format/format.ts +416 -0
- package/src/helpers/format/index.ts +1 -0
- package/src/helpers/getChecksumedAddress.ts +15 -0
- package/src/helpers/index.ts +4 -0
- package/src/helpers/isZeroAddressOrUnset.ts +13 -0
- package/src/helpers/locale.ts +108 -0
- package/src/holding/Holding.ts +109 -0
- package/src/holding/index.ts +1 -0
- package/src/index.ts +34 -0
- package/src/market/Market.ts +479 -0
- package/src/market/MarketConfig.ts +108 -0
- package/src/market/MarketUtils.test.ts +25 -0
- package/src/market/MarketUtils.ts +467 -0
- package/src/market/index.ts +3 -0
- package/src/maths/AdaptiveCurveIrmLib.ts +143 -0
- package/src/maths/MathLib.ts +208 -0
- package/src/maths/MathUtils.ts +31 -0
- package/src/maths/SharesMath.ts +40 -0
- package/src/maths/index.ts +4 -0
- package/src/notifications.ts +167 -0
- package/src/position/Position.ts +251 -0
- package/src/position/index.ts +1 -0
- package/src/signatures/index.ts +18 -0
- package/src/signatures/manager.ts +50 -0
- package/src/signatures/permit.ts +126 -0
- package/src/signatures/permit2.ts +120 -0
- package/src/signatures/types.ts +18 -0
- package/src/signatures/utils.ts +83 -0
- package/src/tests/mocks/markets.ts +110 -0
- package/src/token/ERC20Metadata.ts +124 -0
- package/src/token/Token.ts +83 -0
- package/src/token/TokenNamespace.ts +76 -0
- package/src/token/WrappedToken.ts +142 -0
- package/src/token/index.ts +2 -0
- package/src/types.ts +37 -0
- package/src/user/User.ts +32 -0
- package/src/user/index.ts +2 -0
- package/src/user/user.types.ts +23 -0
- package/src/vault/Vault.ts +370 -0
- package/src/vault/VaultAllocation.ts +58 -0
- package/src/vault/VaultConfig.ts +55 -0
- package/src/vault/VaultUtils.ts +47 -0
- package/src/vault/index.ts +4 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { entries, keys, values } from "@morpho-org/morpho-ts";
|
|
2
|
+
|
|
3
|
+
import { ChainId } from "./chain.types";
|
|
4
|
+
import { ChainUtils } from "./chain.utils";
|
|
5
|
+
|
|
6
|
+
describe("Network", () => {
|
|
7
|
+
it("Should have all data for all networks", () => {
|
|
8
|
+
const chainIdLength = ChainUtils.getAllChainIds().length;
|
|
9
|
+
const chainMetadataLength = keys(ChainUtils.chainMetadata).length;
|
|
10
|
+
|
|
11
|
+
expect(chainIdLength).toEqual(chainMetadataLength);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("Should have consistent chainIds", () => {
|
|
15
|
+
entries(ChainUtils.chainMetadata).forEach(([chainId, { id }]) => {
|
|
16
|
+
expect(+chainId).toEqual(id);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("Should have Testnet in the name for testnet chains", () => {
|
|
21
|
+
Object.values(ChainUtils.chainMetadata)
|
|
22
|
+
.filter(({ isTestnet }) => isTestnet)
|
|
23
|
+
.forEach(({ name }) => {
|
|
24
|
+
expect(name).toMatch(/Testnet/);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it("Should convert correctly a chainId to hexChainId", () => {
|
|
28
|
+
expect(
|
|
29
|
+
ChainUtils.toHexChainId(ChainUtils.ChainId.EthGoerliTestnet)
|
|
30
|
+
).toEqual("0x5");
|
|
31
|
+
expect(ChainUtils.toHexChainId(ChainUtils.ChainId.BaseMainnet)).toEqual(
|
|
32
|
+
"0x2105"
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("Should return all testnets", () => {
|
|
37
|
+
expect(ChainUtils.getTestnets()).toEqual(
|
|
38
|
+
values(ChainUtils.chainMetadata)
|
|
39
|
+
.filter(({ isTestnet }) => isTestnet)
|
|
40
|
+
.map(({ id }) => id)
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
it("Should return all networks", () => {
|
|
44
|
+
expect(ChainUtils.getAllChainIds()).toEqual(
|
|
45
|
+
values(ChainUtils.chainMetadata).map(({ id }) => id)
|
|
46
|
+
);
|
|
47
|
+
expect(
|
|
48
|
+
ChainUtils.getAllChainLabels().map((label) => ChainId[label])
|
|
49
|
+
).toEqual(ChainUtils.getAllChainIds());
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export enum ChainId {
|
|
2
|
+
EthMainnet = 1,
|
|
3
|
+
EthGoerliTestnet = 5,
|
|
4
|
+
BaseMainnet = 8453,
|
|
5
|
+
// PolygonMainnet = 137,
|
|
6
|
+
// MumbaiTestnet = 80001,
|
|
7
|
+
// OptimismMainnet = 10,
|
|
8
|
+
// BscMainnet = 56,
|
|
9
|
+
// ArbitrumMainnet = 42161,
|
|
10
|
+
// ArbitrumTestnet = 421611,
|
|
11
|
+
// GnosisChain = 100,
|
|
12
|
+
// FantomMainnet = 250,
|
|
13
|
+
// FantomTestnet = 4002,
|
|
14
|
+
// HarmonyMainnet = 128,
|
|
15
|
+
// HarmonyTestnet = 1666600000,
|
|
16
|
+
// BscTestnet = 97,
|
|
17
|
+
// OptimismTestnet = 69,
|
|
18
|
+
// EthRopstenTestnet = 3,
|
|
19
|
+
// EthRinkebyTestnet = 4,
|
|
20
|
+
// EthKovanTestnet = 42,
|
|
21
|
+
// GnosisChainTestnet = 10200,
|
|
22
|
+
// AvalancheMainnet = 43114,
|
|
23
|
+
// AvalancheFujiTestnet = 43113,
|
|
24
|
+
// MoonbaseAlphaTestnet = 1287,
|
|
25
|
+
// BaseGoerliTestnet = 84531,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ChainMetadata {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly id: ChainId;
|
|
31
|
+
readonly defaultRpcUrl: string;
|
|
32
|
+
readonly explorerUrl: string;
|
|
33
|
+
readonly nativeCurrency: {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly symbol: string;
|
|
36
|
+
readonly decimals: number;
|
|
37
|
+
};
|
|
38
|
+
readonly isTestnet: boolean;
|
|
39
|
+
readonly shortName: string;
|
|
40
|
+
readonly identifier: string;
|
|
41
|
+
readonly logoSrc: string;
|
|
42
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { values } from "@morpho-org/morpho-ts";
|
|
2
|
+
|
|
3
|
+
import { UnsupportedChainIdError } from "../errors";
|
|
4
|
+
|
|
5
|
+
import { BLUE_AVAILABLE_CHAINS, CHAIN_METADATA } from "./chain.constants";
|
|
6
|
+
import {
|
|
7
|
+
ChainId as _ChainId,
|
|
8
|
+
ChainMetadata as _ChainMetadata,
|
|
9
|
+
} from "./chain.types";
|
|
10
|
+
|
|
11
|
+
export namespace ChainUtils {
|
|
12
|
+
export const blueAvailableChains = BLUE_AVAILABLE_CHAINS;
|
|
13
|
+
export const chainMetadata = CHAIN_METADATA;
|
|
14
|
+
|
|
15
|
+
export interface ChainMetadata extends _ChainMetadata {}
|
|
16
|
+
export import ChainId = _ChainId;
|
|
17
|
+
|
|
18
|
+
export const toHexChainId = (chainId: ChainId) => {
|
|
19
|
+
return `0x${chainId.toString(16)}`;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const getAllChainIds = () =>
|
|
23
|
+
values(ChainId).filter((value) => typeof value === "number");
|
|
24
|
+
|
|
25
|
+
export const getAllChainLabels = () =>
|
|
26
|
+
Object.values(ChainId).filter(
|
|
27
|
+
(value) => typeof value === "string"
|
|
28
|
+
) as (keyof typeof ChainId)[];
|
|
29
|
+
|
|
30
|
+
export const getTestnets = () =>
|
|
31
|
+
getAllChainIds().filter((chainId) => CHAIN_METADATA[chainId].isTestnet);
|
|
32
|
+
|
|
33
|
+
export function isSupported(chainId: number): chainId is ChainId {
|
|
34
|
+
return blueAvailableChains.includes(chainId as ChainId);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function parseSupportedChainId(candidate: any): ChainId {
|
|
38
|
+
const chainId = parseInt(candidate);
|
|
39
|
+
|
|
40
|
+
if (!isSupported(chainId)) throw new UnsupportedChainIdError(candidate);
|
|
41
|
+
|
|
42
|
+
return chainId;
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { parseUnits, parseEther } from "ethers";
|
|
2
|
+
|
|
3
|
+
import { Time } from "@morpho-org/morpho-ts";
|
|
4
|
+
|
|
5
|
+
export const LIQUIDATION_CURSOR = parseEther("0.3");
|
|
6
|
+
|
|
7
|
+
export const MAX_LIQUIDATION_INCENTIVE_FACTOR = parseEther("1.15");
|
|
8
|
+
|
|
9
|
+
export const ORACLE_PRICE_OFFSET = 36;
|
|
10
|
+
export const ORACLE_PRICE_SCALE = parseUnits("1", ORACLE_PRICE_OFFSET);
|
|
11
|
+
|
|
12
|
+
export const MAX_FEE = parseEther("0.25"); // 25%
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_SLIPPAGE_TOLERANCE = parseEther("0.0003"); // 0.03%
|
|
15
|
+
|
|
16
|
+
export const DEFAULT_TARGET_UTILIZATION = parseEther("0.92"); // 92%
|
|
17
|
+
|
|
18
|
+
export const SECONDS_PER_YEAR = Time.s.from.y(1n);
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Address, MarketId } from "./types";
|
|
2
|
+
|
|
3
|
+
export class UnknownDataError extends Error {}
|
|
4
|
+
|
|
5
|
+
export class UnknownTokenError extends UnknownDataError {
|
|
6
|
+
constructor(public readonly address: Address) {
|
|
7
|
+
super(`unknown token ${address}`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class UnknownTokenPriceError extends UnknownDataError {
|
|
12
|
+
constructor(public readonly address: Address) {
|
|
13
|
+
super(`unknown price of token ${address}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class UnknownMarketConfigError extends UnknownDataError {
|
|
18
|
+
constructor(public readonly marketId: MarketId) {
|
|
19
|
+
super(`unknown config for market ${marketId}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class UnknownVaultConfigError extends UnknownDataError {
|
|
24
|
+
constructor(public readonly vault: Address) {
|
|
25
|
+
super(`unknown config for vault ${vault}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class UnsupportedChainIdError extends Error {
|
|
30
|
+
constructor(public readonly chainId: number) {
|
|
31
|
+
super(`unsupported chain ${chainId}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class InvalidInterestAccrualError extends UnknownDataError {
|
|
36
|
+
constructor(
|
|
37
|
+
public readonly marketId: MarketId,
|
|
38
|
+
public readonly timestamp: bigint,
|
|
39
|
+
public readonly lastUpdate: bigint
|
|
40
|
+
) {
|
|
41
|
+
super(
|
|
42
|
+
`invalid interest accrual on market ${marketId}: accrual timestamp ${timestamp} can't be prior to last update ${lastUpdate}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class InvalidSignatureError extends Error {
|
|
48
|
+
constructor(
|
|
49
|
+
public readonly hash: string,
|
|
50
|
+
public readonly signer: Address,
|
|
51
|
+
public readonly recovered: Address
|
|
52
|
+
) {
|
|
53
|
+
super(
|
|
54
|
+
`invalid signature for hash ${hash}: expected ${signer}, recovered ${recovered}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type ErrorClass = new (...args: any[]) => Error;
|
|
60
|
+
export function _try<T>(
|
|
61
|
+
accessor: () => T,
|
|
62
|
+
...errorClasses: ErrorClass[]
|
|
63
|
+
): T | undefined {
|
|
64
|
+
try {
|
|
65
|
+
return accessor();
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (
|
|
68
|
+
errorClasses.length === 0 ||
|
|
69
|
+
errorClasses.some((errorClass) => error instanceof errorClass)
|
|
70
|
+
)
|
|
71
|
+
return;
|
|
72
|
+
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { safeParseNumber } from "./safeParseUnits";
|
|
2
|
+
|
|
3
|
+
describe("safeParseNumber", () => {
|
|
4
|
+
it("should parse excessively small number", () => {
|
|
5
|
+
expect(safeParseNumber(0.000000000000000000000000000000042, 18)).toEqual(
|
|
6
|
+
0n
|
|
7
|
+
);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("should parse excessively large number", () => {
|
|
11
|
+
expect(
|
|
12
|
+
safeParseNumber(4200000000000000000000000000000000000, 18).toString()
|
|
13
|
+
).toEqual(
|
|
14
|
+
4200000000000000000000000000000000000000000000000000000n.toString()
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { parseUnits } from "ethers";
|
|
2
|
+
|
|
3
|
+
export const safeParseUnits = (strValue: string, decimals = 18) => {
|
|
4
|
+
if (!/[-+]?[0-9]*\.?[0-9]+/.test(strValue))
|
|
5
|
+
throw new Error("invalid number: " + strValue);
|
|
6
|
+
|
|
7
|
+
let [whole, dec = ""] = strValue.split(".");
|
|
8
|
+
|
|
9
|
+
dec = dec.slice(0, decimals);
|
|
10
|
+
|
|
11
|
+
return parseUnits(
|
|
12
|
+
[whole || "0", dec].filter((v) => v.length > 0).join("."),
|
|
13
|
+
decimals
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Alternative to Number.toFixed that doesn't use scientific notation for excessively small or large numbers
|
|
18
|
+
function toFixed(x: number, decimals: number) {
|
|
19
|
+
return new Intl.NumberFormat("en-US", {
|
|
20
|
+
style: "decimal",
|
|
21
|
+
useGrouping: false,
|
|
22
|
+
maximumFractionDigits: decimals,
|
|
23
|
+
minimumFractionDigits: decimals,
|
|
24
|
+
}).format(x);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function safeParseNumber(value: number, decimals = 18) {
|
|
28
|
+
return safeParseUnits(toFixed(value, decimals), decimals);
|
|
29
|
+
}
|
package/src/evm.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TransactionRequest,
|
|
3
|
+
Signer,
|
|
4
|
+
parseUnits,
|
|
5
|
+
TransactionResponse,
|
|
6
|
+
} from "ethers";
|
|
7
|
+
|
|
8
|
+
import { ChainId } from "./chain";
|
|
9
|
+
import { MathLib } from "./maths";
|
|
10
|
+
import {
|
|
11
|
+
NotificationProducer,
|
|
12
|
+
NotificationStatus,
|
|
13
|
+
NotificationTopic,
|
|
14
|
+
} from "./notifications";
|
|
15
|
+
import { SignatureUtils } from "./signatures";
|
|
16
|
+
import { SignatureMessage } from "./signatures/types";
|
|
17
|
+
|
|
18
|
+
export interface NotificationOptions<Topic extends NotificationTopic> {
|
|
19
|
+
producer: NotificationProducer<Topic>;
|
|
20
|
+
id: string;
|
|
21
|
+
args: Record<PropertyKey, any>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sends a transaction via the provided signer, optionnally waiting for the corresponding transaction receipt.
|
|
26
|
+
* @param signer The signer to send the tx with.
|
|
27
|
+
* @param tx The transaction request.
|
|
28
|
+
* @param wait Whether to wait for the transaction receipt. Defaults to true.
|
|
29
|
+
* @param notificationOptions The optional notification options. Warning: if `wait` is set to true, the provided topic will never complete.
|
|
30
|
+
*/
|
|
31
|
+
export const sendTransactionWithProducer = async (
|
|
32
|
+
signer: Signer,
|
|
33
|
+
req: TransactionRequest,
|
|
34
|
+
defaultGasLimit?: bigint,
|
|
35
|
+
confirms = 1,
|
|
36
|
+
notificationOptions?: NotificationOptions<NotificationTopic.tx>
|
|
37
|
+
) => {
|
|
38
|
+
let response: TransactionResponse | undefined;
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
let gasLimit: bigint;
|
|
42
|
+
try {
|
|
43
|
+
gasLimit = MathLib.wMulUp(
|
|
44
|
+
await signer.estimateGas(req),
|
|
45
|
+
parseUnits("1.1")
|
|
46
|
+
);
|
|
47
|
+
} catch (error: any) {
|
|
48
|
+
if (defaultGasLimit == null) throw error;
|
|
49
|
+
|
|
50
|
+
gasLimit = defaultGasLimit;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const tx = { ...req, gasLimit };
|
|
54
|
+
|
|
55
|
+
notificationOptions?.producer.next({
|
|
56
|
+
id: notificationOptions.id,
|
|
57
|
+
status: NotificationStatus.signing,
|
|
58
|
+
context: { tx },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
response = await signer.sendTransaction(tx);
|
|
62
|
+
|
|
63
|
+
notificationOptions?.producer.next({
|
|
64
|
+
id: notificationOptions.id,
|
|
65
|
+
status: NotificationStatus.pending,
|
|
66
|
+
context: { args: notificationOptions.args, tx, response },
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const receipt = await response.wait(confirms);
|
|
70
|
+
|
|
71
|
+
notificationOptions?.producer.next({
|
|
72
|
+
id: notificationOptions.id,
|
|
73
|
+
status: NotificationStatus.success,
|
|
74
|
+
context: { args: notificationOptions.args, tx, response, receipt },
|
|
75
|
+
});
|
|
76
|
+
} catch (error: any) {
|
|
77
|
+
notificationOptions?.producer.next({
|
|
78
|
+
id: notificationOptions.id,
|
|
79
|
+
status: NotificationStatus.error,
|
|
80
|
+
context: { args: notificationOptions.args, tx: req, response, error },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Sends a transaction via the provided signer, optionnally waiting for the corresponding transaction receipt.
|
|
87
|
+
* @param signer The signer to send the tx with.
|
|
88
|
+
* @param tx The transaction request.
|
|
89
|
+
* @param wait Whether to wait for the transaction receipt. Defaults to true.
|
|
90
|
+
* @param topic The type of notifications to emit.
|
|
91
|
+
* @param args The optional notification arguments.
|
|
92
|
+
* @return The notification consumer which receives the transaction notifications.
|
|
93
|
+
*/
|
|
94
|
+
export const sendTransaction = (
|
|
95
|
+
signer: Signer,
|
|
96
|
+
tx: TransactionRequest,
|
|
97
|
+
defaultGasLimit?: bigint,
|
|
98
|
+
confirms?: number,
|
|
99
|
+
args: Record<PropertyKey, any> = {}
|
|
100
|
+
) => {
|
|
101
|
+
const producer = new NotificationProducer(NotificationTopic.tx);
|
|
102
|
+
|
|
103
|
+
sendTransactionWithProducer(signer, tx, defaultGasLimit, confirms, {
|
|
104
|
+
producer,
|
|
105
|
+
id: Date.now().toString(),
|
|
106
|
+
args,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return producer.consumer;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const signMessageWithProducer = async (
|
|
113
|
+
signer: Signer,
|
|
114
|
+
message: SignatureMessage,
|
|
115
|
+
notificationOptions?: NotificationOptions<NotificationTopic.signature>
|
|
116
|
+
) => {
|
|
117
|
+
notificationOptions?.producer.next({
|
|
118
|
+
id: notificationOptions.id,
|
|
119
|
+
status: NotificationStatus.signing,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const signature = await SignatureUtils.safeSignTypedData(
|
|
124
|
+
signer,
|
|
125
|
+
message.data.domain,
|
|
126
|
+
message.data.types,
|
|
127
|
+
message.data.value
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
SignatureUtils.verifySignature(
|
|
131
|
+
signature,
|
|
132
|
+
message.hash,
|
|
133
|
+
await signer.getAddress()
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
notificationOptions?.producer.next({
|
|
137
|
+
id: notificationOptions.id,
|
|
138
|
+
status: NotificationStatus.success,
|
|
139
|
+
context: { message, args: notificationOptions.args, signature },
|
|
140
|
+
});
|
|
141
|
+
} catch (error: any) {
|
|
142
|
+
notificationOptions?.producer.next({
|
|
143
|
+
id: notificationOptions.id,
|
|
144
|
+
status: NotificationStatus.error,
|
|
145
|
+
context: { message, args: notificationOptions.args, error },
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Sends a signature request to the provided signer.
|
|
152
|
+
* @param signer The signer to send the approval with.
|
|
153
|
+
* @param args The approval parameters.
|
|
154
|
+
* @param encodeMessage The approval encoder to pass the approval parameters to get the approval transactions.
|
|
155
|
+
* @return The notification consumer which receives the transaction notifications.
|
|
156
|
+
*/
|
|
157
|
+
export const signMessage = <T extends object>(
|
|
158
|
+
signer: Signer,
|
|
159
|
+
args: T,
|
|
160
|
+
encodeMessage: (args: T, chainId: ChainId) => SignatureMessage,
|
|
161
|
+
chainId: ChainId
|
|
162
|
+
) => {
|
|
163
|
+
const producer = new NotificationProducer(NotificationTopic.signature);
|
|
164
|
+
|
|
165
|
+
signMessageWithProducer(signer, encodeMessage(args, chainId), {
|
|
166
|
+
producer,
|
|
167
|
+
id: Date.now().toString(),
|
|
168
|
+
args,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return producer.consumer;
|
|
172
|
+
};
|