hedge-web3 0.1.38 → 0.1.41

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.
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { PublicKey } from '@solana/web3.js';
2
3
  import Decimal from 'decimal.js';
3
4
  /**
@@ -16,11 +17,16 @@ export declare class VaultAccount {
16
17
  denormalizedDebt: number;
17
18
  /** The ordered number of when this vault was created. */
18
19
  vaultNumber: number;
20
+ /** Debt redistribution snapshop */
19
21
  debtProductSnapshotBytes: Decimal;
22
+ /** Collateral redistribution snapshop' */
20
23
  collateralAccumulatorSnapshotBytes: Decimal;
24
+ /** The vault type eg 'SOL-150' */
21
25
  collateralType: string;
22
26
  /** Current State of the vautl ("Open", "Closed", "Liquidated") */
23
27
  vaultStatus: string;
28
+ /** The public key of the next vault to redeem. */
29
+ nextVaultToRedeem: PublicKey;
24
30
  constructor(vault: any, publicKey: PublicKey);
25
31
  /**
26
32
  * Check if some `PublicKey` is the owner
@@ -53,4 +59,23 @@ export declare class VaultAccount {
53
59
  liquidate(): void;
54
60
  updateDebtAndCollateral(vaultTypeAccuntData: any): void;
55
61
  toString(highlight: PublicKey): string;
62
+ /**
63
+ * Creates a VaultAccount from a slice of data
64
+ *
65
+ * It only contains
66
+ * - Denormalized Debt
67
+ * - Deposit
68
+ * - Vault Number
69
+ *
70
+ * This can be used to do off-chain sort with very little data.
71
+ *
72
+ * @returns a new VaultAccount
73
+ */
74
+ static FromMiniSlice(data: Buffer, pubkey: PublicKey): VaultAccount;
75
+ /**
76
+ * Encode a string for doing byte comparison on solana
77
+ *
78
+ * @returns Buffer of the encoded string
79
+ */
80
+ static getBufferForString(string: string): Buffer;
56
81
  }
@@ -1,4 +1,23 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
2
21
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
23
  };
@@ -7,21 +26,36 @@ exports.VaultAccount = void 0;
7
26
  const web3_js_1 = require("@solana/web3.js");
8
27
  const decimal_js_1 = __importDefault(require("decimal.js"));
9
28
  const HedgeDecimal_1 = require("../HedgeDecimal");
29
+ const borsh = __importStar(require("@project-serum/borsh"));
10
30
  /**
11
31
  * A class that represents an on-chian vault.
12
32
  */
13
33
  class VaultAccount {
14
34
  constructor(vault, publicKey) {
35
+ var _a, _b, _c;
36
+ /** Debt redistribution snapshop */
37
+ this.debtProductSnapshotBytes = new decimal_js_1.default(0);
38
+ /** Collateral redistribution snapshop' */
39
+ this.collateralAccumulatorSnapshotBytes = new decimal_js_1.default(0);
40
+ /** Current State of the vautl ("Open", "Closed", "Liquidated") */
41
+ this.vaultStatus = '';
15
42
  this.publicKey = publicKey;
16
43
  this.vaultOwner = vault.vaultOwner;
17
- this.vaultNumber = vault.vaultNumber.toNumber();
44
+ this.vaultNumber = (_a = vault.vaultNumber) === null || _a === void 0 ? void 0 : _a.toNumber();
18
45
  this.pdaSalt = vault.pdaSalt;
19
- this.deposited = vault.deposited.toNumber();
20
- this.denormalizedDebt = vault.denormalizedDebt.toNumber();
21
- this.debtProductSnapshotBytes = (0, HedgeDecimal_1.DecimalFromU128)(vault.debtProductSnapshotBytes.toString());
22
- this.collateralAccumulatorSnapshotBytes = (0, HedgeDecimal_1.DecimalFromU128)(vault.collateralAccumulatorSnapshotBytes.toString());
46
+ this.deposited = (_b = vault.deposited) === null || _b === void 0 ? void 0 : _b.toNumber();
47
+ this.denormalizedDebt = (_c = vault.denormalizedDebt) === null || _c === void 0 ? void 0 : _c.toNumber();
48
+ if (vault.debtProductSnapshotBytes) {
49
+ this.debtProductSnapshotBytes = (0, HedgeDecimal_1.DecimalFromU128)(vault.debtProductSnapshotBytes.toString());
50
+ }
51
+ if (vault.collateralAccumulatorSnapshotBytes) {
52
+ this.collateralAccumulatorSnapshotBytes = (0, HedgeDecimal_1.DecimalFromU128)(vault.collateralAccumulatorSnapshotBytes.toString());
53
+ }
23
54
  this.collateralType = vault.collateralType;
24
- this.vaultStatus = Object.keys(vault.vaultStatus)[0];
55
+ this.nextVaultToRedeem = vault.nextVaultToRedeem;
56
+ if (vault.vaultStatus) {
57
+ this.vaultStatus = Object.keys(vault.vaultStatus)[0];
58
+ }
25
59
  }
26
60
  /**
27
61
  * Check if some `PublicKey` is the owner
@@ -60,7 +94,8 @@ class VaultAccount {
60
94
  }
61
95
  addDebt(newNormalizedDebt, vaultTypeCompoundedInterest) {
62
96
  const denormalizedNewDebt = newNormalizedDebt.div(vaultTypeCompoundedInterest);
63
- this.denormalizedDebt += denormalizedNewDebt.toNumber();
97
+ this.denormalizedDebt = denormalizedNewDebt.add(new decimal_js_1.default(this.denormalizedDebt)).floor().toNumber();
98
+ // this.denormalizedDebt = parseFloat(this.denormalizedDebt.toFixed(0))
64
99
  }
65
100
  addDeposit(depositAmount) {
66
101
  this.deposited += depositAmount;
@@ -81,7 +116,6 @@ class VaultAccount {
81
116
  this.denormalizedDebt = debtProductCurrent
82
117
  .div(this.debtProductSnapshotBytes)
83
118
  .mul(new decimal_js_1.default(this.denormalizedDebt))
84
- // .add(new Decimal(vaultTypeAccuntData.debtRedistributionError))
85
119
  .toNumber();
86
120
  const extraCollateralDeposited = this.denormalizedDebt * collateralAccumulatorCurrent.sub(this.collateralAccumulatorSnapshotBytes).toNumber();
87
121
  this.deposited += extraCollateralDeposited;
@@ -91,15 +125,50 @@ class VaultAccount {
91
125
  toString(highlight) {
92
126
  let arrow = '';
93
127
  if (this.publicKey.toString() === highlight.toString()) {
94
- arrow = ' <----';
128
+ arrow = ' <----!!';
95
129
  }
96
130
  let collateralRatio = 'Infinite';
97
131
  if (this.denormalizedDebt > 0) {
98
- collateralRatio = (this.deposited / this.denormalizedDebt).toFixed(8);
132
+ collateralRatio = new decimal_js_1.default(this.deposited).div(new decimal_js_1.default(this.denormalizedDebt)).toString();
99
133
  }
100
- return `Vault(${this.vaultNumber}): ${this.publicKey
101
- .toString()
102
- .substring(0, 6)}. Debt: ${this.inUsd()} Collat: ${collateralRatio} ${arrow}`;
134
+ let nextVault = 'None';
135
+ if (this.nextVaultToRedeem) {
136
+ nextVault = this.nextVaultToRedeem.toString().substring(0, 6);
137
+ }
138
+ return `Vault(${this.vaultNumber}): ${this.publicKey.toString().substring(0, 6)}. Debt: ${this.denormalizedDebt} Collat: ${this.deposited} Ratio: ${collateralRatio} NextVault: ${nextVault} ${arrow} `;
139
+ }
140
+ /**
141
+ * Creates a VaultAccount from a slice of data
142
+ *
143
+ * It only contains
144
+ * - Denormalized Debt
145
+ * - Deposit
146
+ * - Vault Number
147
+ *
148
+ * This can be used to do off-chain sort with very little data.
149
+ *
150
+ * @returns a new VaultAccount
151
+ */
152
+ static FromMiniSlice(data, pubkey) {
153
+ const props = [borsh.u64('vaultNumber'), borsh.u64('deposited'), borsh.u64('denormalizedDebt')];
154
+ const miniVaultLayout = borsh.struct(props, 'minVaultLayout');
155
+ const decodedData = miniVaultLayout.decode(data);
156
+ return new VaultAccount(decodedData, pubkey);
157
+ }
158
+ /**
159
+ * Encode a string for doing byte comparison on solana
160
+ *
161
+ * @returns Buffer of the encoded string
162
+ */
163
+ static getBufferForString(string) {
164
+ const stringLayout = borsh.struct([borsh.str('name')], 'justStringLayout');
165
+ let buffer = Buffer.alloc(50);
166
+ stringLayout.encode({ name: string }, buffer);
167
+ // Trim empty space at end of buffer
168
+ while (buffer.at(buffer.length - 1) == 0) {
169
+ buffer = buffer.slice(0, buffer.length - 1);
170
+ }
171
+ return buffer;
103
172
  }
104
173
  }
105
174
  exports.VaultAccount = VaultAccount;
@@ -14,11 +14,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.getLinkedListAccounts = void 0;
16
16
  const underscore_1 = __importDefault(require("underscore"));
17
+ const Constants_1 = require("../Constants");
17
18
  const HedgeDecimal_1 = require("../HedgeDecimal");
18
19
  const VaultAccount_1 = require("../state/VaultAccount");
19
20
  const decimal_js_1 = __importDefault(require("decimal.js"));
21
+ const bs58_1 = __importDefault(require("bs58"));
20
22
  function getLinkedListAccounts(program, provider, vaultTypeAccountPublicKey, vaultPublicKey, depositAmount, loanAmount, redeem, liquidate, cachedVaults) {
21
23
  return __awaiter(this, void 0, void 0, function* () {
24
+ console.log('Getting getLinkedListAccounts');
22
25
  const vaultTypeAccount = yield program.account.vaultType.fetch(vaultTypeAccountPublicKey);
23
26
  // Default for null is the vault itself, so set them all to this vault
24
27
  let oldSmallerPublicKey = vaultPublicKey;
@@ -32,29 +35,33 @@ function getLinkedListAccounts(program, provider, vaultTypeAccountPublicKey, vau
32
35
  vaults = cachedVaults;
33
36
  }
34
37
  else {
35
- let allVaults = cachedVaults
36
- ? cachedVaults
37
- : (yield program.account.vault
38
- .all([
39
- // {
40
- // memcmp: { bytes: bs58.encode(inputCollateralType), offset: 8 + 32 + 8 },
41
- // },
42
- ])
43
- .catch((error) => {
44
- console.log('error', error);
45
- })) || [];
46
- // Load them into our account objects
47
- vaults = allVaults.map((vault) => {
48
- return new VaultAccount_1.VaultAccount(vault.account, vault.publicKey);
49
- });
38
+ // let allVaults = cachedVaults
39
+ // ? cachedVaults
40
+ // : (await program.account.vault
41
+ // .all([
42
+ // {
43
+ // memcmp: { bytes: vaultTypeAccount.collateralType, offset: 8 + 32 + 8 },
44
+ // },
45
+ // ])
46
+ // .catch((error) => {
47
+ // console.log('error', error)
48
+ // })) || []
49
+ // // Load them into our account objects
50
+ // vaults = allVaults.map((vault) => {
51
+ // return new VaultAccount(vault.account, vault.publicKey)
52
+ // })
53
+ vaults = yield getMiniVaults(program, vaultTypeAccount.collateralType);
50
54
  }
51
- // Filter out the account that are not the same vault type
52
- vaults = underscore_1.default.filter(vaults, (vault) => {
53
- return vault.collateralType === vaultTypeAccount.collateralType;
54
- });
55
+ console.log('Vault count found:', vaults.length);
56
+ console.log('First Vault', vaults[0]);
55
57
  // Filter out the accounts that are not open
58
+ // TODO filter on vault status. Or we enable people to "close out" empty vaults
59
+ // vaults = _.filter(vaults, (vault) => {
60
+ // return vault.vaultStatus === 'open'
61
+ // })
62
+ // Remove any vaults with no debt or collateral
56
63
  vaults = underscore_1.default.filter(vaults, (vault) => {
57
- return vault.vaultStatus === 'open';
64
+ return vault.denormalizedDebt > 0 && vault.deposited > 0;
58
65
  });
59
66
  // Sort them
60
67
  vaults.sort(sortVaults);
@@ -70,15 +77,32 @@ function getLinkedListAccounts(program, provider, vaultTypeAccountPublicKey, vau
70
77
  oldSmallerPublicKey = vaults[indexBefore - 1].publicKey;
71
78
  }
72
79
  // Pretty print all the vaults before the operation
73
- console.log('Sorted open vaults before');
74
- vaults.forEach((vault) => {
75
- console.log(vault.toString(vaultPublicKey));
76
- });
80
+ // console.log('Sorted open vaults BEFORE at index:', indexBefore)
81
+ // let correctOrderBefore = true
82
+ // for (let i = 0; i < vaults.length; i++) {
83
+ // if (i < vaults.length - 1) {
84
+ // // Verify i points to i+1
85
+ // if (vaults[i].nextVaultToRedeem.toString() !== vaults[i + 1].publicKey.toString()) {
86
+ // correctOrderBefore = false
87
+ // console.log('A vault was found OUT OF ORDER!!', vaults[i])
88
+ // }
89
+ // }
90
+ // }
91
+ // if (correctOrderBefore) {
92
+ // console.log(`Verfied the on-chain order of vault type:`, vaultTypeAccount.collateralType)
93
+ // } else {
94
+ // throw new Error('On-Chian vaults not in order!')
95
+ // }
77
96
  // If it wasn't in the list, add it now
78
97
  if (indexBefore < 0) {
98
+ // console.log('Was not in the list before. Adding now.')
79
99
  vaults.push(thisVault);
80
100
  indexBefore = vaults.length - 1;
81
101
  }
102
+ else {
103
+ // Copy in the vault so we have the full data (needed to run updateDebtAndCollateral)
104
+ vaults[indexBefore] = thisVault;
105
+ }
82
106
  // Now that we know it's def in the list, iterate the list and update
83
107
  // this vault with the opeation we're going to apply
84
108
  const newNormalizedDebt = new decimal_js_1.default(loanAmount);
@@ -98,10 +122,10 @@ function getLinkedListAccounts(program, provider, vaultTypeAccountPublicKey, vau
98
122
  // Sort it again since we've changed one vault
99
123
  vaults = vaults.sort(sortVaults);
100
124
  // Pretty print the list again
101
- console.log('Sorted open vaults with new debt added');
102
- vaults.forEach((vault) => {
103
- console.log(vault.toString(vaultPublicKey));
104
- });
125
+ // console.log('Sorted open vaults with new debt added at index: ', indexAfter)
126
+ // console.log(vaults.map((vault) => {
127
+ // return vault.toString(vaultPublicKey)
128
+ // }))
105
129
  // Search for the vaults new position
106
130
  let indexAfter = -1;
107
131
  vaults.forEach((vault, index) => {
@@ -109,6 +133,13 @@ function getLinkedListAccounts(program, provider, vaultTypeAccountPublicKey, vau
109
133
  indexAfter = index;
110
134
  }
111
135
  });
136
+ // New list with vault
137
+ // console.log('New list with vault now at index:', indexAfter)
138
+ // console.log(
139
+ // vaults.map((vault) => {
140
+ // return vault.toString(vaultPublicKey)
141
+ // })
142
+ // )
112
143
  // Print where it moved from / to
113
144
  console.log('Index Before', indexBefore);
114
145
  console.log('Index After', indexAfter);
@@ -120,9 +151,9 @@ function getLinkedListAccounts(program, provider, vaultTypeAccountPublicKey, vau
120
151
  newLargerPublicKey = vaults[indexAfter + 1].publicKey;
121
152
  }
122
153
  // Print out the new left/right
123
- console.log('oldSmallerPublicKey', oldSmallerPublicKey.toString());
124
- console.log('newSmallerPublicKey', newSmallerPublicKey.toString());
125
- console.log('newLargerPublicKey', newLargerPublicKey.toString());
154
+ // console.log('oldSmallerPublicKey', oldSmallerPublicKey.toString())
155
+ // console.log('newSmallerPublicKey', newSmallerPublicKey.toString())
156
+ // console.log('newLargerPublicKey', newLargerPublicKey.toString())
126
157
  return [oldSmallerPublicKey, newSmallerPublicKey, newLargerPublicKey, vaults];
127
158
  });
128
159
  }
@@ -137,3 +168,33 @@ function sortVaults(a, b) {
137
168
  }
138
169
  return aRatio - bRatio;
139
170
  }
171
+ function getMiniVaults(program, collateralType) {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ const filters = [
174
+ // Filter for Vault Accounts
175
+ {
176
+ // @ts-ignore
177
+ memcmp: program.account.vault.coder.accounts.memcmp(program.account.vault._idlAccount.name),
178
+ },
179
+ // Filter for Vaults with this collateral type
180
+ {
181
+ memcmp: {
182
+ bytes: bs58_1.default.encode(VaultAccount_1.VaultAccount.getBufferForString(collateralType)),
183
+ offset: 8 + 32 + 24,
184
+ },
185
+ },
186
+ ];
187
+ const allAccounts = yield program.provider.connection.getProgramAccounts(Constants_1.HEDGE_PROGRAM_PUBLICKEY, {
188
+ filters: filters,
189
+ // Slice the data only to grab the 3 u64's of size 8 bytes each
190
+ dataSlice: {
191
+ // See programs/hedge-vault/src/account_data/vault.rs for struct layout
192
+ offset: 8 + 32,
193
+ length: 24,
194
+ },
195
+ });
196
+ return allAccounts.map(vaultData => {
197
+ return VaultAccount_1.VaultAccount.FromMiniSlice(vaultData.account.data, vaultData.pubkey);
198
+ });
199
+ });
200
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedge-web3",
3
- "version": "0.1.38",
3
+ "version": "0.1.41",
4
4
  "description": "Hedge Javascript Web3 API",
5
5
  "main": "lib/index.js",
6
6
  "types": "declarations/index.d.ts",
@@ -24,6 +24,7 @@
24
24
  "@types/underscore": "^1.11.4",
25
25
  "@types/uuid": "^8.3.4",
26
26
  "bn.js": "^5.2.0",
27
+ "bs58": "^5.0.0",
27
28
  "decimal.js": "^10.3.1",
28
29
  "rollup": "^2.62.0",
29
30
  "ts-standard": "^11.0.0",
@@ -2,6 +2,8 @@ import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'
2
2
  import Decimal from 'decimal.js'
3
3
  import { DecimalFromU128 } from '../HedgeDecimal'
4
4
 
5
+ import * as borsh from '@project-serum/borsh'
6
+
5
7
  /**
6
8
  * A class that represents an on-chian vault.
7
9
  */
@@ -24,27 +26,40 @@ export class VaultAccount {
24
26
  /** The ordered number of when this vault was created. */
25
27
  vaultNumber: number
26
28
 
27
- debtProductSnapshotBytes: Decimal
29
+ /** Debt redistribution snapshop */
30
+ debtProductSnapshotBytes = new Decimal(0)
28
31
 
29
- collateralAccumulatorSnapshotBytes: Decimal
32
+ /** Collateral redistribution snapshop' */
33
+ collateralAccumulatorSnapshotBytes = new Decimal(0)
30
34
 
35
+ /** The vault type eg 'SOL-150' */
31
36
  collateralType: string
32
37
 
33
38
  /** Current State of the vautl ("Open", "Closed", "Liquidated") */
34
- vaultStatus: string
39
+ vaultStatus = ''
40
+
41
+ /** The public key of the next vault to redeem. */
42
+ nextVaultToRedeem: PublicKey
35
43
 
36
44
  constructor(vault: any, publicKey: PublicKey) {
37
45
  this.publicKey = publicKey
38
46
  this.vaultOwner = vault.vaultOwner
39
- this.vaultNumber = vault.vaultNumber.toNumber()
47
+ this.vaultNumber = vault.vaultNumber?.toNumber()
40
48
  this.pdaSalt = vault.pdaSalt
41
- this.deposited = vault.deposited.toNumber()
42
- this.denormalizedDebt = vault.denormalizedDebt.toNumber()
43
- this.debtProductSnapshotBytes = DecimalFromU128(vault.debtProductSnapshotBytes.toString())
44
- this.collateralAccumulatorSnapshotBytes = DecimalFromU128(vault.collateralAccumulatorSnapshotBytes.toString())
49
+ this.deposited = vault.deposited?.toNumber()
50
+ this.denormalizedDebt = vault.denormalizedDebt?.toNumber()
51
+ if (vault.debtProductSnapshotBytes) {
52
+ this.debtProductSnapshotBytes = DecimalFromU128(vault.debtProductSnapshotBytes.toString())
53
+ }
54
+ if (vault.collateralAccumulatorSnapshotBytes) {
55
+ this.collateralAccumulatorSnapshotBytes = DecimalFromU128(vault.collateralAccumulatorSnapshotBytes.toString())
56
+ }
45
57
  this.collateralType = vault.collateralType
58
+ this.nextVaultToRedeem = vault.nextVaultToRedeem
46
59
 
47
- this.vaultStatus = Object.keys(vault.vaultStatus)[0]
60
+ if (vault.vaultStatus) {
61
+ this.vaultStatus = Object.keys(vault.vaultStatus)[0]
62
+ }
48
63
  }
49
64
 
50
65
  /**
@@ -88,7 +103,8 @@ export class VaultAccount {
88
103
 
89
104
  public addDebt(newNormalizedDebt: Decimal, vaultTypeCompoundedInterest: Decimal) {
90
105
  const denormalizedNewDebt = newNormalizedDebt.div(vaultTypeCompoundedInterest)
91
- this.denormalizedDebt += denormalizedNewDebt.toNumber()
106
+ this.denormalizedDebt = denormalizedNewDebt.add(new Decimal(this.denormalizedDebt)).floor().toNumber()
107
+ // this.denormalizedDebt = parseFloat(this.denormalizedDebt.toFixed(0))
92
108
  }
93
109
  public addDeposit(depositAmount: number) {
94
110
  this.deposited += depositAmount
@@ -113,7 +129,6 @@ export class VaultAccount {
113
129
  this.denormalizedDebt = debtProductCurrent
114
130
  .div(this.debtProductSnapshotBytes)
115
131
  .mul(new Decimal(this.denormalizedDebt))
116
- // .add(new Decimal(vaultTypeAccuntData.debtRedistributionError))
117
132
  .toNumber()
118
133
 
119
134
  const extraCollateralDeposited =
@@ -127,14 +142,57 @@ export class VaultAccount {
127
142
  public toString(highlight: PublicKey): string {
128
143
  let arrow = ''
129
144
  if (this.publicKey.toString() === highlight.toString()) {
130
- arrow = ' <----'
145
+ arrow = ' <----!!'
131
146
  }
132
147
  let collateralRatio = 'Infinite'
133
148
  if (this.denormalizedDebt > 0) {
134
- collateralRatio = (this.deposited / this.denormalizedDebt).toFixed(8)
149
+ collateralRatio = new Decimal(this.deposited).div(new Decimal(this.denormalizedDebt)).toString()
135
150
  }
136
- return `Vault(${this.vaultNumber}): ${this.publicKey
137
- .toString()
138
- .substring(0, 6)}. Debt: ${this.inUsd()} Collat: ${collateralRatio} ${arrow}`
151
+
152
+ let nextVault = 'None'
153
+ if (this.nextVaultToRedeem) {
154
+ nextVault = this.nextVaultToRedeem.toString().substring(0, 6)
155
+ }
156
+
157
+ return `Vault(${this.vaultNumber}): ${this.publicKey.toString().substring(0, 6)}. Debt: ${
158
+ this.denormalizedDebt
159
+ } Collat: ${this.deposited} Ratio: ${collateralRatio} NextVault: ${nextVault} ${arrow} `
160
+ }
161
+ /**
162
+ * Creates a VaultAccount from a slice of data
163
+ *
164
+ * It only contains
165
+ * - Denormalized Debt
166
+ * - Deposit
167
+ * - Vault Number
168
+ *
169
+ * This can be used to do off-chain sort with very little data.
170
+ *
171
+ * @returns a new VaultAccount
172
+ */
173
+ static FromMiniSlice(data: Buffer, pubkey: PublicKey) {
174
+ const props = [borsh.u64('vaultNumber'), borsh.u64('deposited'), borsh.u64('denormalizedDebt')]
175
+ const miniVaultLayout = borsh.struct(props, 'minVaultLayout')
176
+ const decodedData: any = miniVaultLayout.decode(data)
177
+
178
+ return new VaultAccount(decodedData, pubkey)
179
+ }
180
+
181
+ /**
182
+ * Encode a string for doing byte comparison on solana
183
+ *
184
+ * @returns Buffer of the encoded string
185
+ */
186
+ static getBufferForString(string: string) {
187
+ const stringLayout = borsh.struct([borsh.str('name')], 'justStringLayout')
188
+ let buffer = Buffer.alloc(50)
189
+
190
+ stringLayout.encode({ name: string }, buffer)
191
+
192
+ // Trim empty space at end of buffer
193
+ while (buffer.at(buffer.length - 1) == 0) {
194
+ buffer = buffer.slice(0, buffer.length - 1)
195
+ }
196
+ return buffer
139
197
  }
140
198
  }
@@ -1,12 +1,16 @@
1
1
  import { Program, Provider } from '@project-serum/anchor'
2
2
  import { PublicKey, Signer } from '@solana/web3.js'
3
3
  import _ from 'underscore'
4
- import { getVaultSystemStatePublicKey, getVaultTypeOracleAccountPublicKey } from '../Constants'
4
+ import { getVaultSystemStatePublicKey, getVaultTypeOracleAccountPublicKey, HEDGE_PROGRAM_PUBLICKEY } from '../Constants'
5
5
 
6
6
  import { DecimalFromU128 } from '../HedgeDecimal'
7
7
  import { VaultAccount } from '../state/VaultAccount'
8
8
  import Decimal from 'decimal.js'
9
9
  import { Vault } from 'idl/vault'
10
+
11
+ import * as borsh from '@project-serum/borsh'
12
+ import base58 from 'bs58'
13
+
10
14
  export async function getLinkedListAccounts(
11
15
  program: Program<Vault>,
12
16
  provider: Provider,
@@ -18,6 +22,7 @@ export async function getLinkedListAccounts(
18
22
  liquidate: boolean,
19
23
  cachedVaults?: VaultAccount[]
20
24
  ): Promise<[PublicKey, PublicKey, PublicKey, VaultAccount[]]> {
25
+ console.log('Getting getLinkedListAccounts')
21
26
  const vaultTypeAccount = await program.account.vaultType.fetch(vaultTypeAccountPublicKey)
22
27
 
23
28
  // Default for null is the vault itself, so set them all to this vault
@@ -33,32 +38,37 @@ export async function getLinkedListAccounts(
33
38
  if (cachedVaults) {
34
39
  vaults = cachedVaults
35
40
  } else {
36
- let allVaults = cachedVaults
37
- ? cachedVaults
38
- : (await program.account.vault
39
- .all([
40
- // {
41
- // memcmp: { bytes: bs58.encode(inputCollateralType), offset: 8 + 32 + 8 },
42
- // },
43
- ])
44
- .catch((error) => {
45
- console.log('error', error)
46
- })) || []
47
-
48
- // Load them into our account objects
49
- vaults = allVaults.map((vault) => {
50
- return new VaultAccount(vault.account, vault.publicKey)
51
- })
41
+ // let allVaults = cachedVaults
42
+ // ? cachedVaults
43
+ // : (await program.account.vault
44
+ // .all([
45
+ // {
46
+ // memcmp: { bytes: vaultTypeAccount.collateralType, offset: 8 + 32 + 8 },
47
+ // },
48
+ // ])
49
+ // .catch((error) => {
50
+ // console.log('error', error)
51
+ // })) || []
52
+
53
+ // // Load them into our account objects
54
+ // vaults = allVaults.map((vault) => {
55
+ // return new VaultAccount(vault.account, vault.publicKey)
56
+ // })
57
+ vaults = await getMiniVaults(program, vaultTypeAccount.collateralType)
52
58
  }
53
59
 
54
- // Filter out the account that are not the same vault type
55
- vaults = _.filter(vaults, (vault) => {
56
- return vault.collateralType === vaultTypeAccount.collateralType
57
- })
60
+ console.log('Vault count found:', vaults.length)
61
+ console.log('First Vault', vaults[0])
58
62
 
59
63
  // Filter out the accounts that are not open
64
+ // TODO filter on vault status. Or we enable people to "close out" empty vaults
65
+ // vaults = _.filter(vaults, (vault) => {
66
+ // return vault.vaultStatus === 'open'
67
+ // })
68
+
69
+ // Remove any vaults with no debt or collateral
60
70
  vaults = _.filter(vaults, (vault) => {
61
- return vault.vaultStatus === 'open'
71
+ return vault.denormalizedDebt > 0 && vault.deposited > 0
62
72
  })
63
73
 
64
74
  // Sort them
@@ -78,15 +88,31 @@ export async function getLinkedListAccounts(
78
88
  }
79
89
 
80
90
  // Pretty print all the vaults before the operation
81
- console.log('Sorted open vaults before')
82
- vaults.forEach((vault) => {
83
- console.log(vault.toString(vaultPublicKey))
84
- })
91
+ // console.log('Sorted open vaults BEFORE at index:', indexBefore)
92
+ // let correctOrderBefore = true
93
+ // for (let i = 0; i < vaults.length; i++) {
94
+ // if (i < vaults.length - 1) {
95
+ // // Verify i points to i+1
96
+ // if (vaults[i].nextVaultToRedeem.toString() !== vaults[i + 1].publicKey.toString()) {
97
+ // correctOrderBefore = false
98
+ // console.log('A vault was found OUT OF ORDER!!', vaults[i])
99
+ // }
100
+ // }
101
+ // }
102
+ // if (correctOrderBefore) {
103
+ // console.log(`Verfied the on-chain order of vault type:`, vaultTypeAccount.collateralType)
104
+ // } else {
105
+ // throw new Error('On-Chian vaults not in order!')
106
+ // }
85
107
 
86
108
  // If it wasn't in the list, add it now
87
109
  if (indexBefore < 0) {
110
+ // console.log('Was not in the list before. Adding now.')
88
111
  vaults.push(thisVault)
89
112
  indexBefore = vaults.length - 1
113
+ } else {
114
+ // Copy in the vault so we have the full data (needed to run updateDebtAndCollateral)
115
+ vaults[indexBefore] = thisVault
90
116
  }
91
117
 
92
118
  // Now that we know it's def in the list, iterate the list and update
@@ -112,10 +138,10 @@ export async function getLinkedListAccounts(
112
138
  vaults = vaults.sort(sortVaults)
113
139
 
114
140
  // Pretty print the list again
115
- console.log('Sorted open vaults with new debt added')
116
- vaults.forEach((vault) => {
117
- console.log(vault.toString(vaultPublicKey))
118
- })
141
+ // console.log('Sorted open vaults with new debt added at index: ', indexAfter)
142
+ // console.log(vaults.map((vault) => {
143
+ // return vault.toString(vaultPublicKey)
144
+ // }))
119
145
 
120
146
  // Search for the vaults new position
121
147
  let indexAfter = -1
@@ -125,6 +151,14 @@ export async function getLinkedListAccounts(
125
151
  }
126
152
  })
127
153
 
154
+ // New list with vault
155
+ // console.log('New list with vault now at index:', indexAfter)
156
+ // console.log(
157
+ // vaults.map((vault) => {
158
+ // return vault.toString(vaultPublicKey)
159
+ // })
160
+ // )
161
+
128
162
  // Print where it moved from / to
129
163
  console.log('Index Before', indexBefore)
130
164
  console.log('Index After', indexAfter)
@@ -138,9 +172,9 @@ export async function getLinkedListAccounts(
138
172
  }
139
173
 
140
174
  // Print out the new left/right
141
- console.log('oldSmallerPublicKey', oldSmallerPublicKey.toString())
142
- console.log('newSmallerPublicKey', newSmallerPublicKey.toString())
143
- console.log('newLargerPublicKey', newLargerPublicKey.toString())
175
+ // console.log('oldSmallerPublicKey', oldSmallerPublicKey.toString())
176
+ // console.log('newSmallerPublicKey', newSmallerPublicKey.toString())
177
+ // console.log('newLargerPublicKey', newLargerPublicKey.toString())
144
178
 
145
179
  return [oldSmallerPublicKey, newSmallerPublicKey, newLargerPublicKey, vaults]
146
180
  }
@@ -155,3 +189,33 @@ function sortVaults(a: VaultAccount, b: VaultAccount) {
155
189
  }
156
190
  return aRatio - bRatio
157
191
  }
192
+
193
+ async function getMiniVaults(program: Program<Vault>, collateralType: string) {
194
+ const filters = [
195
+ // Filter for Vault Accounts
196
+ {
197
+ // @ts-ignore
198
+ memcmp: program.account.vault.coder.accounts.memcmp(program.account.vault._idlAccount.name),
199
+ },
200
+ // Filter for Vaults with this collateral type
201
+ {
202
+ memcmp: {
203
+ bytes: base58.encode(VaultAccount.getBufferForString(collateralType)),
204
+ offset: 8 + 32 + 24,
205
+ },
206
+ },
207
+ ]
208
+ const allAccounts = await program.provider.connection.getProgramAccounts(HEDGE_PROGRAM_PUBLICKEY, {
209
+ filters: filters,
210
+ // Slice the data only to grab the 3 u64's of size 8 bytes each
211
+ dataSlice: {
212
+ // See programs/hedge-vault/src/account_data/vault.rs for struct layout
213
+ offset: 8 + 32,
214
+ length: 24,
215
+ },
216
+ })
217
+
218
+ return allAccounts.map(vaultData=>{
219
+ return VaultAccount.FromMiniSlice(vaultData.account.data, vaultData.pubkey)
220
+ })
221
+ }