@across-protocol/sdk 4.3.51 → 4.3.53-alpha.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/dist/cjs/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.js +12 -12
- package/dist/cjs/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.js.map +1 -1
- package/dist/cjs/coingecko/Coingecko.d.ts +7 -0
- package/dist/cjs/coingecko/Coingecko.js +71 -0
- package/dist/cjs/coingecko/Coingecko.js.map +1 -1
- package/dist/cjs/pool/poolClient.d.ts +12 -16
- package/dist/cjs/pool/poolClient.js +1 -1
- package/dist/cjs/pool/poolClient.js.map +1 -1
- package/dist/cjs/pool/uma/across/constants.d.ts +2 -0
- package/dist/cjs/pool/uma/across/constants.js +6 -0
- package/dist/cjs/pool/uma/across/constants.js.map +1 -0
- package/dist/cjs/pool/uma/across/index.d.ts +2 -0
- package/dist/cjs/pool/uma/across/index.js +8 -0
- package/dist/cjs/pool/uma/across/index.js.map +1 -0
- package/dist/cjs/pool/uma/across/transactionManager.d.ts +12 -0
- package/dist/cjs/pool/uma/across/transactionManager.js +121 -0
- package/dist/cjs/pool/uma/across/transactionManager.js.map +1 -0
- package/dist/cjs/pool/uma/clients/erc20/client.d.ts +26 -0
- package/dist/cjs/pool/uma/clients/erc20/client.js +39 -0
- package/dist/cjs/pool/uma/clients/erc20/client.js.map +1 -0
- package/dist/cjs/pool/uma/clients/erc20/index.d.ts +1 -0
- package/dist/cjs/pool/uma/clients/erc20/index.js +5 -0
- package/dist/cjs/pool/uma/clients/erc20/index.js.map +1 -0
- package/dist/cjs/pool/uma/clients/index.d.ts +1 -0
- package/dist/cjs/pool/uma/clients/index.js +6 -0
- package/dist/cjs/pool/uma/clients/index.js.map +1 -0
- package/dist/cjs/pool/uma/index.d.ts +13 -0
- package/dist/cjs/pool/uma/index.js +9 -0
- package/dist/cjs/pool/uma/index.js.map +1 -0
- package/dist/cjs/pool/uma/oracle/index.d.ts +1 -0
- package/dist/cjs/pool/uma/oracle/index.js +6 -0
- package/dist/cjs/pool/uma/oracle/index.js.map +1 -0
- package/dist/cjs/pool/uma/oracle/utils.d.ts +7 -0
- package/dist/cjs/pool/uma/oracle/utils.js +21 -0
- package/dist/cjs/pool/uma/oracle/utils.js.map +1 -0
- package/dist/cjs/pool/uma/utils.d.ts +17 -0
- package/dist/cjs/pool/uma/utils.js +69 -0
- package/dist/cjs/pool/uma/utils.js.map +1 -0
- package/dist/esm/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.js +12 -12
- package/dist/esm/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.js.map +1 -1
- package/dist/esm/coingecko/Coingecko.d.ts +7 -0
- package/dist/esm/coingecko/Coingecko.js +71 -0
- package/dist/esm/coingecko/Coingecko.js.map +1 -1
- package/dist/esm/pool/poolClient.d.ts +12 -16
- package/dist/esm/pool/poolClient.js +1 -1
- package/dist/esm/pool/poolClient.js.map +1 -1
- package/dist/esm/pool/uma/across/constants.d.ts +2 -0
- package/dist/esm/pool/uma/across/constants.js +3 -0
- package/dist/esm/pool/uma/across/constants.js.map +1 -0
- package/dist/esm/pool/uma/across/index.d.ts +2 -0
- package/dist/esm/pool/uma/across/index.js +4 -0
- package/dist/esm/pool/uma/across/index.js.map +1 -0
- package/dist/esm/pool/uma/across/transactionManager.d.ts +12 -0
- package/dist/esm/pool/uma/across/transactionManager.js +122 -0
- package/dist/esm/pool/uma/across/transactionManager.js.map +1 -0
- package/dist/esm/pool/uma/clients/erc20/client.d.ts +26 -0
- package/dist/esm/pool/uma/clients/erc20/client.js +35 -0
- package/dist/esm/pool/uma/clients/erc20/client.js.map +1 -0
- package/dist/esm/pool/uma/clients/erc20/index.d.ts +1 -0
- package/dist/esm/pool/uma/clients/erc20/index.js +2 -0
- package/dist/esm/pool/uma/clients/erc20/index.js.map +1 -0
- package/dist/esm/pool/uma/clients/index.d.ts +1 -0
- package/dist/esm/pool/uma/clients/index.js +3 -0
- package/dist/esm/pool/uma/clients/index.js.map +1 -0
- package/dist/esm/pool/uma/index.d.ts +13 -0
- package/dist/esm/pool/uma/index.js +9 -0
- package/dist/esm/pool/uma/index.js.map +1 -0
- package/dist/esm/pool/uma/oracle/index.d.ts +1 -0
- package/dist/esm/pool/uma/oracle/index.js +3 -0
- package/dist/esm/pool/uma/oracle/index.js.map +1 -0
- package/dist/esm/pool/uma/oracle/utils.d.ts +23 -0
- package/dist/esm/pool/uma/oracle/utils.js +33 -0
- package/dist/esm/pool/uma/oracle/utils.js.map +1 -0
- package/dist/esm/pool/uma/utils.d.ts +17 -0
- package/dist/esm/pool/uma/utils.js +68 -0
- package/dist/esm/pool/uma/utils.js.map +1 -0
- package/dist/types/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.d.ts.map +1 -1
- package/dist/types/coingecko/Coingecko.d.ts +7 -0
- package/dist/types/coingecko/Coingecko.d.ts.map +1 -1
- package/dist/types/pool/poolClient.d.ts +12 -16
- package/dist/types/pool/poolClient.d.ts.map +1 -1
- package/dist/types/pool/uma/across/constants.d.ts +3 -0
- package/dist/types/pool/uma/across/constants.d.ts.map +1 -0
- package/dist/types/pool/uma/across/index.d.ts +3 -0
- package/dist/types/pool/uma/across/index.d.ts.map +1 -0
- package/dist/types/pool/uma/across/transactionManager.d.ts +13 -0
- package/dist/types/pool/uma/across/transactionManager.d.ts.map +1 -0
- package/dist/types/pool/uma/clients/erc20/client.d.ts +27 -0
- package/dist/types/pool/uma/clients/erc20/client.d.ts.map +1 -0
- package/dist/types/pool/uma/clients/erc20/index.d.ts +2 -0
- package/dist/types/pool/uma/clients/erc20/index.d.ts.map +1 -0
- package/dist/types/pool/uma/clients/index.d.ts +2 -0
- package/dist/types/pool/uma/clients/index.d.ts.map +1 -0
- package/dist/types/pool/uma/index.d.ts +14 -0
- package/dist/types/pool/uma/index.d.ts.map +1 -0
- package/dist/types/pool/uma/oracle/index.d.ts +2 -0
- package/dist/types/pool/uma/oracle/index.d.ts.map +1 -0
- package/dist/types/pool/uma/oracle/utils.d.ts +24 -0
- package/dist/types/pool/uma/oracle/utils.d.ts.map +1 -0
- package/dist/types/pool/uma/utils.d.ts +18 -0
- package/dist/types/pool/uma/utils.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.ts +3 -3
- package/src/coingecko/Coingecko.ts +69 -0
- package/src/pool/poolClient.ts +1 -1
- package/src/pool/uma/across/constants.ts +2 -0
- package/src/pool/uma/across/index.ts +2 -0
- package/src/pool/uma/across/transactionManager.ts +73 -0
- package/src/pool/uma/clients/erc20/README.md +29 -0
- package/src/pool/uma/clients/erc20/client.e2e.ts +26 -0
- package/src/pool/uma/clients/erc20/client.ts +67 -0
- package/src/pool/uma/clients/erc20/index.ts +1 -0
- package/src/pool/uma/clients/index.ts +1 -0
- package/src/pool/uma/index.ts +33 -0
- package/src/pool/uma/oracle/index.ts +2 -0
- package/src/pool/uma/oracle/utils.ts +38 -0
- package/src/pool/uma/utils.test.ts +24 -0
- package/src/pool/uma/utils.ts +53 -0
|
@@ -29,6 +29,12 @@ type PriceCache = {
|
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
type PriceCacheBySymbol = {
|
|
33
|
+
[symbol: string]: {
|
|
34
|
+
[currency: string]: Omit<CoinGeckoPrice, "address">;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
32
38
|
type CGTokenPrice = {
|
|
33
39
|
[currency: string]: number;
|
|
34
40
|
last_updated_at: number;
|
|
@@ -61,6 +67,7 @@ export type HistoricPriceChartData = {
|
|
|
61
67
|
export class Coingecko {
|
|
62
68
|
private static instance: Coingecko | undefined;
|
|
63
69
|
private prices: PriceCache;
|
|
70
|
+
private pricesBySymbol: PriceCacheBySymbol;
|
|
64
71
|
private _maxPriceAge = 300; // seconds
|
|
65
72
|
|
|
66
73
|
// Retry configuration.
|
|
@@ -103,6 +110,7 @@ export class Coingecko {
|
|
|
103
110
|
private readonly customPlatformIdMap?: Record<number, string>
|
|
104
111
|
) {
|
|
105
112
|
this.prices = {};
|
|
113
|
+
this.pricesBySymbol = {};
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
protected async getPlatformId(chainId: number): Promise<string> {
|
|
@@ -281,6 +289,29 @@ export class Coingecko {
|
|
|
281
289
|
return [tokenPrice.timestamp.toString(), tokenPrice.price];
|
|
282
290
|
}
|
|
283
291
|
|
|
292
|
+
async getCurrentPriceBySymbol(symbol: string, currency = "usd"): Promise<[string, number]> {
|
|
293
|
+
let tokenPrice = this.getCachedSymbolPrice(symbol, currency);
|
|
294
|
+
if (tokenPrice === undefined) {
|
|
295
|
+
const result = await this.call<Record<string, CGTokenPrice>>(
|
|
296
|
+
`simple/price?symbols=${symbol}&vs_currencies=${currency}&include_last_updated_at=true`
|
|
297
|
+
);
|
|
298
|
+
const cgPrice = result?.[symbol.toLowerCase()];
|
|
299
|
+
if (cgPrice === undefined || !cgPrice?.[currency]) {
|
|
300
|
+
const errMsg = `Failed to retrieve ${symbol}/${currency} price via Coingecko API`;
|
|
301
|
+
this.logger.debug({
|
|
302
|
+
at: "Coingecko#getCurrentPriceBySymbol",
|
|
303
|
+
message: errMsg,
|
|
304
|
+
});
|
|
305
|
+
throw new Error(errMsg);
|
|
306
|
+
} else {
|
|
307
|
+
this.updatePriceCacheBySymbol(cgPrice, symbol, currency);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
tokenPrice = this.getCachedSymbolPrice(symbol, currency);
|
|
311
|
+
assert(tokenPrice !== undefined);
|
|
312
|
+
return [tokenPrice.timestamp.toString(), tokenPrice.price];
|
|
313
|
+
}
|
|
314
|
+
|
|
284
315
|
// Return an array of spot prices for an array of collateral addresses in one async call. Note we might in future
|
|
285
316
|
// This was adapted from packages/merkle-distributor/kpi-options-helpers/calculate-uma-tvl.ts
|
|
286
317
|
async getContractPrices(
|
|
@@ -381,6 +412,11 @@ export class Coingecko {
|
|
|
381
412
|
return this.prices[platform_id][currency];
|
|
382
413
|
}
|
|
383
414
|
|
|
415
|
+
protected getPriceCacheBySymbol(symbol: string): { [currency: string]: Omit<CoinGeckoPrice, "address"> } {
|
|
416
|
+
if (this.pricesBySymbol[symbol] === undefined) this.pricesBySymbol[symbol] = {};
|
|
417
|
+
return this.pricesBySymbol[symbol];
|
|
418
|
+
}
|
|
419
|
+
|
|
384
420
|
protected getCachedAddressPrice(
|
|
385
421
|
contractAddress: string,
|
|
386
422
|
currency: string,
|
|
@@ -409,6 +445,16 @@ export class Coingecko {
|
|
|
409
445
|
}
|
|
410
446
|
}
|
|
411
447
|
|
|
448
|
+
protected getCachedSymbolPrice(symbol: string, currency: string): Omit<CoinGeckoPrice, "address"> | undefined {
|
|
449
|
+
const priceCache = this.getPriceCacheBySymbol(symbol);
|
|
450
|
+
const now: number = msToS(Date.now());
|
|
451
|
+
const tokenPrice: Omit<CoinGeckoPrice, "address"> | undefined = priceCache[currency];
|
|
452
|
+
if (tokenPrice === undefined || tokenPrice.timestamp + this.maxPriceAge <= now) {
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
return tokenPrice;
|
|
456
|
+
}
|
|
457
|
+
|
|
412
458
|
protected updatePriceCache(cgPrice: CGTokenPrice, contractAddress: string, currency: string, platform_id: string) {
|
|
413
459
|
const priceCache = this.getPriceCache(currency, platform_id);
|
|
414
460
|
if (priceCache[contractAddress] === undefined) {
|
|
@@ -433,6 +479,29 @@ export class Coingecko {
|
|
|
433
479
|
}
|
|
434
480
|
}
|
|
435
481
|
|
|
482
|
+
protected updatePriceCacheBySymbol(cgPrice: CGTokenPrice, symbol: string, currency: string) {
|
|
483
|
+
const priceCache = this.getPriceCacheBySymbol(symbol);
|
|
484
|
+
if (priceCache[currency] === undefined) {
|
|
485
|
+
priceCache[currency] = { price: 0, timestamp: 0 };
|
|
486
|
+
}
|
|
487
|
+
if (cgPrice.last_updated_at > priceCache[currency].timestamp) {
|
|
488
|
+
priceCache[currency] = {
|
|
489
|
+
price: cgPrice[currency],
|
|
490
|
+
timestamp: cgPrice.last_updated_at,
|
|
491
|
+
};
|
|
492
|
+
this.logger.debug({
|
|
493
|
+
at: "Coingecko#updatePriceCacheBySymbol",
|
|
494
|
+
message: `Updated ${symbol}/${currency} token price cache.`,
|
|
495
|
+
});
|
|
496
|
+
} else {
|
|
497
|
+
this.logger.debug({
|
|
498
|
+
at: "Coingecko#updatePriceCacheBySymbol",
|
|
499
|
+
message: `No new price available for symbol ${symbol}.`,
|
|
500
|
+
token: cgPrice,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
436
505
|
private async _callBasic(path: string, timeout?: number) {
|
|
437
506
|
const url = `${this.host}/${path}`;
|
|
438
507
|
|
package/src/pool/poolClient.ts
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { Signer } from "ethers";
|
|
3
|
+
import { TransactionRequest, TransactionReceipt } from "@ethersproject/abstract-provider";
|
|
4
|
+
|
|
5
|
+
function makeKey(tx: TransactionRequest) {
|
|
6
|
+
return JSON.stringify(
|
|
7
|
+
Object.entries(tx).map(([key, value]) => {
|
|
8
|
+
return [key, (value || "").toString()];
|
|
9
|
+
})
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Config = {
|
|
14
|
+
confirmations?: number;
|
|
15
|
+
};
|
|
16
|
+
export type Emit = (event: string, key: string, data: TransactionReceipt | string | TransactionRequest | Error) => void;
|
|
17
|
+
export default (config: Config, signer: Signer, emit: Emit = () => null) => {
|
|
18
|
+
assert(signer.provider, "signer requires a provider, use signer.connect(provider)");
|
|
19
|
+
const { confirmations = 3 } = config;
|
|
20
|
+
const requests = new Map<string, TransactionRequest>();
|
|
21
|
+
const submissions = new Map<string, string>();
|
|
22
|
+
const mined = new Map<string, TransactionReceipt>();
|
|
23
|
+
function request(unsignedTx: TransactionRequest) {
|
|
24
|
+
// this no longer calls signer.populateTransaction, to allow metamask to fill in missing details instead
|
|
25
|
+
// use overrides if you want to manually fill in other tx details, including the overrides.customData field.
|
|
26
|
+
const populated = unsignedTx;
|
|
27
|
+
const key = makeKey(populated);
|
|
28
|
+
assert(!requests.has(key), "Transaction already in progress");
|
|
29
|
+
requests.set(key, populated);
|
|
30
|
+
return key;
|
|
31
|
+
}
|
|
32
|
+
async function processRequest(key: string) {
|
|
33
|
+
const request = requests.get(key);
|
|
34
|
+
assert(request, "invalid request");
|
|
35
|
+
// always delete request, it should only be submitted once
|
|
36
|
+
requests.delete(key);
|
|
37
|
+
try {
|
|
38
|
+
const sent = await signer.sendTransaction(request);
|
|
39
|
+
submissions.set(key, sent.hash);
|
|
40
|
+
emit("submitted", key, sent.hash);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
emit("error", key, err as Error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function processSubmission(key: string) {
|
|
46
|
+
const hash = submissions.get(key);
|
|
47
|
+
assert(hash, "invalid submission");
|
|
48
|
+
assert(signer.provider, "signer requires a provider, use signer.connect(provider)");
|
|
49
|
+
// we look for this transaction, but it may never find it if its sped up
|
|
50
|
+
const receipt = await signer.provider.getTransactionReceipt(hash).catch(() => undefined);
|
|
51
|
+
if (receipt == null) return;
|
|
52
|
+
if (receipt.confirmations < confirmations) return;
|
|
53
|
+
submissions.delete(key);
|
|
54
|
+
mined.set(key, receipt);
|
|
55
|
+
emit("mined", key, receipt);
|
|
56
|
+
}
|
|
57
|
+
function isMined(key: string) {
|
|
58
|
+
return Promise.resolve(mined.get(key));
|
|
59
|
+
}
|
|
60
|
+
async function update() {
|
|
61
|
+
for (const key of Array.from(requests.keys())) {
|
|
62
|
+
await processRequest(key);
|
|
63
|
+
}
|
|
64
|
+
for (const key of Array.from(submissions.keys())) {
|
|
65
|
+
await processSubmission(key);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
request,
|
|
70
|
+
isMined,
|
|
71
|
+
update,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# UMA SDK ERC20 Client
|
|
2
|
+
|
|
3
|
+
Client to interface with ERC20 style tokens, based on Ethers and typechain.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import {ethers} from 'ethers'
|
|
9
|
+
|
|
10
|
+
// assume you have a url injected from env
|
|
11
|
+
const provider = new ethers.providers.WebSocketProvider(env.CUSTOM_NODE_URL)
|
|
12
|
+
|
|
13
|
+
// get the contract instance
|
|
14
|
+
const erc20Address:string = // assume you have an emp address you want to connect to
|
|
15
|
+
const erc20Instance:uma.clients.erc20.Instance = uma.clients.erc20.connect(erc20Address,provider)
|
|
16
|
+
|
|
17
|
+
// gets all emp events, see ethers queryFilter for details on contructing the query.
|
|
18
|
+
const events = await erc20Instance.queryFilter({})
|
|
19
|
+
|
|
20
|
+
// returns EventState, defined in the emp client. This can contain user balances as well as approval limits.
|
|
21
|
+
const state:uma.clients.erc20.EventState = uma.clients.erc20.getEventState(events)
|
|
22
|
+
|
|
23
|
+
// Types
|
|
24
|
+
types {Transfer,Approval, Instance, EventState} = uma.clients.erc20
|
|
25
|
+
// Transfer and Approval are event types
|
|
26
|
+
// Instance is the contract instance once connected
|
|
27
|
+
// Event state describes what the state reconstruction based on contract events will look like
|
|
28
|
+
|
|
29
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
import * as Client from "./client";
|
|
4
|
+
|
|
5
|
+
const address = "0xeca82185adCE47f39c684352B0439f030f860318";
|
|
6
|
+
// these require integration testing, skip for ci
|
|
7
|
+
describe("erc20", function () {
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
let events: any;
|
|
10
|
+
let client: Client.Instance;
|
|
11
|
+
test("inits", function () {
|
|
12
|
+
const provider = ethers.providers.getDefaultProvider(process.env.CUSTOM_NODE_URL);
|
|
13
|
+
client = Client.connect(address, provider);
|
|
14
|
+
assert.ok(client);
|
|
15
|
+
});
|
|
16
|
+
test("getEventState between", async function () {
|
|
17
|
+
events = await client.queryFilter({}, 12477952, 12477952 + 1000);
|
|
18
|
+
assert.ok(events.length);
|
|
19
|
+
});
|
|
20
|
+
test("getEventState", async function () {
|
|
21
|
+
const state = await Client.getEventState(events);
|
|
22
|
+
assert.ok(state.balances);
|
|
23
|
+
assert.ok(state.approvalsByOwner);
|
|
24
|
+
assert.ok(state.approvalsBySpender);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ERC20Ethers, ERC20Ethers__factory } from "@uma/contracts-node";
|
|
2
|
+
// eslint-disable-next-line no-restricted-imports
|
|
3
|
+
import { Event } from "ethers";
|
|
4
|
+
import { set } from "lodash";
|
|
5
|
+
import { Balances } from "../../utils";
|
|
6
|
+
import type { Provider, GetEventType } from "../..";
|
|
7
|
+
|
|
8
|
+
export type Instance = ERC20Ethers;
|
|
9
|
+
const Factory = ERC20Ethers__factory;
|
|
10
|
+
|
|
11
|
+
export function connect(address: string, provider: Provider): Instance {
|
|
12
|
+
return Factory.connect(address, provider);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface EventState {
|
|
16
|
+
// any address that created a position, regardless of if they have closed it
|
|
17
|
+
balances?: Balances;
|
|
18
|
+
// approvals are keyed both ways here for ease of lookup by either owner or spender
|
|
19
|
+
approvalsByOwner?: {
|
|
20
|
+
[owner: string]: {
|
|
21
|
+
[spender: string]: {
|
|
22
|
+
amount: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
approvalsBySpender?: {
|
|
27
|
+
[spender: string]: {
|
|
28
|
+
[owner: string]: {
|
|
29
|
+
amount: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type Transfer = GetEventType<Instance, "Transfer">;
|
|
36
|
+
export type Approval = GetEventType<Instance, "Approval">;
|
|
37
|
+
|
|
38
|
+
// takes all events and returns user balances and approvals
|
|
39
|
+
function reduceEvents(state: EventState = {}, event: Event): EventState {
|
|
40
|
+
switch (event.event) {
|
|
41
|
+
case "Transfer": {
|
|
42
|
+
const typedEvent = event as Transfer;
|
|
43
|
+
const { from, to, value } = typedEvent.args;
|
|
44
|
+
const balances = Balances(state.balances || {});
|
|
45
|
+
balances.sub(from, value);
|
|
46
|
+
balances.add(to, value);
|
|
47
|
+
return {
|
|
48
|
+
...state,
|
|
49
|
+
balances: balances.balances,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
case "Approval": {
|
|
53
|
+
const typedEvent = event as Approval;
|
|
54
|
+
const { owner, spender, value } = typedEvent.args;
|
|
55
|
+
set(state, ["approvalsByOwner", owner, spender], value.toString());
|
|
56
|
+
set(state, ["approvalsBySpender", spender, owner], value.toString());
|
|
57
|
+
return {
|
|
58
|
+
...state,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return state;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getEventState(events: Event[], initialState: EventState = {}): EventState {
|
|
66
|
+
return events.reduce(reduceEvents, initialState);
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./client";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as erc20 from "./erc20";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// eslint-disable-next-line no-restricted-imports
|
|
2
|
+
import { Contract, ethers, Event } from "ethers";
|
|
3
|
+
import type { TypedEventFilterEthers as TypedEventFilter, TypedEventEthers as TypedEvent } from "@uma/contracts-node";
|
|
4
|
+
|
|
5
|
+
export * as across from "./across";
|
|
6
|
+
export * as clients from "./clients";
|
|
7
|
+
export * as oracle from "./oracle";
|
|
8
|
+
export * as utils from "./utils";
|
|
9
|
+
|
|
10
|
+
export { type Provider } from "@ethersproject/providers";
|
|
11
|
+
|
|
12
|
+
type Result = ethers.utils.Result;
|
|
13
|
+
|
|
14
|
+
export interface Callable {
|
|
15
|
+
(...args: unknown[]): unknown;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type SerializableEvent = Omit<
|
|
19
|
+
Event,
|
|
20
|
+
"decode" | "removeListener" | "getBlock" | "getTransaction" | "getTransactionReceipt"
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
// this convoluted type is meant to cast events to the types you need based on the contract and event name
|
|
24
|
+
// example: type NewContractRegistered = GetEventType<Registry,"NewContractRegistered">;
|
|
25
|
+
// TODO: the any below is a hacky solution because some typechain types fail to resolve due to
|
|
26
|
+
// incompatible TypedEventFilter and TypedEvent types. This will be fixed by upgrading typechain
|
|
27
|
+
// to a version where Ethers events are exported as first class types.
|
|
28
|
+
export type GetEventType<ContractType extends Contract, EventName extends string> = ReturnType<
|
|
29
|
+
ContractType["filters"][EventName] extends Callable ? ContractType["filters"][EventName] : never
|
|
30
|
+
> extends TypedEventFilter<infer T, infer S>
|
|
31
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
TypedEvent<T & S extends Result ? T & S : any>
|
|
33
|
+
: never;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { BigNumberish } from "../../../utils";
|
|
2
|
+
import sortedLastIndexBy from "lodash/sortedLastIndexBy";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* eventKey. Make a unique and sortable identifier string for an event
|
|
6
|
+
* Used by poolClient.ts lines 166, 246
|
|
7
|
+
*
|
|
8
|
+
* @param {Event} event
|
|
9
|
+
* @returns {string} - the unique id
|
|
10
|
+
*/
|
|
11
|
+
export function eventKey(event: {
|
|
12
|
+
blockNumber: BigNumberish;
|
|
13
|
+
transactionIndex: BigNumberish;
|
|
14
|
+
logIndex: BigNumberish;
|
|
15
|
+
}): string {
|
|
16
|
+
return [
|
|
17
|
+
// we pad these because numbers of varying lengths will not sort correctly, ie "10" will incorrectly sort before "9", but "09" will be correct.
|
|
18
|
+
event.blockNumber.toString().padStart(16, "0"),
|
|
19
|
+
event.transactionIndex.toString().padStart(16, "0"),
|
|
20
|
+
event.logIndex?.toString().padStart(16, "0"),
|
|
21
|
+
// ~ is the last printable ascii char, so it does not interfere with sorting
|
|
22
|
+
].join("~");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* insertOrdered. Inserts items in an array maintaining sorted order, in this case lowest to highest. Does not check duplicates.
|
|
27
|
+
* Mainly used for caching all known events, in order of oldest to newest.
|
|
28
|
+
* Used by poolClient.ts line 181
|
|
29
|
+
*
|
|
30
|
+
* @param {T[]} array
|
|
31
|
+
* @param {T} element
|
|
32
|
+
* @param {Function} orderBy
|
|
33
|
+
*/
|
|
34
|
+
export function insertOrderedAscending<T>(array: T[], element: T, orderBy: (element: T) => string | number): T[] {
|
|
35
|
+
const index = sortedLastIndexBy(array, element, orderBy);
|
|
36
|
+
array.splice(index, 0, element);
|
|
37
|
+
return array;
|
|
38
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as utils from "./utils";
|
|
2
|
+
import assert from "assert";
|
|
3
|
+
test("Balances", function () {
|
|
4
|
+
const balances = utils.Balances();
|
|
5
|
+
assert.ok(balances);
|
|
6
|
+
balances.create("a", "100");
|
|
7
|
+
balances.create("b", "99");
|
|
8
|
+
let result = balances.get("a");
|
|
9
|
+
assert.equal(result, "100");
|
|
10
|
+
result = balances.get("b");
|
|
11
|
+
assert.equal(result, "99");
|
|
12
|
+
|
|
13
|
+
result = balances.sub("a", 1);
|
|
14
|
+
assert.equal(result, "99");
|
|
15
|
+
|
|
16
|
+
result = balances.sub("b", 1);
|
|
17
|
+
assert.equal(result, "98");
|
|
18
|
+
|
|
19
|
+
result = balances.add("b", 2);
|
|
20
|
+
assert.equal(result, "100");
|
|
21
|
+
|
|
22
|
+
const bals = balances.balances;
|
|
23
|
+
assert.equal(Object.keys(bals).length, 2);
|
|
24
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { BigNumber, BigNumberish, delay as sleep } from "../../utils";
|
|
3
|
+
|
|
4
|
+
export { delay as sleep } from "../../utils";
|
|
5
|
+
|
|
6
|
+
// check if a value is not null or undefined, useful for numbers which could be 0.
|
|
7
|
+
// "is" syntax: https://stackoverflow.com/questions/40081332/what-does-the-is-keyword-do-in-typescript
|
|
8
|
+
/* eslint-disable-next-line @typescript-eslint/ban-types */
|
|
9
|
+
export function exists<T>(value: T | null | undefined): value is NonNullable<T> {
|
|
10
|
+
return value !== null && value !== undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// useful for maintaining balances from events
|
|
14
|
+
export type Balances = { [key: string]: string };
|
|
15
|
+
export function Balances(balances: Balances = {}) {
|
|
16
|
+
function create(id: string, amount = "0") {
|
|
17
|
+
assert(!has(id), "balance already exists");
|
|
18
|
+
return set(id, amount);
|
|
19
|
+
}
|
|
20
|
+
function has(id: string) {
|
|
21
|
+
return exists(balances[id]);
|
|
22
|
+
}
|
|
23
|
+
function set(id: string, amount: string) {
|
|
24
|
+
balances[id] = amount;
|
|
25
|
+
return amount;
|
|
26
|
+
}
|
|
27
|
+
function add(id: string, amount: BigNumberish) {
|
|
28
|
+
return set(id, BigNumber.from(amount).add(getOrCreate(id)).toString());
|
|
29
|
+
}
|
|
30
|
+
function sub(id: string, amount: BigNumberish) {
|
|
31
|
+
return set(id, BigNumber.from(getOrCreate(id)).sub(amount).toString());
|
|
32
|
+
}
|
|
33
|
+
function get(id: string) {
|
|
34
|
+
assert(has(id), "balance does not exist");
|
|
35
|
+
return balances[id];
|
|
36
|
+
}
|
|
37
|
+
function getOrCreate(id: string) {
|
|
38
|
+
if (has(id)) return get(id);
|
|
39
|
+
return create(id);
|
|
40
|
+
}
|
|
41
|
+
return { create, add, sub, get, balances, set, has, getOrCreate };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Loop forever but wait until execution is finished before starting next timer. Throw an error to break this
|
|
45
|
+
// or add another utlity function if you need it to end on condition.
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
export async function loop(fn: (...args: any[]) => any, delay: number, ...args: any[]) {
|
|
48
|
+
do {
|
|
49
|
+
await fn(...args);
|
|
50
|
+
await sleep(delay);
|
|
51
|
+
/* eslint-disable-next-line no-constant-condition */
|
|
52
|
+
} while (true);
|
|
53
|
+
}
|