@zga/client 0.0.0-stage → 0.1.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.
Files changed (70) hide show
  1. package/dist/addresses.d.ts +29 -0
  2. package/dist/addresses.js +43 -0
  3. package/dist/billing.d.ts +41 -0
  4. package/dist/billing.js +141 -0
  5. package/dist/bin/crank.d.ts +2 -0
  6. package/dist/bin/crank.js +34 -0
  7. package/dist/bin/relayer.d.ts +2 -0
  8. package/dist/bin/relayer.js +37 -0
  9. package/dist/chain.d.ts +14 -0
  10. package/dist/chain.js +48 -0
  11. package/dist/config.d.ts +11 -0
  12. package/dist/config.js +61 -0
  13. package/dist/crank/gate.d.ts +2 -0
  14. package/dist/crank/gate.js +26 -0
  15. package/dist/crank/lending.d.ts +16 -0
  16. package/dist/crank/lending.js +144 -0
  17. package/dist/crank/prices.d.ts +11 -0
  18. package/dist/crank/prices.js +177 -0
  19. package/dist/crank/runner.d.ts +15 -0
  20. package/dist/crank/runner.js +30 -0
  21. package/dist/crank/shieldFees.d.ts +15 -0
  22. package/dist/crank/shieldFees.js +161 -0
  23. package/dist/epoch-root.d.ts +23 -0
  24. package/dist/epoch-root.js +98 -0
  25. package/dist/explorer.d.ts +41 -0
  26. package/dist/explorer.js +222 -0
  27. package/dist/gossip.d.ts +4 -0
  28. package/dist/gossip.js +77 -0
  29. package/dist/idl.d.ts +2 -0
  30. package/dist/idl.js +52 -0
  31. package/dist/index.d.ts +31 -0
  32. package/dist/index.js +47 -0
  33. package/dist/lending.d.ts +82 -0
  34. package/dist/lending.js +164 -0
  35. package/dist/log.d.ts +27 -0
  36. package/dist/log.js +173 -0
  37. package/dist/lookupTable.d.ts +2 -0
  38. package/dist/lookupTable.js +47 -0
  39. package/dist/observer.d.ts +59 -0
  40. package/dist/observer.js +255 -0
  41. package/dist/receipt.d.ts +30 -0
  42. package/dist/receipt.js +27 -0
  43. package/dist/relayer/chain.d.ts +35 -0
  44. package/dist/relayer/chain.js +171 -0
  45. package/dist/relayer/errors.d.ts +8 -0
  46. package/dist/relayer/errors.js +29 -0
  47. package/dist/relayer/limits.d.ts +15 -0
  48. package/dist/relayer/limits.js +47 -0
  49. package/dist/relayer/relayer.d.ts +25 -0
  50. package/dist/relayer/relayer.js +54 -0
  51. package/dist/relayer/server.d.ts +8 -0
  52. package/dist/relayer/server.js +101 -0
  53. package/dist/relayer/settings.d.ts +23 -0
  54. package/dist/relayer/settings.js +69 -0
  55. package/dist/replay.d.ts +13 -0
  56. package/dist/replay.js +118 -0
  57. package/dist/session.d.ts +29 -0
  58. package/dist/session.js +31 -0
  59. package/dist/settlement.d.ts +17 -0
  60. package/dist/settlement.js +96 -0
  61. package/dist/sha256.d.ts +3 -0
  62. package/dist/sha256.js +83 -0
  63. package/dist/shieldLimits.d.ts +15 -0
  64. package/dist/shieldLimits.js +24 -0
  65. package/dist/signer.d.ts +5 -0
  66. package/dist/signer.js +43 -0
  67. package/dist/token.d.ts +6 -0
  68. package/dist/token.js +33 -0
  69. package/package.json +25 -4
  70. package/README.md +0 -3
@@ -0,0 +1,29 @@
1
+ import { Connection, PublicKey } from '@solana/web3.js';
2
+ export declare class Addresses {
3
+ readonly programId: PublicKey;
4
+ constructor(programId: PublicKey);
5
+ private derive;
6
+ get config(): PublicKey;
7
+ get zgapMint(): PublicKey;
8
+ get zgapAuthority(): PublicKey;
9
+ get zgapReserve(): PublicKey;
10
+ get zgapCollateralVault(): PublicKey;
11
+ get zgapSolVault(): PublicKey;
12
+ get zgapReserveSol(): PublicKey;
13
+ get lpLock(): PublicKey;
14
+ get yieldVault(): PublicKey;
15
+ get rewardDistributor(): PublicKey;
16
+ priceCache(assetId: number): PublicKey;
17
+ session(sessionId: Uint8Array): PublicKey;
18
+ vault(session: PublicKey): PublicKey;
19
+ prizeVault(session: PublicKey): PublicKey;
20
+ guild(guildId: Uint8Array): PublicKey;
21
+ rateLimit(guild: PublicKey): PublicKey;
22
+ gameManifest(gameId: Uint8Array): PublicKey;
23
+ gameCreator(creator: PublicKey, gameId: Uint8Array): PublicKey;
24
+ royaltyVault(creator: PublicKey, gameId: Uint8Array): PublicKey;
25
+ attestationCert(enclave: PublicKey): PublicKey;
26
+ player(session: PublicKey, user: PublicKey): PublicKey;
27
+ agentBond(user: PublicKey): PublicKey;
28
+ }
29
+ export declare function zgaMintOf(connection: Connection, addresses: Addresses): Promise<PublicKey>;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Addresses = void 0;
4
+ exports.zgaMintOf = zgaMintOf;
5
+ const web3_js_1 = require("@solana/web3.js");
6
+ const enc = (s) => new TextEncoder().encode(s);
7
+ class Addresses {
8
+ constructor(programId) {
9
+ this.programId = programId;
10
+ }
11
+ derive(...seeds) {
12
+ return web3_js_1.PublicKey.findProgramAddressSync(seeds, this.programId)[0];
13
+ }
14
+ get config() { return this.derive(enc('config')); }
15
+ get zgapMint() { return this.derive(enc('zgap_mint')); }
16
+ get zgapAuthority() { return this.derive(enc('zgap_authority')); }
17
+ get zgapReserve() { return this.derive(enc('zgap_reserve')); }
18
+ get zgapCollateralVault() { return this.derive(enc('zgap_collateral_vault')); }
19
+ get zgapSolVault() { return this.derive(enc('zgap_sol_vault')); }
20
+ get zgapReserveSol() { return this.derive(enc('zgap_reserve_sol')); }
21
+ get lpLock() { return this.derive(enc('lp_lock')); }
22
+ get yieldVault() { return this.derive(enc('yield_vault')); }
23
+ get rewardDistributor() { return this.derive(enc('reward_distributor')); }
24
+ priceCache(assetId) { return this.derive(enc('price_cache'), Uint8Array.of(assetId)); }
25
+ session(sessionId) { return this.derive(enc('session'), sessionId); }
26
+ vault(session) { return this.derive(enc('vault'), session.toBytes()); }
27
+ prizeVault(session) { return this.derive(enc('prize_vault'), session.toBytes()); }
28
+ guild(guildId) { return this.derive(enc('guild'), guildId); }
29
+ rateLimit(guild) { return this.derive(enc('rate_limit'), guild.toBytes()); }
30
+ gameManifest(gameId) { return this.derive(enc('game_manifest'), gameId); }
31
+ gameCreator(creator, gameId) { return this.derive(enc('game_creator'), creator.toBytes(), gameId); }
32
+ royaltyVault(creator, gameId) { return this.derive(enc('royalty_vault'), creator.toBytes(), gameId); }
33
+ attestationCert(enclave) { return this.derive(enc('attestation_cert'), enclave.toBytes()); }
34
+ player(session, user) { return this.derive(enc('player'), session.toBytes(), user.toBytes()); }
35
+ agentBond(user) { return this.derive(enc('agent_bond'), user.toBytes()); }
36
+ }
37
+ exports.Addresses = Addresses;
38
+ async function zgaMintOf(connection, addresses) {
39
+ const info = await connection.getAccountInfo(addresses.lpLock);
40
+ if (!info)
41
+ throw new Error('The liquidity lock does not exist');
42
+ return new web3_js_1.PublicKey(info.data.subarray(0, 32));
43
+ }
@@ -0,0 +1,41 @@
1
+ import { AddressLookupTableAccount, Connection, Keypair, PublicKey } from '@solana/web3.js';
2
+ import { EnclaveSigner } from './signer';
3
+ export interface UsageReceipt {
4
+ session: PublicKey;
5
+ player: PublicKey;
6
+ hours: number;
7
+ fromSlot: bigint;
8
+ toSlot: bigint;
9
+ }
10
+ export interface BillingTerms {
11
+ slotsPerHour: bigint;
12
+ maxHoursPerBill: number;
13
+ marginSlots: bigint;
14
+ }
15
+ export declare const DEFAULT_BILLING_TERMS: BillingTerms;
16
+ export declare function usageReceiptHash(r: UsageReceipt): Uint8Array;
17
+ export declare function nextUsageReceipt(a: {
18
+ session: PublicKey;
19
+ player: PublicKey;
20
+ billedThroughSlot: bigint;
21
+ currentSlot: bigint;
22
+ rate: bigint;
23
+ credit: bigint;
24
+ }, terms?: BillingTerms): UsageReceipt | null;
25
+ export interface BillEnv {
26
+ program: any;
27
+ connection: Connection;
28
+ payer: Keypair;
29
+ enclave: EnclaveSigner;
30
+ lookupTable: AddressLookupTableAccount;
31
+ }
32
+ export interface Attestation {
33
+ slot: number;
34
+ publicKey: Uint8Array;
35
+ signature: Uint8Array;
36
+ }
37
+ export declare const subscriptionAddress: (programId: PublicKey, session: PublicKey, player: PublicKey) => PublicKey;
38
+ export declare const subscriptionVaultAddress: (programId: PublicKey, subscription: PublicKey) => PublicKey;
39
+ export declare const observerVaultAddress: (programId: PublicKey, session: PublicKey) => PublicKey;
40
+ export declare function billSubscription(env: BillEnv, receipt: UsageReceipt, guildSignature: Uint8Array, attestations: Attestation[]): Promise<string>;
41
+ export declare function claimObserverPay(program: any, connection: Connection, observer: Keypair, session: PublicKey): Promise<string>;
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.observerVaultAddress = exports.subscriptionVaultAddress = exports.subscriptionAddress = exports.DEFAULT_BILLING_TERMS = void 0;
4
+ exports.usageReceiptHash = usageReceiptHash;
5
+ exports.nextUsageReceipt = nextUsageReceipt;
6
+ exports.billSubscription = billSubscription;
7
+ exports.claimObserverPay = claimObserverPay;
8
+ const web3_js_1 = require("@solana/web3.js");
9
+ const anchor_1 = require("@coral-xyz/anchor");
10
+ const addresses_1 = require("./addresses");
11
+ const sha256_1 = require("./sha256");
12
+ const token_1 = require("./token");
13
+ exports.DEFAULT_BILLING_TERMS = { slotsPerHour: 9000n, maxHoursPerBill: 744, marginSlots: 150n };
14
+ function u32le(n) {
15
+ const b = new Uint8Array(4);
16
+ new DataView(b.buffer).setUint32(0, n, true);
17
+ return b;
18
+ }
19
+ function u64le(n) {
20
+ const b = new Uint8Array(8);
21
+ new DataView(b.buffer).setBigUint64(0, n, true);
22
+ return b;
23
+ }
24
+ function usageReceiptHash(r) {
25
+ return (0, sha256_1.sha256)(new TextEncoder().encode('zga-usage-v1'), r.session.toBytes(), r.player.toBytes(), u32le(r.hours), u64le(r.fromSlot), u64le(r.toSlot));
26
+ }
27
+ function nextUsageReceipt(a, terms = exports.DEFAULT_BILLING_TERMS) {
28
+ if (a.rate <= 0n)
29
+ return null;
30
+ const elapsed = a.currentSlot - terms.marginSlots - a.billedThroughSlot;
31
+ if (elapsed < terms.slotsPerHour)
32
+ return null;
33
+ let hours = elapsed / terms.slotsPerHour;
34
+ const affordable = a.credit / a.rate;
35
+ if (hours > affordable)
36
+ hours = affordable;
37
+ if (hours > BigInt(terms.maxHoursPerBill))
38
+ hours = BigInt(terms.maxHoursPerBill);
39
+ if (hours < 1n)
40
+ return null;
41
+ return {
42
+ session: a.session,
43
+ player: a.player,
44
+ hours: Number(hours),
45
+ fromSlot: a.billedThroughSlot,
46
+ toSlot: a.billedThroughSlot + hours * terms.slotsPerHour,
47
+ };
48
+ }
49
+ const subscriptionAddress = (programId, session, player) => web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('subscription'), session.toBuffer(), player.toBuffer()], programId)[0];
50
+ exports.subscriptionAddress = subscriptionAddress;
51
+ const subscriptionVaultAddress = (programId, subscription) => web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('sub_vault'), subscription.toBuffer()], programId)[0];
52
+ exports.subscriptionVaultAddress = subscriptionVaultAddress;
53
+ const observerVaultAddress = (programId, session) => web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('observer_vault'), session.toBuffer()], programId)[0];
54
+ exports.observerVaultAddress = observerVaultAddress;
55
+ async function billSubscription(env, receipt, guildSignature, attestations) {
56
+ const { program, connection, payer, enclave, lookupTable } = env;
57
+ const attesters = [...attestations].sort((x, y) => x.slot - y.slot);
58
+ if (attesters.length < 2 || attesters.length > 3)
59
+ throw new Error('A bill needs two or three attestations');
60
+ const addr = new addresses_1.Addresses(program.programId);
61
+ const session = await program.account.session.fetch(receipt.session);
62
+ const guildAccount = await program.account.guildAccount.fetch(session.guild);
63
+ const gameId = Uint8Array.from(session.gameId);
64
+ const manifest = await program.account.gameManifest.fetch(addr.gameManifest(gameId));
65
+ const creator = manifest.creator;
66
+ const hash = usageReceiptHash(receipt);
67
+ const signatureIxs = [
68
+ web3_js_1.Ed25519Program.createInstructionWithPublicKey({ publicKey: enclave.publicKey, message: hash, signature: guildSignature }),
69
+ ...attesters.map((a) => web3_js_1.Ed25519Program.createInstructionWithPublicKey({ publicKey: a.publicKey, message: hash, signature: a.signature })),
70
+ ];
71
+ const subscription = (0, exports.subscriptionAddress)(program.programId, receipt.session, receipt.player);
72
+ const zgaMint = await (0, addresses_1.zgaMintOf)(connection, addr);
73
+ const billIx = await program.methods
74
+ .billSubscription({ session: receipt.session, player: receipt.player, hours: receipt.hours, fromSlot: new anchor_1.BN(receipt.fromSlot.toString()), toSlot: new anchor_1.BN(receipt.toSlot.toString()) }, Buffer.from(attesters.map((a) => a.slot)))
75
+ .accounts({
76
+ protocolRedeem: {
77
+ zgaPriceCache: addr.priceCache(0), solPriceCache: addr.priceCache(1), solVault: addr.zgapSolVault, reserveSolAccount: addr.zgapReserveSol,
78
+ },
79
+ session: receipt.session,
80
+ subscription,
81
+ subVault: (0, exports.subscriptionVaultAddress)(program.programId, subscription),
82
+ observerVault: (0, exports.observerVaultAddress)(program.programId, receipt.session),
83
+ yieldVault: addr.yieldVault,
84
+ rewardDistributor: addr.rewardDistributor,
85
+ protocolConfig: addr.config,
86
+ zgapAuthority: addr.zgapAuthority,
87
+ zgapMint: addr.zgapMint,
88
+ zgapCollateralVault: addr.zgapCollateralVault,
89
+ zgapReserve: addr.zgapReserve,
90
+ zgapReserveZgaAccount: (0, token_1.associatedTokenAddress)(zgaMint, addr.zgapReserve),
91
+ lpLockTokenAccount: addr.lpLock,
92
+ guildTokenAccount: (0, token_1.associatedTokenAddress)(addr.zgapMint, guildAccount.operator),
93
+ gameManifest: addr.gameManifest(gameId),
94
+ gameCreatorAccount: addr.gameCreator(creator, gameId),
95
+ royaltyVault: addr.royaltyVault(creator, gameId),
96
+ guildAccount: session.guild,
97
+ payer: payer.publicKey,
98
+ tokenProgram: token_1.TOKEN_PROGRAM_ID,
99
+ systemProgram: web3_js_1.SystemProgram.programId,
100
+ ixSysvar: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY,
101
+ })
102
+ .instruction();
103
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
104
+ const message = new web3_js_1.TransactionMessage({ payerKey: payer.publicKey, recentBlockhash: blockhash, instructions: [...signatureIxs, billIx] }).compileToV0Message([lookupTable]);
105
+ const tx = new web3_js_1.VersionedTransaction(message);
106
+ tx.sign([payer]);
107
+ if (tx.serialize().length > 1232)
108
+ throw new Error('The billing transaction does not fit in a packet');
109
+ const sent = await connection.sendTransaction(tx);
110
+ await connection.confirmTransaction({ signature: sent, blockhash, lastValidBlockHeight }, 'confirmed');
111
+ return sent;
112
+ }
113
+ const ATA_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
114
+ async function claimObserverPay(program, connection, observer, session) {
115
+ const addr = new addresses_1.Addresses(program.programId);
116
+ const tokenAccount = (0, token_1.associatedTokenAddress)(addr.zgapMint, observer.publicKey);
117
+ const makeAccount = new web3_js_1.TransactionInstruction({
118
+ programId: ATA_PROGRAM_ID,
119
+ keys: [
120
+ { pubkey: observer.publicKey, isSigner: true, isWritable: true },
121
+ { pubkey: tokenAccount, isSigner: false, isWritable: true },
122
+ { pubkey: observer.publicKey, isSigner: false, isWritable: false },
123
+ { pubkey: addr.zgapMint, isSigner: false, isWritable: false },
124
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
125
+ { pubkey: token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
126
+ ],
127
+ data: Buffer.from([1]),
128
+ });
129
+ return program.methods
130
+ .claimObserverPay()
131
+ .accounts({
132
+ session,
133
+ observerVault: (0, exports.observerVaultAddress)(program.programId, session),
134
+ observerTokenAccount: tokenAccount,
135
+ observer: observer.publicKey,
136
+ tokenProgram: token_1.TOKEN_PROGRAM_ID,
137
+ })
138
+ .preInstructions([makeAccount])
139
+ .signers([observer])
140
+ .rpc();
141
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const chain_1 = require("../chain");
6
+ const config_1 = require("../config");
7
+ const gate_1 = require("../crank/gate");
8
+ const prices_1 = require("../crank/prices");
9
+ const runner_1 = require("../crank/runner");
10
+ const shieldFees_1 = require("../crank/shieldFees");
11
+ const lending_1 = require("../crank/lending");
12
+ async function main() {
13
+ const env = (0, config_1.readEnv)();
14
+ const connection = new web3_js_1.Connection(env.rpcUrl, 'confirmed');
15
+ const caller = (0, config_1.loadWallet)(env.walletPath);
16
+ const program = (0, config_1.makeProgram)(connection, caller, (0, config_1.loadIdl)(env.idlPath));
17
+ const missing = (0, chain_1.missingFromIdl)(program, ['PriceFeedAccount', 'PriceCacheAccount', 'ShieldAssetConfig', 'LendingVault', 'LendingLoan'], ['updatePriceFeedAggregated', 'updatePriceFeedDlmmAggregated', 'updatePriceFeedFixed', 'crankPriceCache', 'updateMarketCapGate', 'routeShieldFees', 'routeShieldZgapFees', 'routeZnlZgapFees', 'liquidateZnlLoan']);
18
+ if (missing.length > 0)
19
+ throw new Error(`the IDL at ${env.idlPath} lacks ${missing.join(', ')}: the crank needs the IDL of the program it talks to`);
20
+ const log = (message) => console.log(`[crank ${new Date().toISOString()}] ${message}`);
21
+ log(`caller ${caller.publicKey.toBase58()}, program ${program.programId.toBase58()}`);
22
+ const liquidate = process.env.ZGA_LIQUIDATE === '1';
23
+ if (liquidate)
24
+ log('liquidating loans: the caller must hold the borrowed assets');
25
+ const postSecs = Math.min(540, Number(process.env.ZGA_PRICE_POST_SECS ?? 450) || 0);
26
+ if (postSecs > 0)
27
+ log(`posting a price sample at most every ${postSecs}s`);
28
+ const tasks = [(0, prices_1.priceFeedsTask)(30000, postSecs), (0, gate_1.marketCapGateTask)(), (0, shieldFees_1.shieldFeesTask)(), (0, lending_1.lendingFeesTask)(), ...(liquidate ? [(0, lending_1.lendingLiquidationTask)()] : [])];
29
+ await (0, runner_1.runTasks)({ program, connection, caller, log }, tasks, { once: process.argv.includes('--once') });
30
+ }
31
+ main().catch((e) => {
32
+ console.error(e);
33
+ process.exit(1);
34
+ });
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const chain_1 = require("../chain");
6
+ const config_1 = require("../config");
7
+ const limits_1 = require("../relayer/limits");
8
+ const relayer_1 = require("../relayer/relayer");
9
+ const chain_2 = require("../relayer/chain");
10
+ const server_1 = require("../relayer/server");
11
+ const settings_1 = require("../relayer/settings");
12
+ async function main() {
13
+ const env = (0, config_1.readEnv)();
14
+ const settings = (0, settings_1.readRelayerSettings)();
15
+ const connection = new web3_js_1.Connection(env.rpcUrl, 'confirmed');
16
+ const relayer = (0, config_1.loadWallet)(env.walletPath);
17
+ const program = (0, config_1.makeProgram)(connection, relayer, (0, config_1.loadIdl)(env.idlPath));
18
+ const log = (message) => console.log(`[relayer ${new Date().toISOString()}] ${message}`);
19
+ const missing = (0, chain_1.missingFromIdl)(program, ['ShieldAssetConfig', 'ShieldDirectory', 'NopAccount', 'NopShieldFees'], ['unshield', 'unshieldDrop', 'initNopShieldFees']);
20
+ if (missing.length > 0)
21
+ throw new Error(`the IDL at ${env.idlPath} lacks ${missing.join(', ')}: the relayer needs the IDL of the program it talks to`);
22
+ const chain = new chain_2.SolanaShieldedRelayerChain(program, connection, relayer, settings, log);
23
+ const info = await chain.prepare();
24
+ log(`relayer ${info.relayer}, program ${info.programId}, ${info.assets.length} asset(s), ${info.lookupTables.length} lookup table(s)`);
25
+ const balance = await connection.getBalance(relayer.publicKey);
26
+ log(`balance ${(balance / 1e9).toFixed(4)} SOL${balance < settings.minBalanceLamports ? ' (below the minimum: it will refuse requests)' : ''}`);
27
+ const handler = new relayer_1.ShieldedRelayer(chain, new limits_1.RelayerRateLimiter(settings.ratePerMinute), new limits_1.RelayerInFlight(settings.maxInFlight), settings.logRequests ? log : () => { });
28
+ const server = (0, server_1.createShieldedRelayerServer)(handler, { maxBodyBytes: settings.maxBodyBytes, trustProxy: settings.trustProxy, corsOrigin: settings.corsOrigin });
29
+ server.listen(settings.port, settings.host, () => log(`listening on ${settings.host}:${settings.port}`));
30
+ const stop = () => { log('stopping'); server.close(() => process.exit(0)); };
31
+ process.on('SIGINT', stop);
32
+ process.on('SIGTERM', stop);
33
+ }
34
+ main().catch((e) => {
35
+ console.error(e);
36
+ process.exit(1);
37
+ });
@@ -0,0 +1,14 @@
1
+ import { Connection, PublicKey } from '@solana/web3.js';
2
+ export declare const ZGA_ASSET_ID = 0;
3
+ export declare const SOL_ASSET_ID = 1;
4
+ export declare const EPOCH_DURATION_SLOTS: bigint;
5
+ export declare function pda(programId: PublicKey, ...seeds: (Buffer | Uint8Array)[]): PublicKey;
6
+ export declare const configPda: (programId: PublicKey) => PublicKey;
7
+ export declare const lpLockPda: (programId: PublicKey) => PublicKey;
8
+ export declare const feedPda: (programId: PublicKey, assetId: number) => PublicKey;
9
+ export declare const cachePda: (programId: PublicKey, assetId: number) => PublicKey;
10
+ export declare function chainTime(connection: Connection): Promise<number>;
11
+ export declare function idlAccountName(program: any, wanted: string): string;
12
+ export declare function decodeAccount(program: any, wanted: string, data: Buffer): any;
13
+ export declare function missingFromIdl(program: any, accounts: string[], methods: string[]): string[];
14
+ export declare function currentEpoch(program: any, connection: Connection): Promise<bigint>;
package/dist/chain.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cachePda = exports.feedPda = exports.lpLockPda = exports.configPda = exports.EPOCH_DURATION_SLOTS = exports.SOL_ASSET_ID = exports.ZGA_ASSET_ID = void 0;
4
+ exports.pda = pda;
5
+ exports.chainTime = chainTime;
6
+ exports.idlAccountName = idlAccountName;
7
+ exports.decodeAccount = decodeAccount;
8
+ exports.missingFromIdl = missingFromIdl;
9
+ exports.currentEpoch = currentEpoch;
10
+ const web3_js_1 = require("@solana/web3.js");
11
+ exports.ZGA_ASSET_ID = 0;
12
+ exports.SOL_ASSET_ID = 1;
13
+ exports.EPOCH_DURATION_SLOTS = BigInt(process.env.EPOCH_DURATION_SLOTS ?? '648000');
14
+ function pda(programId, ...seeds) {
15
+ return web3_js_1.PublicKey.findProgramAddressSync(seeds, programId)[0];
16
+ }
17
+ const configPda = (programId) => pda(programId, Buffer.from('config'));
18
+ exports.configPda = configPda;
19
+ const lpLockPda = (programId) => pda(programId, Buffer.from('lp_lock'));
20
+ exports.lpLockPda = lpLockPda;
21
+ const feedPda = (programId, assetId) => pda(programId, Buffer.from('price_feed'), Buffer.from([assetId]));
22
+ exports.feedPda = feedPda;
23
+ const cachePda = (programId, assetId) => pda(programId, Buffer.from('price_cache'), Buffer.from([assetId]));
24
+ exports.cachePda = cachePda;
25
+ async function chainTime(connection) {
26
+ const t = await connection.getBlockTime(await connection.getSlot());
27
+ return t ?? Math.floor(Date.now() / 1000);
28
+ }
29
+ function idlAccountName(program, wanted) {
30
+ const found = (program.idl?.accounts ?? []).find((a) => String(a.name).toLowerCase() === wanted.toLowerCase());
31
+ if (!found)
32
+ throw new Error(`the IDL has no account type ${wanted}: the crank needs the IDL of the program it talks to`);
33
+ return found.name;
34
+ }
35
+ function decodeAccount(program, wanted, data) {
36
+ return program.coder.accounts.decode(idlAccountName(program, wanted), data);
37
+ }
38
+ function missingFromIdl(program, accounts, methods) {
39
+ const have = new Set((program.idl?.accounts ?? []).map((a) => String(a.name).toLowerCase()));
40
+ const callable = new Set(Object.keys(program.methods ?? {}));
41
+ return [...accounts.filter((a) => !have.has(a.toLowerCase())), ...methods.filter((m) => !callable.has(m))];
42
+ }
43
+ async function currentEpoch(program, connection) {
44
+ const config = await program.account.protocolConfig.fetch((0, exports.configPda)(program.programId));
45
+ const slot = BigInt(await connection.getSlot());
46
+ const start = BigInt(config.globalZgaEpochStartSlot.toString());
47
+ return slot > start ? (slot - start) / exports.EPOCH_DURATION_SLOTS : 0n;
48
+ }
@@ -0,0 +1,11 @@
1
+ import * as anchor from '@coral-xyz/anchor';
2
+ import { Connection, Keypair } from '@solana/web3.js';
3
+ export interface ClientEnv {
4
+ rpcUrl: string;
5
+ walletPath: string;
6
+ idlPath: string;
7
+ }
8
+ export declare function readEnv(env?: NodeJS.ProcessEnv): ClientEnv;
9
+ export declare function loadWallet(path: string): Keypair;
10
+ export declare function loadIdl(path: string): any;
11
+ export declare function makeProgram(connection: Connection, wallet: Keypair, idl: any): anchor.Program;
package/dist/config.js ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.readEnv = readEnv;
37
+ exports.loadWallet = loadWallet;
38
+ exports.loadIdl = loadIdl;
39
+ exports.makeProgram = makeProgram;
40
+ const anchor = __importStar(require("@coral-xyz/anchor"));
41
+ const web3_js_1 = require("@solana/web3.js");
42
+ const fs_1 = require("fs");
43
+ function readEnv(env = process.env) {
44
+ const need = (key) => {
45
+ const v = env[key];
46
+ if (!v)
47
+ throw new Error(`${key} is not set`);
48
+ return v;
49
+ };
50
+ return { rpcUrl: need('RPC_URL'), walletPath: need('WALLET_PATH'), idlPath: env.IDL_PATH ?? './idl/zga_protocol.json' };
51
+ }
52
+ function loadWallet(path) {
53
+ return web3_js_1.Keypair.fromSecretKey(Uint8Array.from(JSON.parse((0, fs_1.readFileSync)(path, 'utf8'))));
54
+ }
55
+ function loadIdl(path) {
56
+ return JSON.parse((0, fs_1.readFileSync)(path, 'utf8'));
57
+ }
58
+ function makeProgram(connection, wallet, idl) {
59
+ const provider = new anchor.AnchorProvider(connection, new anchor.Wallet(wallet), { commitment: 'confirmed' });
60
+ return new anchor.Program(idl, provider);
61
+ }
@@ -0,0 +1,2 @@
1
+ import { CrankTask } from './runner';
2
+ export declare function marketCapGateTask(everyMs?: number): CrankTask;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.marketCapGateTask = marketCapGateTask;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const chain_1 = require("../chain");
6
+ function marketCapGateTask(everyMs = 600000) {
7
+ return {
8
+ name: 'market-cap-gate',
9
+ everyMs,
10
+ async run(ctx) {
11
+ const pid = ctx.program.programId;
12
+ const lpLock = (0, chain_1.lpLockPda)(pid);
13
+ const info = await ctx.connection.getAccountInfo(lpLock);
14
+ if (!info)
15
+ return;
16
+ const zgaMint = new web3_js_1.PublicKey(info.data.subarray(0, 32));
17
+ await ctx.program.methods.updateMarketCapGate().accountsPartial({
18
+ protocolConfig: (0, chain_1.configPda)(pid),
19
+ zgaPriceCache: (0, chain_1.cachePda)(pid, chain_1.ZGA_ASSET_ID),
20
+ lpLockVault: lpLock,
21
+ zgaMint,
22
+ }).rpc();
23
+ ctx.log('market-cap gate recorded');
24
+ },
25
+ };
26
+ }
@@ -0,0 +1,16 @@
1
+ import { CrankTask } from './runner';
2
+ export interface LoanFacts {
3
+ principal: bigint;
4
+ collateralAmount: bigint;
5
+ accruedAt: bigint;
6
+ dueAt: bigint;
7
+ rateBps: bigint;
8
+ thresholdBps: bigint;
9
+ collateralScaleTo9: bigint;
10
+ collateralPriceE9: bigint;
11
+ borrowPriceE9: bigint;
12
+ now: bigint;
13
+ }
14
+ export declare function isLoanLiquidatable(f: LoanFacts): boolean;
15
+ export declare function lendingFeesTask(everyMs?: number): CrankTask;
16
+ export declare function lendingLiquidationTask(everyMs?: number): CrankTask;